Skip to content

API reference

Webhooks

Endpoints we POST signed events to, their delivery history and resends.

10 operationsBase URL https://packetexchange.io/api/v1Postman collection

List your webhook endpoints

GET/api/v1/account/webhooks

Access
API key. Scoped keys need account:read.
Rate limit
100 requests per second (the default)

Secrets are redacted to their last 4 characters.

Response 200

  • datarequired

    object[]

  • idrequired

    string (uuid) · data[].id

  • urlrequired

    string (uri) · data[].url

  • secretLast4required

    string · data[].secretLast4

    Last 4 characters of the signing secret, to tell secrets apart
  • eventsrequired

    string[] · data[].events

    One of call.completed, call.ringing, call.answered, call.gathered, sms.sent, sms.dlr, sms.delivered, sms.failed, campaign.started, campaign.completed, topup.confirmed, balance.low and 24 more
  • isActiverequired

    boolean · data[].isActive

    False when you disabled it, or after sustained delivery failures
  • failureCountrequired

    integer · data[].failureCount

    Consecutive failed deliveries; resets on success
  • lastDeliveryAtrequired

    string (date-time) | null · data[].lastDeliveryAt

    ISO-8601 timestamp (UTC)
  • createdAtrequired

    string (date-time) · data[].createdAt

    ISO-8601 timestamp (UTC)
  • updatedAtrequired

    string (date-time) · data[].updatedAt

    ISO-8601 timestamp (UTC)

Errors

  • 400VALIDATION_ERROR, INVALID_INPUT or BAD_REQUEST. For VALIDATION_ERROR, error.details is an array of { path, message }.
  • 401UNAUTHORIZED: missing, invalid, expired or revoked credential.
  • 403FORBIDDEN: not allowed - a scoped key lacks the scope, or the endpoint is session-only.
  • 429RATE_LIMITED: slow down and retry after the Retry-After seconds.
  • 500INTERNAL_ERROR: unexpected failure. Quote X-Request-Id to support.

Create a webhook endpoint

POST/api/v1/account/webhooks

Access
API key. Scoped keys need webhooks:write.
Rate limit
100 requests per second (the default)

The signing secret is returned ONCE in secret. URLs must be https and resolve to a public address. Works from a dashboard session, or with an API key that was created with the webhooks:write permission. A full-access key does not include webhooks:write: it has to be chosen explicitly, so keys issued before this permission existed cannot manage endpoints.

Each delivery is a POST of { event, data, timestamp } with headers X-Webhook-Event, X-Webhook-Id (the delivery id), X-PX-Timestamp (Unix seconds at send time) and two signatures: X-PX-Signature: v1=<hex HMAC-SHA256 of "<timestamp>.<raw body>"> (verify this one, and reject timestamps more than 5 minutes old to stop replays) and the legacy X-Webhook-Signature: sha256=<hex HMAC-SHA256 of the raw body>, kept during the transition.

Request body (application/json)

  • urlrequired

    string (uri)

  • eventsrequired

    string[]

    One of call.completed, call.ringing, call.answered, call.gathered, sms.sent, sms.dlr, sms.delivered, sms.failed, campaign.started, campaign.completed, topup.confirmed, balance.low and 24 more

Response 201

  • datarequired

    object

  • idrequired

    string (uuid) · data.id

  • urlrequired

    string (uri) · data.url

  • secretLast4required

    string · data.secretLast4

    Last 4 characters of the signing secret, to tell secrets apart
  • eventsrequired

    string[] · data.events

    One of call.completed, call.ringing, call.answered, call.gathered, sms.sent, sms.dlr, sms.delivered, sms.failed, campaign.started, campaign.completed, topup.confirmed, balance.low and 24 more
  • isActiverequired

    boolean · data.isActive

    False when you disabled it, or after sustained delivery failures
  • failureCountrequired

    integer · data.failureCount

    Consecutive failed deliveries; resets on success
  • lastDeliveryAtrequired

    string (date-time) | null · data.lastDeliveryAt

    ISO-8601 timestamp (UTC)
  • createdAtrequired

    string (date-time) · data.createdAt

    ISO-8601 timestamp (UTC)
  • updatedAtrequired

    string (date-time) · data.updatedAt

    ISO-8601 timestamp (UTC)
  • secretrequired

    string · data.secret

    The signing secret. Returned ONCE; store it now.

