Webhooks

Webhooks allow developers to register URLs that receive notification of events for users via HTTP. Every registered webhook is uniquely identified by a globally unique ID; all subsequent operations take this ID in the resource path.

External Endpoints

Webhooks must support POST and respond with an Http Status 200 within the required timeout, otherwise the delivery will be retried. Webhook notifications are retried a maximum of 5 times, pausing 10 seconds between retries, before the webhook is marked as failed. Subsequent events are not delivered to failed webhooks. Renewing a webhook will clear its "failed" status and will start receiving event notifications once again.

Webhook endpoints must respond in a timely fashion. Our system must be able to establish a connection to the webhook URL server within 3 seconds and the Http Status 200 must be received within 2 seconds in order for the webhook delivery to be marked as a success.

Expiration

Webhook registrations expire after some period of time (expireAt, typically 10 days) after which they will no longer receive notifications. Expired (or non-expired) webhooks may be renewed indefinitely, however. Expired webhooks will be permanently expunged at some point (purgeAt). When a webhook is renewed, the renewedAt and renewedBy properties are set to the current time, and the expireAt and purgeAt properties are reset to new values after renewedAt.

When a webhook is renewed it resets the failure flag but it does not remove any other past delivery statistics.

Metadata, Signatures, & Deduplication

Along with every webhook notification event, metadata about the webhook event and its delivery can either be included in the HTTP header, the request body itself, or not at all. The metadataPolicy property controls the desired metadata delivery.

Webhook metadata consists of the following properties:

Key Description
signature The hashed value of the webhook event. If policy is HEADER, this value is sent as X-VON-Signature
webhookId The unique id of the webhook. If the policy is HEADER, this value is sent as X-VON-Webhook-Id
deliveryId A unique id identifying the specific event delivery. If the policy is HEADER, this value is sent as X-VON-Delivery-Id
attempt Identifies the number of times the event has been attempted to be delivered for the same deliveryId. If the policy is HEADER, this value is sent as X-VON-Attempt

In order for this metadata to be delivered, metadataPolicy must be set to either HEADER or BODY. Additionally, the signature is only set if signingAlgo is set to HMAC_SHA256 and a non-empty signingKey is set.

Since the system retries delivery for webhook entries that have timed-out, the endpoint itself needs to know whether multiple POSTs to a URL represent the same or different event. The deliveryId and attempt properties provide the endpoint the necessary information to distinguish between separate notification events versus retries.

Events

Webhook notification events have the following format, for example:

{
    "event": {
        "accountId": "-1",
        "direction": "OUTBOUND",
        "duration": 0,
        "externalId": "abc1234-288c-40d3-8ec8-3618a3ae7698_123",
        "id": "abc1234a806d07a6ff17ba",
        "internal": false,
        "phoneNumber": "xxxxxxxxxx",
        "startTime": "2020-10-01T16:10:06.000+0000",
        "state": "RINGING",
        "type": "CALL",
        "ucpType": "VBS",
        "userId": "1234"
    },
    "metadata": {
        "attempt": 1,
        "deliveryId": "abc1234-2795-446c-be6b-7cba85c6bba2",
        "signature": "abc1234663a4d0589ec092677b4af18b4a747ac8cfa6198a57b8c6bfec9bf28a",
        "webhookId": "abc1234-c1ad-4a14-a30b-69dd92f57af2"
    }
}

The metadata property is only included if metadataPolicy is set to BODY. The signature, a hex-encoded string, is computed from the json value of the event property, where all properties are sorted alphabetically with no whitespace between property names or their values. For example, the signature on the above example event body would be computed on the following json:

{"accountId": "-1","direction": "OUTBOUND","duration": 0,"externalId": "abc1234-288c-40d3-8ec8-3618a3ae7698_123","id": "abc1234a806d07a6ff17ba","internal": false,"phoneNumber": "xxxxxxxxxx","startTime": "2020-10-01T16:10:06.000+0000","state": "RINGING","type": "CALL","ucpType": "VBS","userId": "1234"}

And using the secret key mysecretkey would have the following signature:

`95aafd08cb72b1f9216ccd002b8917b04e41ecb19276ae759241fdc0cbb53fb5.`

Redirection Policy

Webhook endpoints are permitted to return either Http Status 302 or 307. The system will follow redirects, up to some limit, after which, the system will mark the webhook as failed. If the webhook endpoint returns a 302, then the webhook event is delivered as an Http Get to the URL found in the response Location header. If a 307 is returned, then the webhook event is delivered as an Http POST to the URL found in the response Location header.

Statistics

The system keeps track of the total number of attempts, successes and failures. It also keeps track of the date/time of the last success and failure. For failures, the most recent Http status code and descriptive message are also kept. When a webhook is renewed only the isFailed property is reset back to false; all other statistics are kept for informational purposes.

A webhook is not considered either a success or failure until all of the retry attempts for a particular delivery have been exhausted. For example, if system retries a webhook delivery five times and fails, it counts as a single additional attempt and failure.

Webhook statistics can be used to discover events that are potentially missed if the URL is temporarily unavailable (for example, because of system maintenance). In such a case, missed events can be queried for everything that happened after lastSuccess. It is good practice to renew any existing webhooks immediately after a system is restored following maintenance.