Webhooks API
Manage webhook subscriptions for event notifications.
Every request needs a bearer token and goes to a path under https://api.anypost.com/v1. See API conventions for the shared request, error, and pagination rules.
List webhooks
GET /v1/webhooks
Returns the authenticated team's webhooks, newest-first, with cursor pagination.
Parameters
limitintegerin query[optional]Number of items to return.
afterstringin query[optional]Opaque cursor from a previous response's
next_cursor. Do not parse.
Example request
curl https://api.anypost.com/v1/webhooks \
-H "Authorization: Bearer $ANYPOST_API_KEY"Response body
On success (200), the response body is:
dataarray of Webhookhas_morebooleannext_cursorstringMay be null.
{
"data": [
{
"id": "wh_550e8400-e29b-41d4-a716-446655440000",
"name": "Production deliverability hook",
"url": "https://hooks.example.com/anypost",
"events": [
"email.sent"
],
"status": "active",
"signing_secret_prefix": "whsec_AbCdEf",
"signing_secret_previous_prefix": "whsec_XyZwVu",
"signing_secret_grace_expires_at": "string",
"last_delivery_at": "string",
"created_at": "string"
}
],
"has_more": true,
"next_cursor": "string"
}Responses
| Status | Description |
|---|---|
200 | Paginated list of webhooks. |
401 | Missing or invalid credentials. |
Create a webhook
POST /v1/webhooks
Creates a new webhook subscription for the authenticated team. The full signing secret is returned only in the response to this request — store it securely; subsequent reads return only the prefix. Outbound delivery payloads are HMAC-signed with this secret so the receiver can verify authenticity.
Request body
Send as JSON with Content-Type: application/json.
namestring[required]urlstring (uri)[required]HTTPS endpoint that will receive signed event payloads. Must use the
https://scheme.eventsarray of string[required]
Example request
curl https://api.anypost.com/v1/webhooks \
-X POST \
-H "Authorization: Bearer $ANYPOST_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "Production deliverability hook",
"url": "https://hooks.example.com/anypost",
"events": [
"email.sent"
]
}'Response body
On success (201), the response body is:
idstringnamestringurlstring (uri)HTTPS endpoint that will receive signed event payloads. Must use the
https://scheme.eventsarray of stringEvent types this webhook subscribes to.
statusstringactivewebhooks receive event deliveries;disabledwebhooks are paused — no deliveries are attempted, but the configuration is preserved so it can be resumed.circuit_disabledis a transient state set automatically after consecutive delivery failures: events are still queued and retried, and the webhook returns toactiveon recovery. Onlyactiveanddisabledmay be set through the API;circuit_disabledis server-managed.One of:
active,circuit_disabled,disabled.signing_secret_prefixstringThe first 12 characters of the signing secret, shown for identification.
signing_secret_previous_prefixstringThe first 12 characters of the previous signing secret while a rotation grace window is active, else
null. During the window deliveries are signed with both secrets.May be null.
signing_secret_grace_expires_atstring (date-time)When the current rotation's 24h grace window ends and the previous signing secret stops being honored, or
nullif no rotation is in progress.May be null.
last_delivery_atstring (date-time)May be null.
created_atstring (date-time)signing_secretstringThe full signing secret. Returned only once at creation. Store it securely; future deliveries will be HMAC-signed with it.
{
"id": "wh_550e8400-e29b-41d4-a716-446655440000",
"name": "Production deliverability hook",
"url": "https://hooks.example.com/anypost",
"events": [
"email.sent"
],
"status": "active",
"signing_secret_prefix": "whsec_AbCdEf",
"signing_secret_previous_prefix": "whsec_XyZwVu",
"signing_secret_grace_expires_at": "string",
"last_delivery_at": "string",
"created_at": "string",
"signing_secret": "whsec_AbCdEfGhIjKlMnOp..."
}Responses
| Status | Description |
|---|---|
201 | Webhook created. The full signing secret is returned only in this response. |
401 | Missing or invalid credentials. |
413 | Request body exceeded the 5 MB gateway limit. Rejected at the transport layer before authentication or validation, so the response uses a shorter error shape than application-layer errors. The connection is closed after the response. |
422 | Request validation failed. |
Retrieve a webhook
GET /v1/webhooks/{id}
Returns metadata for a single webhook. The full signing secret is never returned here — only signing_secret_prefix.
Parameters
idstringin path[required]
Example request
curl https://api.anypost.com/v1/webhooks/wh_550e8400-e29b-41d4-a716-446655440000 \
-H "Authorization: Bearer $ANYPOST_API_KEY"Response body
On success (200), the response body is:
idstringnamestringurlstring (uri)HTTPS endpoint that will receive signed event payloads. Must use the
https://scheme.eventsarray of stringEvent types this webhook subscribes to.
statusstringactivewebhooks receive event deliveries;disabledwebhooks are paused — no deliveries are attempted, but the configuration is preserved so it can be resumed.circuit_disabledis a transient state set automatically after consecutive delivery failures: events are still queued and retried, and the webhook returns toactiveon recovery. Onlyactiveanddisabledmay be set through the API;circuit_disabledis server-managed.One of:
active,circuit_disabled,disabled.signing_secret_prefixstringThe first 12 characters of the signing secret, shown for identification.
signing_secret_previous_prefixstringThe first 12 characters of the previous signing secret while a rotation grace window is active, else
null. During the window deliveries are signed with both secrets.May be null.
signing_secret_grace_expires_atstring (date-time)When the current rotation's 24h grace window ends and the previous signing secret stops being honored, or
nullif no rotation is in progress.May be null.
last_delivery_atstring (date-time)May be null.
created_atstring (date-time)
{
"id": "wh_550e8400-e29b-41d4-a716-446655440000",
"name": "Production deliverability hook",
"url": "https://hooks.example.com/anypost",
"events": [
"email.sent"
],
"status": "active",
"signing_secret_prefix": "whsec_AbCdEf",
"signing_secret_previous_prefix": "whsec_XyZwVu",
"signing_secret_grace_expires_at": "string",
"last_delivery_at": "string",
"created_at": "string"
}Responses
| Status | Description |
|---|---|
200 | The webhook. |
404 | Resource not found. |
Update a webhook
PATCH /v1/webhooks/{id}
Updates an existing webhook's name, URL, subscribed events, and
status. The signing secret is not rotated here — use
POST /webhooks/{id}/rotate-secret for that. Pause delivery
without losing configuration by setting status to disabled.
Parameters
idstringin path[required]
Request body
Send as JSON with Content-Type: application/json.
namestring[required]urlstring (uri)[required]HTTPS endpoint that will receive signed event payloads. Must use the
https://scheme.eventsarray of string[required]statusstring[required]One of:
active,disabled.
Example request
curl https://api.anypost.com/v1/webhooks/wh_550e8400-e29b-41d4-a716-446655440000 \
-X PATCH \
-H "Authorization: Bearer $ANYPOST_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "string",
"url": "string",
"events": [
"email.sent"
],
"status": "active"
}'Response body
On success (200), the response body is:
idstringnamestringurlstring (uri)HTTPS endpoint that will receive signed event payloads. Must use the
https://scheme.eventsarray of stringEvent types this webhook subscribes to.
statusstringactivewebhooks receive event deliveries;disabledwebhooks are paused — no deliveries are attempted, but the configuration is preserved so it can be resumed.circuit_disabledis a transient state set automatically after consecutive delivery failures: events are still queued and retried, and the webhook returns toactiveon recovery. Onlyactiveanddisabledmay be set through the API;circuit_disabledis server-managed.One of:
active,circuit_disabled,disabled.signing_secret_prefixstringThe first 12 characters of the signing secret, shown for identification.
signing_secret_previous_prefixstringThe first 12 characters of the previous signing secret while a rotation grace window is active, else
null. During the window deliveries are signed with both secrets.May be null.
signing_secret_grace_expires_atstring (date-time)When the current rotation's 24h grace window ends and the previous signing secret stops being honored, or
nullif no rotation is in progress.May be null.
last_delivery_atstring (date-time)May be null.
created_atstring (date-time)
{
"id": "wh_550e8400-e29b-41d4-a716-446655440000",
"name": "Production deliverability hook",
"url": "https://hooks.example.com/anypost",
"events": [
"email.sent"
],
"status": "active",
"signing_secret_prefix": "whsec_AbCdEf",
"signing_secret_previous_prefix": "whsec_XyZwVu",
"signing_secret_grace_expires_at": "string",
"last_delivery_at": "string",
"created_at": "string"
}Responses
| Status | Description |
|---|---|
200 | The updated webhook. |
401 | Missing or invalid credentials. |
404 | Resource not found. |
413 | Request body exceeded the 5 MB gateway limit. Rejected at the transport layer before authentication or validation, so the response uses a shorter error shape than application-layer errors. The connection is closed after the response. |
422 | Request validation failed. |
Delete a webhook
DELETE /v1/webhooks/{id}
Permanently deletes the webhook subscription. In-flight deliveries already enqueued at the time of deletion may still be attempted.
Parameters
idstringin path[required]
Example request
curl https://api.anypost.com/v1/webhooks/wh_550e8400-e29b-41d4-a716-446655440000 \
-X DELETE \
-H "Authorization: Bearer $ANYPOST_API_KEY"Responses
| Status | Description |
|---|---|
204 | Deleted. |
404 | Resource not found. |
Send a test event
POST /v1/webhooks/{id}/test
Synthesize a signed webhook.test event and POST it
once to the webhook's configured URL. Useful for
verifying that your endpoint receives, parses, and
validates Anypost signatures end-to-end before going
live.
Synchronous and one-shot. No retries; no row is
added to your delivery history. The response carries
the outcome (status code, latency, optional response
body preview, optional error). Allowed on any webhook
status — including disabled and circuit_disabled —
so you can verify connectivity before flipping a
webhook back to active.
The request is signed and wrapped in the same outer
envelope (batch_id, timestamp, events[]) as a
real delivery, so your signature-verification path is
exercised end-to-end. The single event inside is a
synthetic webhook.test event with its own shape —
distinct from the id / type / occurred_at /
data shape that real email events use:
{
"batch_id": "wbt_…",
"timestamp": 1730000000,
"events": [
{
"schema_version": 1,
"event_id": "evt_test_…",
"event": "webhook.test",
"occurred_at": "…",
"received_at": "…",
"webhook_id": "wh_…",
"message": "This is a test event from Anypost. No real email was sent."
}
]
}The webhook.test event type is reserved for this
endpoint — it cannot be subscribed to via the
events field on a webhook, and is never emitted by
real email traffic. Use the event_id (evt_test_…)
or the webhook.test type to filter it out of your
analytics on receipt.
Parameters
idstringin path[required]
Example request
curl https://api.anypost.com/v1/webhooks/wh_550e8400-e29b-41d4-a716-446655440000/test \
-X POST \
-H "Authorization: Bearer $ANYPOST_API_KEY"Response body
On success (200), the response body is:
deliveredbooleanTrue only when the customer endpoint returned a 2xx status.
status_codeintegerHTTP status returned by the customer endpoint, or
nullon network failure.May be null.
latency_msintegerWall-clock time from request start to response or error, in milliseconds.
errorstringShort error description if the request never completed (DNS failure, TLS failure, timeout, …).
nullwhen the customer endpoint responded, regardless of status code.May be null.
response_body_previewstringFirst ~1 KiB of the customer endpoint's response body, or
nullif empty.May be null.
{
"delivered": true,
"status_code": 200,
"latency_ms": 142,
"error": null,
"response_body_preview": "{\"ok\":true}"
}Responses
| Status | Description |
|---|---|
200 | The test attempt completed. Inspect delivered and status_code to determine whether the customer endpoint accepted the payload — a 200 response here does NOT imply success. |
401 | Missing or invalid credentials. |
404 | Resource not found. |
Rotate the signing secret
POST /v1/webhooks/{id}/rotate-secret
Generates a new signing secret for the webhook and returns it exactly once in this response — store it securely; subsequent reads return only the prefix.
The previous secret stays valid for a 24-hour grace window.
During the window every delivery is signed with both secrets
(the Anypost-Signature header carries a v1= component for
each), so a verifier that still holds the old secret keeps
matching while you redeploy with the new one.
Rotating again while a grace window is still active returns
409 — wait for the window to end (see
signing_secret_grace_expires_at) before rotating again.
Parameters
idstringin path[required]
Example request
curl https://api.anypost.com/v1/webhooks/wh_550e8400-e29b-41d4-a716-446655440000/rotate-secret \
-X POST \
-H "Authorization: Bearer $ANYPOST_API_KEY"Response body
On success (200), the response body is:
idstringnamestringurlstring (uri)HTTPS endpoint that will receive signed event payloads. Must use the
https://scheme.eventsarray of stringEvent types this webhook subscribes to.
statusstringactivewebhooks receive event deliveries;disabledwebhooks are paused — no deliveries are attempted, but the configuration is preserved so it can be resumed.circuit_disabledis a transient state set automatically after consecutive delivery failures: events are still queued and retried, and the webhook returns toactiveon recovery. Onlyactiveanddisabledmay be set through the API;circuit_disabledis server-managed.One of:
active,circuit_disabled,disabled.signing_secret_prefixstringThe first 12 characters of the signing secret, shown for identification.
signing_secret_previous_prefixstringThe first 12 characters of the previous signing secret while a rotation grace window is active, else
null. During the window deliveries are signed with both secrets.May be null.
signing_secret_grace_expires_atstring (date-time)When the current rotation's 24h grace window ends and the previous signing secret stops being honored, or
nullif no rotation is in progress.May be null.
last_delivery_atstring (date-time)May be null.
created_atstring (date-time)signing_secretstringThe full signing secret. Returned only once at creation. Store it securely; future deliveries will be HMAC-signed with it.
{
"id": "wh_550e8400-e29b-41d4-a716-446655440000",
"name": "Production deliverability hook",
"url": "https://hooks.example.com/anypost",
"events": [
"email.sent"
],
"status": "active",
"signing_secret_prefix": "whsec_AbCdEf",
"signing_secret_previous_prefix": "whsec_XyZwVu",
"signing_secret_grace_expires_at": "string",
"last_delivery_at": "string",
"created_at": "string",
"signing_secret": "whsec_AbCdEfGhIjKlMnOp..."
}Responses
| Status | Description |
|---|---|
200 | Secret rotated. The full new signing secret is returned only in this response. |
401 | Missing or invalid credentials. |
404 | Resource not found. |
409 | The request conflicts with the current state of the resource. |