Errors

  • 400VALIDATION_ERROR, INVALID_INPUT or BAD_REQUEST. For VALIDATION_ERROR, error.details is an array of { path, message }.
  • 401UNAUTHORIZED: missing, invalid, expired or revoked credential.
  • 403FORBIDDEN: not allowed - a scoped key lacks the scope, or the endpoint is session-only.
  • 409CONFLICT (or a code-specific 409): the change clashes with existing state.
  • 429RATE_LIMITED: slow down and retry after the Retry-After seconds.
  • 500INTERNAL_ERROR: unexpected failure. Quote X-Request-Id to support.

Update a webhook endpoint

PATCH/api/v1/account/webhooks/{id}

Access
API key. Scoped keys need webhooks:write.
Rate limit
100 requests per second (the default)

Change the URL, the subscribed events or isActive. The signing secret and delivery history are kept. Works from a dashboard session, or with an API key that was created with the webhooks:write permission. A full-access key does not include webhooks:write: it has to be chosen explicitly, so keys issued before this permission existed cannot manage endpoints.

Parameters

  • idrequired

    path · string

Request body (application/json)

  • url

    string (uri)

  • events

    string[]

    One of call.completed, call.ringing, call.answered, call.gathered, sms.sent, sms.dlr, sms.delivered, sms.failed, campaign.started, campaign.completed, topup.confirmed, balance.low and 24 more
  • isActive

    boolean

Response 200

  • datarequired

    object

    Same fields as Webhook, shown earlier on this page.

Errors

  • 400VALIDATION_ERROR, INVALID_INPUT or BAD_REQUEST. For VALIDATION_ERROR, error.details is an array of { path, message }.
  • 401UNAUTHORIZED: missing, invalid, expired or revoked credential.
  • 403FORBIDDEN: not allowed - a scoped key lacks the scope, or the endpoint is session-only.
  • 404NOT_FOUND: no such resource on your account.
  • 409CONFLICT (or a code-specific 409): the change clashes with existing state.
  • 429RATE_LIMITED: slow down and retry after the Retry-After seconds.
  • 500INTERNAL_ERROR: unexpected failure. Quote X-Request-Id to support.

Delete a webhook endpoint

DELETE/api/v1/account/webhooks/{id}

Access
API key. Scoped keys need webhooks:write.
Rate limit
100 requests per second (the default)

Removes the endpoint, its secret and its delivery history. Events stop immediately. Works from a dashboard session, or with an API key that was created with the webhooks:write permission. A full-access key does not include webhooks:write: it has to be chosen explicitly, so keys issued before this permission existed cannot manage endpoints.

Parameters

  • idrequired

    path · string

Response 200

  • datarequired

    object

  • idrequired

    string (uuid) · data.id

  • deletedrequired

    boolean · data.deleted

    One of true

Errors

  • 400VALIDATION_ERROR, INVALID_INPUT or BAD_REQUEST. For VALIDATION_ERROR, error.details is an array of { path, message }.
  • 401UNAUTHORIZED: missing, invalid, expired or revoked credential.
  • 403FORBIDDEN: not allowed - a scoped key lacks the scope, or the endpoint is session-only.
  • 404NOT_FOUND: no such resource on your account.
  • 409CONFLICT (or a code-specific 409): the change clashes with existing state.
  • 429RATE_LIMITED: slow down and retry after the Retry-After seconds.
  • 500INTERNAL_ERROR: unexpected failure. Quote X-Request-Id to support.

Rotate a webhook signing secret

POST/api/v1/account/webhooks/{id}/rotate-secret

Access
API key. Scoped keys need webhooks:write.
Rate limit
100 requests per second (the default)

Issues a new secret, returned once. Deliveries are signed with it from this moment (including pending retries); there is no overlap window, so update your receiver at the same time. Works from a dashboard session, or with an API key that was created with the webhooks:write permission. A full-access key does not include webhooks:write: it has to be chosen explicitly, so keys issued before this permission existed cannot manage endpoints.

Parameters

  • idrequired

    path · string

Response 200

  • datarequired

    object

  • idrequired

    string (uuid) · data.id

  • urlrequired

    string (uri) · data.url

  • secretLast4required

    string · data.secretLast4

    Last 4 characters of the signing secret, to tell secrets apart
  • eventsrequired

    string[] · data.events

    One of call.completed, call.ringing, call.answered, call.gathered, sms.sent, sms.dlr, sms.delivered, sms.failed, campaign.started, campaign.completed, topup.confirmed, balance.low and 24 more
  • isActiverequired

    boolean · data.isActive

    False when you disabled it, or after sustained delivery failures
  • failureCountrequired

    integer · data.failureCount

    Consecutive failed deliveries; resets on success
  • lastDeliveryAtrequired

    string (date-time) | null · data.lastDeliveryAt

    ISO-8601 timestamp (UTC)
  • createdAtrequired

    string (date-time) · data.createdAt

    ISO-8601 timestamp (UTC)
  • updatedAtrequired

    string (date-time) · data.updatedAt

    ISO-8601 timestamp (UTC)
  • secretrequired

    string · data.secret

    The signing secret. Returned ONCE; store it now.
  • messagerequired

    string · data.message

