The SPiD platform supports server-side callbacks that enable your service to subscribe to changes in data. Your service can then cache data and receive updates, rather than continuously poll SPiD's servers. Caching data and using this API can improve the reliability of your app or service and decrease its load times.

Whenever a subscribed change occurs, SPiD makes an HTTP POST request to a callback URL of your choice, with a list of changes. Your app will generally receive notification of the change within a couple of minutes of its occurrence.

Please ensure that your callback endpoints are accessible by SPiD through your firewalls and that errors generated by the your callback endpoint are monitored and handled.

Types of callbacks

The SPiD platform currently supports two kinds of callbacks:

  • User status changes

Callback requests

When an object changes, an HTTP POST request will be made to the client-defined callback URL. The response body will contain Base64 URL encoded text signed with your signature secret.

Note: The content-type of the POST request made by SPiD is "text/plain".

NOTE: Your signature secret is not the same as your client secret (which is used for authentication). It is a different secret specifically used for signing requests and decoding signed responses. If you do not have a signing secret, contact SPiD.

The response body contains an encoded signature and encoded data, separated by a dot, e.g. "<signature>.<data>". Here is an example:


The decoded data is a JSON object:

  "object": "user",
  "algorithm": "HMAC-SHA256",
  "entry": [
          "userId": 123,
          "changedFields": "status",
          "time": "2012-10-19 10:10:15"
          "userId": 456,
          "changedFields": "status",
          "time": "2012-10-19 10:10:19"

Decoding responses


The following example manually achieves the same effect as the PHP SDK function Client::parseSignedRequest($signed) (see callback.php in the PHP SDK for a full example).


      VGS_Client::CLIENT_ID       => '4cf36fakdk2sj17e030000',
      VGS_Client::CLIENT_SECRET   => 'lsh4nf82f',
      VGS_Client::STAGING_DOMAIN  => '',
      VGS_Client::HTTPS           => true,
      VGS_Client::REDIRECT_URI    => "",
      VGS_Client::DOMAIN          => '',
      VGS_Client::COOKIE          => true,
      VGS_Client::API_VERSION     => 2,
      VGS_Client::PRODUCTION      => true,
      VGS_Client::CLIENT_SIGN_SECRET => 'jsu3f6',

$client = new VGS_Client($SPID_CREDENTIALS);

function parse_signed_request($signed_request, $secret) {
  list($encoded_sig, $payload) = explode('.', $signed_request, 2);
  $sig = base64_url_decode($encoded_sig);
  $data = base64_url_decode($payload);
  $expected_sig = hash_hmac('sha256', $payload, $secret, $raw = true);
  if (!hash_equals($sig, $expected_sig)) {
    return null;
  return $data;

function base64_url_decode($input) {
  return base64_decode(strtr($input, '-_', '+/'));

$payload = file_get_contents("php://input");
$parsed = parse_signed_request($payload, $SPID_CREDENTIALS[VGS_Client::CLIENT_SIGN_SECRET]);
$data = json_decode($parsed, true);


The following example manually achieves the same effect as the Java API client method SpidSecurityHelper.decryptAndValidateSignedRequest(String request) (see in the Java API client for a full example).

byte[] signature = base64UrlDecode(request.split("\\.")[0]);
byte[] payload = base64UrlDecode(request.split("\\.")[1]);
byte[] expectedSignature = null;

try {
    SecretKeySpec sks = new SecretKeySpec(signSecret.getBytes("UTF-8"), "HmacSHA256");
    Mac mac = Mac.getInstance("HmacSHA256");
    expectedSignature = mac.doFinal(payload.getBytes("UTF-8"));
} catch (NoSuchAlgorithmException | InvalidKeyException | UnsupportedEncodingException ex) {
    throw ex;

if (!MessageDigest.isEqual(expectedSignature, signature)) {
    throw new SpidApiException("Signature is not valid!");

Processing callback data

The response only contains a description of what changes occurred - it does not inline individual resources. Having received, decoded and verified the integrity of callback data, the client should iterate through all the entry objects and retrieve up-to-date objects through relevant endpoints. These objects can be safely cached until the next callback request is made.

SPiD aggregates changes and sends batched updates every five minutes. This means that for every subscription, you will receive at most one request every five minutes, and possible less often (if there are fewer changes).

Callback responses

When your client receives callback requests, it should respond with HTTP code 202 Accepted. If the callback does not respond with 202, SPiD will retry again immediately, and then four more times at increasing intervals over the subsequent 25 hours. If all of these requests go un-accepted, the callback request will be marked as failed.

Retries occur after five minutes, fifteen minutes, one hour, twelve hours and again twelve hours. There is a thirty second timeout before SPiD quits the connection and considers the request failed. Do not synchronously process callback data before responding to the callback request.

User status callback

This callback will inform the client when a user's status changes.

User status state machine

Table of Contents

Relevant type definitions

Help us improve

Did you spot an error? Or maybe you just have a suggestion for how we can improve? Leave a comment, or better yet, send us a pull request on GitHub to fix it (in-browser editing, only takes a moment).

History of this page


Do you have questions, or just want to contribute some newly gained insight? Want to share an example? Please leave a comment. SPiD reads and responds to every question. Additionally, your experience can help others using SPiD, and it can help us continuously improve our documentation.