PHP callback handler for Dropbox Sign

Updated Sep 29, 2025

In this article

You need to have a method that can accept a HTTP POST request. Typically this is done by accessing the $_POST array.

This is what a very simple implementation of the callback handler looks like in PHP, and is ok for illustrating how callbacks can be setup.

<?php
// If people find their way here, give them a way to get back home
echo '<a href="index.php">Home<br /></a>';
$data = json_decode($_POST['json']);
$api_key = getenv('HS_APIKEY') ? getenv('HS_APIKEY') : '';

// this checks the MD5 header before processing the body of the POST
$md5_header_check = base64_encode(hash_hmac('md5', $data, $api_key));
$md5_header = $_SERVER['Content-MD5'];
 
if ($md5_header != $md5_header_check) {
     echo 'well that didn\'t work';
     // seems fishy that it didn't work...
     // maybe notify yourself that it failed but save the POST body
     // so you can look into it
     goto nope_skip;
}

// Verify the event hash:
$event_time = $data->event->event_time;
$event_type = $data->event->event_type;
$calculated_hash = hash_hmac("sha256", $event_time . $event_type, $api_key);
$event_hash = $data->event->event_hash;
if ($calculated_hash !== $event_hash) {
     goto nope_skip;
}
$reported_app = $data->event->event_metadata->reported_for_app_id;
if ($reported_app === 'my_testing_app_client_id_here') {
     if ($event_type === 'signature_request_all_signed') {
   
     // The signature_request_all_signed event is called whenever the signature
     // request is completely signed by all signees, and Dropbox Sign has processed
     // the document, and it's available for download.

      } elseif ($event_type === 'callback_test') {

     // do something cool

      } else {

     // do something cool

      }
}

// Always be sure to return this response so that Dropbox Sign knows
// that your app processed the event successfully. Otherwise, Dropbox Sign
// will assume it failed and will retry up to six more times.
echo 'Hello API Event Received';
// Skip everything and fail the response if the hash doesn't match
nope_skip:
Once that's setup, you could use something like ngrok to test locally
Was this article helpful?

Let us know how why it didn't help:

Thanks for letting us know!

Thanks for your feedback!

Other ways to get help