Errors

  • 400VALIDATION_ERROR, INVALID_INPUT or BAD_REQUEST. For VALIDATION_ERROR, error.details is an array of { path, message }.
  • 401UNAUTHORIZED: missing, invalid, expired or revoked credential.
  • 403FORBIDDEN: not allowed - a scoped key lacks the scope, or the endpoint is session-only.
  • 404NOT_FOUND: no such resource on your account.
  • 409CONFLICT (or a code-specific 409): the change clashes with existing state.
  • 429RATE_LIMITED: slow down and retry after the Retry-After seconds.
  • 500INTERNAL_ERROR: unexpected failure. Quote X-Request-Id to support.

Send a test ping to a webhook endpoint

POST/api/v1/account/webhooks/{id}/test

Access
API key. Scoped keys need account:write.
Rate limit
100 requests per second (the default)

Queues a ping delivery, signed like any other, usually delivered within 15 seconds.

Parameters

  • idrequired

    path · string

Response 202

  • datarequired

    object

  • idrequired

    string (uuid) · data.id

  • statusrequired

    string · data.status

    One of pending, sending, delivered, failed
  • eventrequired

    string · data.event

    One of ping

Errors

  • 400VALIDATION_ERROR, INVALID_INPUT or BAD_REQUEST. For VALIDATION_ERROR, error.details is an array of { path, message }.
  • 401UNAUTHORIZED: missing, invalid, expired or revoked credential.
  • 403FORBIDDEN: not allowed - a scoped key lacks the scope, or the endpoint is session-only.
  • 404NOT_FOUND: no such resource on your account.
  • 409CONFLICT (or a code-specific 409): the change clashes with existing state.
  • 429RATE_LIMITED: slow down and retry after the Retry-After seconds.
  • 500INTERNAL_ERROR: unexpected failure. Quote X-Request-Id to support.

List deliveries for one webhook endpoint

GET/api/v1/account/webhooks/{id}/deliveries

Access
API key. Scoped keys need account:read.
Rate limit
100 requests per second (the default)

Parameters

  • idrequired

    path · string

  • cursor

    query · string (uuid)

    The nextCursor from the previous page
  • limit

    query · integer

    Default 20

Response 200

  • datarequired

    object[]

  • idrequired

    string (uuid) · data[].id

    Also sent as the X-Webhook-Id header. Automatic retries reuse it (de-duplicate on it); a manual resend is a new delivery with a new id.
  • webhookId

    string (uuid) · data[].webhookId

    Present on the account-wide list
  • url

    string · data[].url

    The endpoint URL, on the account-wide list
  • eventrequired

    string · data[].event

  • statusrequired

    string · data[].status

    One of pending, sending, delivered, failed
  • httpStatusrequired

    integer | null · data[].httpStatus

    Response code your server returned on the last attempt
  • attemptsrequired

    integer · data[].attempts

    Attempts so far (max 5, with attempts^2-minute backoff)
  • lastErrorrequired

    string | null · data[].lastError

  • nextRetryAtrequired

    string (date-time) | null · data[].nextRetryAt

    ISO-8601 timestamp (UTC)
  • createdAtrequired

    string (date-time) · data[].createdAt

    ISO-8601 timestamp (UTC)
  • deliveredAtrequired

    string (date-time) | null · data[].deliveredAt

    ISO-8601 timestamp (UTC)
  • nextCursorrequired

    string | null

    Pass as cursor to fetch the next page; null on the last page.
  • hasMorerequired

    boolean

    True when another page exists.

Errors

  • 400VALIDATION_ERROR, INVALID_INPUT or BAD_REQUEST. For VALIDATION_ERROR, error.details is an array of { path, message }.
  • 401UNAUTHORIZED: missing, invalid, expired or revoked credential.
  • 403FORBIDDEN: not allowed - a scoped key lacks the scope, or the endpoint is session-only.
  • 404NOT_FOUND: no such resource on your account.
  • 429RATE_LIMITED: slow down and retry after the Retry-After seconds.
  • 500INTERNAL_ERROR: unexpected failure. Quote X-Request-Id to support.

List webhook deliveries across all your endpoints

GET/api/v1/account/webhooks/deliveries

Access
API key. Scoped keys need account:read.
Rate limit
100 requests per second (the default)

