diff --git a/includes/simplenews.admin.inc b/includes/simplenews.admin.inc
index e6d5df2..ced18cb 100644
--- a/includes/simplenews.admin.inc
+++ b/includes/simplenews.admin.inc
@@ -265,7 +265,7 @@ function simplenews_issue_send($nids) {
     }
 
     if ($node->status == NODE_NOT_PUBLISHED) {
-      simplenews_update_sent_status($node, SIMPLENEWS_COMMAND_SEND_PUBLISH);
+      simplenews_newsletter_update_sent_status($node, SIMPLENEWS_COMMAND_SEND_PUBLISH);
       drupal_set_message(t('Newsletter %title is unpublished and will be sent on publish', array('%title' => $node->title)));
       continue;
     }
diff --git a/includes/simplenews.mail.inc b/includes/simplenews.mail.inc
index 5dd0948..4fcc2c9 100644
--- a/includes/simplenews.mail.inc
+++ b/includes/simplenews.mail.inc
@@ -37,7 +37,7 @@ function simplenews_add_node_to_spool($node) {
     ->execute();
 
   // Update simplenews newsletter status to send pending.
-  simplenews_update_sent_status($node);
+  simplenews_newsletter_update_sent_status($node);
 
   // When cron is not used the newsletter is send immediately to the emails
   // in the spool. When cron is used newsletters are send to addresses in the
diff --git a/simplenews.api.php b/simplenews.api.php
index 7178080..ed9ecf5 100644
--- a/simplenews.api.php
+++ b/simplenews.api.php
@@ -6,63 +6,141 @@
  */
 
 /**
- * @todo
+ * @mainpage Simplenews API documentation.
+ *
+ * Simplenews builds on the following basic concepts.
+ *
+ * @link subscriber Subscribers @endlink subscribe to @link newsletter
+ * newsletters (categories) @endlink. That connection is called
+ * a @link subscription subscription @endlink. Nodes of enabled content types
+ * are @link issue newsletter issues @endlink. These are then sent to the
+ * subscribers of the newsletter the issue is attached to.
+ *
+ * Sending is done by first adding a row for each subscriber to the @link spool
+ * mail spool @endlink.
+ * Then they are processed either immediatly or during cron runs. The actual
+ * sending happens through a @link source source instance @endlink, which is
+ * first instanciated based on the mail spool and then used to generated the
+ * actual mail content.
+ *
  */
-function hook_simplenews_issue_operations() {
 
-}
+/**
+ * @defgroup subscriber Subscriber
+ *
+ * TODO
+ */
 
 /**
- * @todo
+ * @defgroup newsletter Newsletter (category)
+ *
+ * TODO
  */
-function hook_simplenews_subscription_operations() {
 
-}
+/**
+ * @defgroup subscription Subscription
+ *
+ * TODO
+ */
 
 /**
- * @todo
+ * @defgroup issue Newsletter issue
+ *
+ * TODO
  */
-function hook_simplenews_category_insert($category) {
 
-}
+/**
+ * @defgroup spool Mail spool
+ *
+ * TODO
+ */
 
 /**
- * @todo
+ * @defgroup source Source
+ *
+ * TODO
  */
-function hook_simplenews_category_update($category) {
 
+/**
+ * Return operations to be applied to newsletter issues.
+ *
+ * @ingroup issue
+ */
+function hook_simplenews_issue_operations() {
+  $operations = array(
+    'activate' => array(
+      'label' => t('Send'),
+      'callback' => 'simplenews_issue_send',
+    ),
+  );
+  return $operations;
+}
+
+/**
+ * Return operations to be applied to subscriptions.
+ *
+ * @ingroup issue
+ */
+function hook_simplenews_subscription_operations() {
+  $operations = array(
+    'activate' => array(
+      'label' => t('Activate'),
+      'callback' => 'simplenews_subscription_activate',
+      'callback arguments' => array(SIMPLENEWS_SUBSCRIPTION_ACTIVE),
+    ),
+    'inactivate' => array(
+      'label' => t('Inactivate'),
+      'callback' => 'simplenews_subscription_activate',
+      'callback arguments' => array(SIMPLENEWS_SUBSCRIPTION_INACTIVE),
+    ),
+    'delete' => array(
+      'label' => t('Delete'),
+      'callback' => 'simplenews_subscription_delete_multiple',
+    ),
+  );
+  return $operations;
 }
 
 /**
- * @todo
+ * Act after a newsletter category has been saved.
+ *
+ * @ingroup newsletter
  */
-function hook_simplenews_category_delete($category) {
+function hook_simplenews_category_update($category) {
 
 }
 
 /**
- * @todo
+ * Act after a newsletter category has been deleted.
+ *
+ * @ingroup newsletter
  */
-function hook_simplenews_mailing_list_insert($list) {
+function hook_simplenews_category_delete($category) {
 
 }
 
 /**
- * @todo
+ * Act after a subscriber is updated.
+ *
+ * @ingroup subscriber
  */
 function hook_simplenews_subscriber_update($subscriber) {
 
 }
 
 /**
- * @todo
+ * Act after a new subscriber has been created.
+ *
+ * @ingroup subscriber
  */
 function hook_simplenews_subscriber_insert($subscriber) {
 
 }
 
 /**
- * @todo
+ * Act after a subscriber has been deleted.
+ *
+ * @ingroup subscriber
  */
 function hook_simplenews_subscriber_delete($subscriber) {
 
@@ -76,6 +154,8 @@ function hook_simplenews_subscriber_delete($subscriber) {
  *
  * @param $subscription
  *   The subscription object for this specific subscribe action.
+ *
+ * @ingroup subscriber
  */
 function hook_simplenews_subscribe_user($subscriber, $subscription) {
 
@@ -89,6 +169,8 @@ function hook_simplenews_subscribe_user($subscriber, $subscription) {
  *
  * @param $subscription
  *   The subscription object for this specific unsubscribe action.
+ *
+ * @ingroup subscriber
  */
 function hook_simplenews_unsubscribe_user($subscriber, $subscription) {
 
@@ -101,6 +183,8 @@ function hook_simplenews_unsubscribe_user($subscriber, $subscription) {
  *   An array keyed by the name of the class that provides the implementation,
  *   the array value consists of another array with the keys label and
  *   description.
+ *
+ * @ingroup source
  */
 function hook_simplenews_source_cache_info() {
   return array(
@@ -113,4 +197,4 @@ function hook_simplenews_source_cache_info() {
       'description' => t('This caches the rendered content to be sent for multiple recipients. It is not possible to use subscriber specific theming but tokens can be used for personalization.'),
     ),
   );
-}
\ No newline at end of file
+}
diff --git a/simplenews.module b/simplenews.module
index 2d019c5..7b25ecf 100644
--- a/simplenews.module
+++ b/simplenews.module
@@ -381,6 +381,8 @@ function simplenews_node_view($node, $view_mode) {
 
 /**
  * Get simplenews category term values from a node object.
+ *
+ * @ingroup issue
  */
 function simplenews_get_term_values($node) {
   $category_field = simplenews_get_category_field($node->type);
@@ -559,6 +561,8 @@ function simplenews_node_load($nodes, $types) {
  *   Array of content types or single content type string.
  * @return boolean
  *   TRUE if at least one of $types is enabled for Simplenews.
+ *
+ * @ingroup issue
  */
 function simplenews_check_node_types($types) {
   if (!is_array($types)) {
@@ -578,7 +582,9 @@ function simplenews_check_node_types($types) {
  * Get all node types supported by Simplenews.
  *
  * @return
- *   Array of node-types which can be used a simplenews newsletter.
+ *   Array of node-types which can be used a simplenews newsletter issue.
+ *
+ * @ingroup issue
  */
 function simplenews_get_content_types() {
   $simplenews_types = &drupal_static(__FUNCTION__, array());
@@ -598,6 +604,8 @@ function simplenews_get_content_types() {
  *
  * @param $type
  *   A node type object.
+ *
+ * @ingroup issue
  */
 function simplenews_add_term_field($type) {
   $field_name = variable_get('simplenews_category_field', 'field_simplenews_term');
@@ -664,6 +672,8 @@ function simplenews_add_term_field($type) {
  *
  * @param $type
  *   A node type object.
+ *
+ * @ingroup issue
  */
 function simplenews_remove_term_field($type) {
   $field_name = variable_get('simplenews_category_field', 'field_simplenews_term');
@@ -681,6 +691,8 @@ function simplenews_remove_term_field($type) {
  * @return field definition
  *   Field name of the field containing the newsletter category term.
  *   FALSE if no field is selected.
+ *
+ * @ingroup issue
  */
 function simplenews_get_category_field($bundle_name) {
   $fields_info = field_info_fields();
@@ -828,25 +840,6 @@ function simplenews_cron() {
 }
 
 /**
- * Implements hook_simplenews_category_delete().
- *
- * For the time being we use mailing list and category in sync.
- * Deletes the newsletter list when category is deleted.
- */
-function simplenews_simplenews_category_delete($category) {
-
-  // Delete subscriptions
-  simplenews_subscription_delete(array('tid' => $category->tid));
-  drupal_set_message(t('All subscriptions to newsletter %newsletter have been deleted.', array('%newsletter' => _simplenews_newsletter_name($category))));
-
-  // Delete subscription block
-  db_delete('block')
-    ->condition('module', 'simplenews')
-    ->condition('delta', $category->tid)
-    ->execute();
-}
-
-/**
  * Implements hook_form_FORM_ID_alter().
  *
  * Add simplenews subscription fields to user register form.
@@ -1258,6 +1251,7 @@ function simplenews_forms($form_id, $args) {
  *   A user object if a user with that mail address exists, otherwise an object
  *   with the properties mail and uid (set to 0).
  *
+ * @ingroup subscriber
  */
 function simplenews_load_user_by_mail($mail) {
   $account = user_load_by_mail($mail);
@@ -1294,6 +1288,8 @@ function simplenews_load_user_by_mail($mail) {
  *    mass subscribe: mass admin UI
  *    mass unsubscribe: mass admin UI
  *    action: Drupal actions
+ *
+ * @ingroup subscription
  */
 function simplenews_subscribe_user($mail, $tid, $confirm = TRUE, $source = 'unknown', $preferred_language = NULL) {
   global $language;
@@ -1391,6 +1387,8 @@ function simplenews_subscribe_user($mail, $tid, $confirm = TRUE, $source = 'unkn
  *   - mass subscribe: Mass admin UI.
  *   - mass unsubscribe: Mass admin UI.
  *   - action: Drupal actions.
+ *
+ * @ingroup subscription
  */
 function simplenews_unsubscribe_user($mail, $tid, $confirm = TRUE, $source = 'unknown') {
   $subscriber = simplenews_get_subscription(simplenews_load_user_by_mail($mail));
@@ -1440,6 +1438,8 @@ function simplenews_unsubscribe_user($mail, $tid, $confirm = TRUE, $source = 'un
  * @return boolean
  *   TRUE if the email address is subscribed; otherwise false.
  *
+ * @ingroup subscription
+ *
  * @todo Only return active subscriptions.
  * @todo Caching should be done in simplenews_get_subscription().
  */
@@ -1564,7 +1564,18 @@ function simplenews_get_subscription($account) {
 }
 
 /**
- * @todo
+ * Returns a list of active subscriptions for a given newsletter category.
+ *
+ * WARNING: Use with caution - this might return a huge list
+ *
+ * @param $tid
+ *   The newsletter category term id.
+ *
+ * @return
+ *   An array keyed by the mail address, containing another array with the keys
+ *   mail, uid, language, snid and status.
+ *
+ * @ingroup subscription
  */
 function simplenews_get_subscriptions_by_list($tid) {
   $query = db_select('simplenews_subscriber', 'sn');
@@ -1586,6 +1597,8 @@ function simplenews_get_subscriptions_by_list($tid) {
  *   Associative array of database fields to be updated.
  *
  * @todo Replace with simplenews_subscription_save() ?
+ *
+ * @ingroup subscription
  */
 function simplenews_subscription_update($conditions = array(), $data) {
   $query = db_update('simplenews_subscription');
@@ -1597,7 +1610,9 @@ function simplenews_subscription_update($conditions = array(), $data) {
 }
 
 /**
- * @todo
+ * Save a subscription.
+ *
+ * @ingroup subscription
  */
 function simplenews_subscription_save($subscription) {
   db_merge('simplenews_subscription')
@@ -1620,6 +1635,8 @@ function simplenews_subscription_save($subscription) {
  *   An associative array of conditions matching the records to be delete.
  *   Example: array('tid' => 5, 'snid' => 12)
  *   Delete the subscription of subscriber 12 to newsletter tid 5.
+ *
+ * @ingroup subscription
  */
 function simplenews_subscription_delete($conditions = array()) {
   $query = db_delete('simplenews_subscription');
@@ -1637,6 +1654,8 @@ function simplenews_subscription_delete($conditions = array()) {
  * @return
  *   Newsletter subscriber object.
  *   FALSE if subscriber does not exist.
+ *
+ * @ingroup subscriber
  */
 function simplenews_subscriber_load($snid, $reset = FALSE) {
   $subscribers = simplenews_subscribers_load_multiple(array($snid), $reset);
@@ -1645,6 +1664,8 @@ function simplenews_subscriber_load($snid, $reset = FALSE) {
 
 /**
  * @todo
+ *
+ * @ingroup subscriber
  */
 function simplenews_subscribers_load_multiple($snids = array(), $conditions = array(), $reset = FALSE) {
   $subscribers = &drupal_static(__FUNCTION__, array());
@@ -1671,6 +1692,8 @@ function simplenews_subscribers_load_multiple($snids = array(), $conditions = ar
 
 /**
  * Store subscriber object in the database.
+ *
+ * @ingroup subscriber
  */
 function simplenews_subscriber_save(&$subscriber) {
   if (!empty($subscriber->snid)) {
@@ -1707,6 +1730,8 @@ function simplenews_subscriber_save(&$subscriber) {
  *
  * @param $snid
  *   Simplenews subscriber object.
+ *
+ * @ingroup subscriber
  */
 function simplenews_subscriber_delete(stdClass $subscriber) {
   db_delete('simplenews_subscriber')
@@ -1716,13 +1741,15 @@ function simplenews_subscriber_delete(stdClass $subscriber) {
 }
 
 /**
- * Create a list of recent newsletters.
+ * Create a list of recent newsletters issues.
  *
  * @param integer $tid
  *   The newsletter category id.
  * @param integer $count
  *   The number of newsletters.
  *
+ * @ingroup issue
+ *
  * @todo Replace this list by a View.
  */
 function simplenews_recent_newsletters($tid, $count = 5) {
@@ -1800,7 +1827,9 @@ function simplenews_views_api() {
  *   Simplenews category ID.
  * @return
  *   Newsletter category object.
- *   FALSE if category does not exist.
+ *   FALSE if category does not exist
+ *
+ * @ingroup newsletter
  */
 function simplenews_category_load($tid, $reset = FALSE) {
   if (!is_numeric($tid)) {
@@ -1815,6 +1844,8 @@ function simplenews_category_load($tid, $reset = FALSE) {
  *
  * @return
  *   array of category names. Translated if required.
+ *
+ * @ingroup newsletter.
  */
 function simplenews_category_list() {
   $categories = simplenews_categories_load_multiple();
@@ -1827,6 +1858,8 @@ function simplenews_category_list() {
 
 /**
  * @todo
+ *
+ * @ingroup newsletter
  */
 function simplenews_categories_load_multiple($tids = array(), $conditions = array(), $reset = FALSE) {
   $categories = &drupal_static(__FUNCTION__, array());
@@ -1867,6 +1900,8 @@ function simplenews_categories_load_multiple($tids = array(), $conditions = arra
  *
  * @param $category
  *   Newsletter category object
+ *
+ * @ingroup newsletter
  */
 function simplenews_category_save($category) {
   db_merge('simplenews_category')
@@ -1893,6 +1928,8 @@ function simplenews_category_save($category) {
  *
  * @param $category
  *   Simplenews category object or category ID.
+ *
+ * @ingroup newsletter
  */
 function simplenews_category_delete($category) {
   if (!is_object($category)) {
@@ -1903,6 +1940,17 @@ function simplenews_category_delete($category) {
     db_delete('simplenews_category')
       ->condition('tid', $category->tid)
       ->execute();
+
+    // Delete subscriptions
+    simplenews_subscription_delete(array('tid' => $category->tid));
+    drupal_set_message(t('All subscriptions to newsletter %newsletter have been deleted.', array('%newsletter' => _simplenews_newsletter_name($category))));
+
+    // Delete subscription block
+    db_delete('block')
+      ->condition('module', 'simplenews')
+      ->condition('delta', $category->tid)
+      ->execute();
+
     module_invoke_all('simplenews_category_delete', $category);
   }
 }
@@ -1913,6 +1961,8 @@ function simplenews_category_delete($category) {
  * @param $show_all
  *   FALSE = Don't show mailing lists which are marked 'hidden'.
  *   TRUE  = Show all mailing lists.
+ *
+ * @ingroup newsletter
  */
 function simplenews_get_mailing_lists($show_all = FALSE) {
   $lists = &drupal_static(__FUNCTION__, array());
@@ -1927,6 +1977,8 @@ function simplenews_get_mailing_lists($show_all = FALSE) {
 
 /**
  * @todo
+ *
+ * @ingroup issue
  */
 function simplenews_newsletter_load($nid, $reset = FALSE) {
   $conditions = array();
@@ -1936,6 +1988,8 @@ function simplenews_newsletter_load($nid, $reset = FALSE) {
 
 /**
  * @todo
+ *
+ * @ingroup issue
  */
 function simplenews_newsletter_load_multiple($nids = array(), $conditions = array(), $reset = FALSE) {
   $newsletters = &drupal_static(__FUNCTION__, array());
@@ -1968,6 +2022,8 @@ function simplenews_newsletter_load_multiple($nids = array(), $conditions = arra
  *
  * @param $newsletter
  *   Simplenews newsletter object.
+ *
+ * @ingroup issue
  */
 function simplenews_newsletter_save($newsletter) {
   db_merge('simplenews_newsletter')
@@ -1984,6 +2040,8 @@ function simplenews_newsletter_save($newsletter) {
  *
  * @param $newsletter
  *   Simplenews newsletter object or nid.
+ *
+ * @ingroup issue
  */
 function simplenews_newsletter_delete($newsletter) {
   if (!is_object($newsletter)) {
@@ -2262,21 +2320,6 @@ function simplenews_format_options() {
 }
 
 /**
- * Implements hook_locale().
- */
-function simplenews_locale($op = 'groups', $group = NULL) {
-  switch ($op) {
-    case 'groups':
-      return array('simplenews' => t('Simplenews'));
-      break;
-    case 'info':
-      $info['simplenews']['refresh callback'] = 'simplenews_locale_refresh';
-      return $info;
-      break;
-  }
-}
-
-/**
  * Implements hook_variable_info().
  */
 function simplenews_variable_info($options) {
@@ -2422,6 +2465,8 @@ function simplenews_theme() {
  *
  * @see simplenews-block.tpl.php
  * @see theme_simplenews-block()
+ *
+ * @ingroup theming
  */
 function template_preprocess_simplenews_block(&$variables) {
   global $user;
@@ -2468,6 +2513,8 @@ function template_preprocess_simplenews_block(&$variables) {
  * $variables are empty:
  *
  * @see simplenews-multi-block.tpl.php
+ *
+ * @ingroup theming
  */
 function template_preprocess_simplenews_multi_block(&$variables) {
   // Block content variables
@@ -2508,6 +2555,8 @@ function template_preprocess_simplenews_field(&$variables, $hook) {
  * @todo
  *
  * @see theme_field().
+ *
+ * @ingroup theming
  */
 // @todo make simplenews_field.tpl.php
 function theme_simplenews_field($variables) {
@@ -2559,6 +2608,8 @@ function theme_simplenews_field($variables) {
  *
  * @see simplenews-newsletter-body.tpl.php
  * @see theme_simplenews_newsletter_body().
+ *
+ * @ingroup theming
  */
 function template_preprocess_simplenews_newsletter_body(&$variables) {
   // We don't want to include links and comments in the email.
@@ -2585,6 +2636,8 @@ function template_preprocess_simplenews_newsletter_body(&$variables) {
 
  *
  * @see simplenews-newsletter-footer.tpl.php
+ *
+ * @ingroup theming
  */
 function template_preprocess_simplenews_newsletter_footer(&$variables) {
   // We don't want to include links and comments in the email.
@@ -2622,8 +2675,10 @@ function simplenews_node_tab_access($node) {
  *   The node object to be updated.
  * @param $status
  *   The new status, defaults to SIMPLENEWS_STATUS_SEND_PENDING.
+ *
+ * @ingroup newsletter
  */
-function simplenews_update_sent_status($node, $status = SIMPLENEWS_STATUS_SEND_PENDING) {
+function simplenews_newsletter_update_sent_status($node, $status = SIMPLENEWS_STATUS_SEND_PENDING) {
   // When this node is selected for translation, all translation of this node
   // will be sent too.
   if (module_exists('translation') && translation_supported_type($node->type) && $node->tnid > 0) {
@@ -2715,6 +2770,8 @@ function simplenews_cronapi($op) {
 
 /**
  * Returns TRUE if the newsletter category requires double opt in.
+ *
+ * @ingroup newsletter
  */
 function simplenews_require_double_opt_in($tid, $account) {
   global $user;
@@ -2731,6 +2788,8 @@ function simplenews_require_double_opt_in($tid, $account) {
 
 /**
  * Returns the available simplenews sources.
+ *
+ * @ingroup source
  */
 function simplenews_get_source_caches() {
   $sources = module_invoke_all('simplenews_source_cache_info');
@@ -2740,6 +2799,8 @@ function simplenews_get_source_caches() {
 
 /**
  * Implements hook_simplenews_source_cache_info().
+ *
+ * @ingroup source
  */
 function simplenews_simplenews_source_cache_info() {
   return array(
