diff --git a/PuSHHub.inc b/PuSHHub.inc
index 4b6a707..e738096 100644
--- a/PuSHHub.inc
+++ b/PuSHHub.inc
@@ -97,20 +97,93 @@ class PuSHHub {
   /**
    * Handle a subscription request.
    *
-   * @param $post
-   *   A valid PubSubHubbub subscription request.
+   * @param array $post
+   *   A valid PubSubHubbub subscribe request.
+   *
+   *   The keys of this array are defined at http://pubsubhubbub.googlecode.com/svn/trunk/pubsubhubbub-core-0.3.html#anchor5
+   *
+   *   - 'hub_callback': REQUIRED. The subscriber's callback URL where
+   *     notifications should be delivered.
+   *   - 'hub_mode': REQUIRED. The literal string "subscribe".
+   *   - 'hub_topic': REQUIRED. The topic URL that the subscriber wishes to
+   *     subscribe to.
+   *   - 'hub_secret': OPTIONAL. A subscriber-provided secret string that will
+   *     be used to compute an HMAC digest for authorized content distribution.
+   *     If not supplied, the HMAC digest will not be present for content
+   *     distribution requests. This parameter SHOULD only be specified when the
+   *     request was made over HTTPS [RFC2818]. This parameter MUST be less than
+   *     200 bytes in length.
+   *   - 'hub_verify_token': OPTIONAL. A subscriber-provided opaque token that
+   *     will be echoed back in the verification request to assist the subscriber
+   *     in identifying which subscription request is being verified. If this is
+   *     not included, no token will be included in the verification request.
+   *
+   *   This Push Hub Drupal module deviates from the spec in it's usage of hub_verify.
+   *   The spec intends this parameter use values of 'sync' and/or 'async' to
+   *   determine how the hub will verify the subscription. This module verifies
+   *   immediately.
+   *
+   *   The module maintains spec conformance while ignoring incoming values for
+   *   hub.lease_seconds. The constant PUSH_HUB_LEASE_SECONDS is used instead.
+   *
+   * @return
+   *   Boolean TRUE or FALSE for whether the subscription was successful or unsuccessful.
    */
   public function subscribe($post) {
     watchdog('push_hub', "subscribe request", NULL, WATCHDOG_DEBUG);
-    if (isset($post['hub_topic']) && isset($post['hub_callback']) && $this->verify($post)) {
+    if ($post['hub_mode'] === 'subscribe' && isset($post['hub_topic']) && isset($post['hub_callback'])  && $this->verify($post)) {
       $this->subscriptions->save($post['hub_topic'], $post['hub_callback'], isset($post['hub_secret']) ? $post['hub_secret'] : '');
-      header('HTTP/1.1 204 "No Content"', null, 204);
       watchdog('push_hub', "subscription verified and saved", NULL, WATCHDOG_DEBUG);
-      exit();
+      return TRUE;
+    } else {
+      watchdog('push_hub', "subscription invalid and not saved", NULL, WATCHDOG_DEBUG);
+      return FALSE;
+    }
+  }
+
+  /**
+   * Handle a subscription request.
+   *
+   *   The keys of this array are defined at http://pubsubhubbub.googlecode.com/svn/trunk/pubsubhubbub-core-0.3.html#anchor5
+   *
+   *   - 'hub_callback': REQUIRED. The subscriber's callback URL where
+   *     notifications should be delivered.
+   *   - 'hub_mode': REQUIRED. The literal string "unsubscribe".
+   *   - 'hub_topic': REQUIRED. The topic URL that the subscriber wishes to
+   *     subscribe to.
+   *   - 'hub_secret': OPTIONAL. A subscriber-provided secret string that will
+   *     be used to compute an HMAC digest for authorized content distribution.
+   *     If not supplied, the HMAC digest will not be present for content
+   *     distribution requests. This parameter SHOULD only be specified when the
+   *     request was made over HTTPS [RFC2818]. This parameter MUST be less than
+   *     200 bytes in length.
+   *   - 'hub_verify_token': OPTIONAL. A subscriber-provided opaque token that
+   *     will be echoed back in the verification request to assist the subscriber
+   *     in identifying which subscription request is being verified. If this is
+   *     not included, no token will be included in the verification request.
+   *
+   *   This Push Hub Drupal module deviates from the spec in it's usage of hub_verify.
+   *   The spec intends this parameter use values of 'sync' and/or 'async' to
+   *   determine how the hub will verify the subscription. This module verifies
+   *   immediately.
+   *
+   *   The module maintains spec conformance while ignoring incoming values for
+   *   hub.lease_seconds. The constant PUSH_HUB_LEASE_SECONDS is used instead.
+   *
+   * @return
+   *   Boolean TRUE or FALSE for whether the subscription was successful or unsuccessful.
+   */
+  public function unsubscribe($post) {
+    watchdog('push_hub', "unsubscribe request", NULL, WATCHDOG_DEBUG);
+    if ($post['hub_mode'] === 'unsubscribe' && isset($post['hub_topic']) && isset($post['hub_callback'])  && $this->verify($post)) {
+      // @todo, does delete need to return anything to verify that the delete actually happened?
+      $this->subscriptions->delete($post['hub_topic'], $post['hub_callback']);
+      watchdog('push_hub', "unsubscription verified and record deleted", NULL, WATCHDOG_DEBUG);
+      return TRUE;
+    } else {
+      watchdog('push_hub', "unsubscription invalid and no records deleted", NULL, WATCHDOG_DEBUG);
+      return FALSE;
     }
-    watchdog('push_hub', "subscription invalid and not saved", NULL, WATCHDOG_DEBUG);
-    header('HTTP/1.1 404 "Not Found"', null, 404);
-    exit();
   }
 
   /**
diff --git a/push_hub.pages.inc b/push_hub.pages.inc
index eb98583..1ebec7d 100644
--- a/push_hub.pages.inc
+++ b/push_hub.pages.inc
@@ -4,5 +4,86 @@
  * Hub endpoint.
  */
 function push_hub_endpoint() {
-  push_hub()->subscribe($_POST);
+
+  // These parameters are required in the pubsubhubbub spec.
+  // http://pubsubhubbub.googlecode.com/svn/trunk/pubsubhubbub-core-0.3.html
+  // @todo hub_verify is required in the spec but this module only supports immediate verification.
+  $required_parameters = array(
+    'hub_callback',
+    'hub_mode',
+    'hub_topic',
+    'hub_verify',
+  );
+
+  // Instead of passing around the full $_POST directly, build up an array
+  // that only contains parameters in the pubsubhubub spec.
+  $passable_post = array();
+
+  // Assume that $_POST has required paramters. Set to false when one is found
+  // to be missing.
+  $has_required_parameters = TRUE;
+
+  // if required parameters are missing, build up an array for the error message.
+  $missing_parameters = array();
+
+  // A boolean for whether the subscribe/unsubscribe request was successful.
+  $success = FALSE;
+
+  // Build up $passable_post
+  foreach ($required_parameters as $value) {
+    if (!empty($_POST[$value])) {
+      $passable_post[$value] = $_POST[$value];
+    }
+    else {
+      $has_required_parameters = FALSE;
+      $missing_parameters[] = $value;
+    }
+  }
+
+  // Check that no required parameters were found to be missing.
+  if ($has_required_parameters === TRUE) {
+
+    // These parameters are in the pubsubhubbub spec but not required.
+    $optional_parameters = array (
+      'hub_lease_seconds',
+      'hub_secret',
+      'hub_verify_token',
+    );
+
+    // Add optional parameters to $passable_post.
+    foreach ($optional_parameters as $value) {
+      if (!empty($_POST[$value])) {
+        $passable_post[$value] = $_POST[$value];
+      }
+    }
+
+    // hub_mode should only be 'subscribe' or 'unsubscribe'.
+    switch($passable_post['hub_mode']) {
+      case 'subscribe':
+        $success = push_hub()->subscribe($passable_post);
+        break;
+      case 'unsubscribe':
+        $success = push_hub()->unsubscribe($passable_post);
+        break;
+      default:
+        watchdog('push_hub', 'subscription invalid and not saved. hub_mode was not "subscribe" or "unsubscribe".', NULL, WATCHDOG_DEBUG);
+        $success = FALSE;
+        break;
+    }
+  }
+  else {
+    watchdog('push_hub', 'subscription invalid and not saved. POST did not contain all required parameters. Missing ' . implode(', ', $missing_parameters), NULL, WATCHDOG_DEBUG);
+    $success = FALSE;
+  }
+
+  // If the post was succesful, set a 204. Otherwise set a 404.
+  if ($success === TRUE) {
+    header('HTTP/1.1 204 "No Content"', null, 204);
+  }
+  else {
+    header('HTTP/1.1 404 "Not Found"', null, 404);
+  }
+
+  // The request is complete. Exit.
+  drupal_exit();
 }