Newest first. Filter by endpoint, status or event. Includes the endpoint URL on each row.

Parameters

  • cursor

    query · string (uuid)

    The nextCursor from the previous page
  • limit

    query · integer

    Default 20
  • webhookId

    query · string (uuid)

    Only deliveries to this endpoint
  • status

    query · string

    One of pending, sending, delivered, failed
  • event

    query · string

    Only this event name, e.g. call.completed

Response 200

  • datarequired

    object[]

    Same fields as WebhookDelivery, shown earlier on this page.
  • nextCursorrequired

    string | null

    Pass as cursor to fetch the next page; null on the last page.
  • hasMorerequired

    boolean

    True when another page exists.

Errors

  • 400VALIDATION_ERROR, INVALID_INPUT or BAD_REQUEST. For VALIDATION_ERROR, error.details is an array of { path, message }.
  • 401UNAUTHORIZED: missing, invalid, expired or revoked credential.
  • 403FORBIDDEN: not allowed - a scoped key lacks the scope, or the endpoint is session-only.
  • 429RATE_LIMITED: slow down and retry after the Retry-After seconds.
  • 500INTERNAL_ERROR: unexpected failure. Quote X-Request-Id to support.

Get one webhook delivery, with its payload

GET/api/v1/account/webhooks/deliveries/{deliveryId}

Access
API key. Scoped keys need account:read.
Rate limit
100 requests per second (the default)

Parameters

  • deliveryIdrequired

    path · string

Response 200

  • datarequired

    object

  • idrequired

    string (uuid) · data.id

    Also sent as the X-Webhook-Id header. Automatic retries reuse it (de-duplicate on it); a manual resend is a new delivery with a new id.
  • webhookId

    string (uuid) · data.webhookId

    Present on the account-wide list
  • url

    string · data.url

    The endpoint URL, on the account-wide list
  • eventrequired

    string · data.event

  • statusrequired

    string · data.status

    One of pending, sending, delivered, failed
  • httpStatusrequired

    integer | null · data.httpStatus

    Response code your server returned on the last attempt
  • attemptsrequired

    integer · data.attempts

    Attempts so far (max 5, with attempts^2-minute backoff)
  • lastErrorrequired

    string | null · data.lastError

  • nextRetryAtrequired

    string (date-time) | null · data.nextRetryAt

    ISO-8601 timestamp (UTC)
  • createdAtrequired

    string (date-time) · data.createdAt

    ISO-8601 timestamp (UTC)
  • deliveredAtrequired

    string (date-time) | null · data.deliveredAt

    ISO-8601 timestamp (UTC)
  • payloadrequired

    object · data.payload

    The exact JSON body that was (or will be) POSTed

Errors

  • 400VALIDATION_ERROR, INVALID_INPUT or BAD_REQUEST. For VALIDATION_ERROR, error.details is an array of { path, message }.
  • 401UNAUTHORIZED: missing, invalid, expired or revoked credential.
  • 403FORBIDDEN: not allowed - a scoped key lacks the scope, or the endpoint is session-only.
  • 404NOT_FOUND: no such resource on your account.
  • 429RATE_LIMITED: slow down and retry after the Retry-After seconds.
  • 500INTERNAL_ERROR: unexpected failure. Quote X-Request-Id to support.

Resend a webhook delivery

POST/api/v1/account/webhooks/deliveries/{deliveryId}/resend

Access
API key. Scoped keys need account:write.
Rate limit
30 requests per minute

Queues a NEW delivery with the same event and payload to the same endpoint, signed with a fresh timestamp. The original delivery is left as it was, so the history shows both. The new delivery has its own id (X-Webhook-Id); the payload is byte-identical, so de-duplicate on your own event data if you need exactly-once processing. The endpoint must be active.

Parameters

  • deliveryIdrequired

    path · string

Response 202

  • datarequired

    object

    Same fields as WebhookDelivery, shown earlier on this page.

Errors

  • 400VALIDATION_ERROR, INVALID_INPUT or BAD_REQUEST. For VALIDATION_ERROR, error.details is an array of { path, message }.
  • 401UNAUTHORIZED: missing, invalid, expired or revoked credential.
  • 403FORBIDDEN: not allowed - a scoped key lacks the scope, or the endpoint is session-only.
  • 404NOT_FOUND: no such resource on your account.
  • 409CONFLICT (or a code-specific 409): the change clashes with existing state.
  • 429RATE_LIMITED: slow down and retry after the Retry-After seconds.
  • 500INTERNAL_ERROR: unexpected failure. Quote X-Request-Id to support.