ChangelogStatus
Docs/API reference

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 Webhook
has_moreboolean
next_cursorstring

May 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

StatusDescription
200Paginated list of webhooks.
401Missing 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:

idstring
namestring
urlstring (uri)

HTTPS endpoint that will receive signed event payloads. Must use the https:// scheme.

eventsarray of string

Event types this webhook subscribes to.

statusstring

active webhooks receive event deliveries; disabled webhooks are paused — no deliveries are attempted, but the configuration is preserved so it can be resumed. circuit_disabled is a transient state set automatically after consecutive delivery failures: events are still queued and retried, and the webhook returns to active on recovery. Only active and disabled may be set through the API; circuit_disabled is server-managed.

One of: active, circuit_disabled, disabled.

signing_secret_prefixstring

The first 12 characters of the signing secret, shown for identification.

signing_secret_previous_prefixstring

The 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 null if no rotation is in progress.

May be null.

last_delivery_atstring (date-time)

May be null.

created_atstring (date-time)
signing_secretstring

The 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

StatusDescription
201Webhook created. The full signing secret is returned only in this response.
401Missing or invalid credentials.
413Request 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.
422Request 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:

idstring
namestring
urlstring (uri)

HTTPS endpoint that will receive signed event payloads. Must use the https:// scheme.

eventsarray of string

Event types this webhook subscribes to.

statusstring

active webhooks receive event deliveries; disabled webhooks are paused — no deliveries are attempted, but the configuration is preserved so it can be resumed. circuit_disabled is a transient state set automatically after consecutive delivery failures: events are still queued and retried, and the webhook returns to active on recovery. Only active and disabled may be set through the API; circuit_disabled is server-managed.

One of: active, circuit_disabled, disabled.

signing_secret_prefixstring

The first 12 characters of the signing secret, shown for identification.

signing_secret_previous_prefixstring

The 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 null if 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

StatusDescription
200The webhook.
404Resource 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:

idstring
namestring
urlstring (uri)

HTTPS endpoint that will receive signed event payloads. Must use the https:// scheme.

eventsarray of string

Event types this webhook subscribes to.

statusstring

active webhooks receive event deliveries; disabled webhooks are paused — no deliveries are attempted, but the configuration is preserved so it can be resumed. circuit_disabled is a transient state set automatically after consecutive delivery failures: events are still queued and retried, and the webhook returns to active on recovery. Only active and disabled may be set through the API; circuit_disabled is server-managed.

One of: active, circuit_disabled, disabled.

signing_secret_prefixstring

The first 12 characters of the signing secret, shown for identification.

signing_secret_previous_prefixstring

The 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 null if 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

StatusDescription
200The updated webhook.
401Missing or invalid credentials.
404Resource not found.
413Request 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.
422Request 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

StatusDescription
204Deleted.
404Resource 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:

deliveredboolean

True only when the customer endpoint returned a 2xx status.

status_codeinteger

HTTP status returned by the customer endpoint, or null on network failure.

May be null.

latency_msinteger

Wall-clock time from request start to response or error, in milliseconds.

errorstring

Short error description if the request never completed (DNS failure, TLS failure, timeout, …). null when the customer endpoint responded, regardless of status code.

May be null.

response_body_previewstring

First ~1 KiB of the customer endpoint's response body, or null if empty.

May be null.

{
  "delivered": true,
  "status_code": 200,
  "latency_ms": 142,
  "error": null,
  "response_body_preview": "{\"ok\":true}"
}

Responses

StatusDescription
200The 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.
401Missing or invalid credentials.
404Resource 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:

idstring
namestring
urlstring (uri)

HTTPS endpoint that will receive signed event payloads. Must use the https:// scheme.

eventsarray of string

Event types this webhook subscribes to.

statusstring

active webhooks receive event deliveries; disabled webhooks are paused — no deliveries are attempted, but the configuration is preserved so it can be resumed. circuit_disabled is a transient state set automatically after consecutive delivery failures: events are still queued and retried, and the webhook returns to active on recovery. Only active and disabled may be set through the API; circuit_disabled is server-managed.

One of: active, circuit_disabled, disabled.

signing_secret_prefixstring

The first 12 characters of the signing secret, shown for identification.

signing_secret_previous_prefixstring

The 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 null if no rotation is in progress.

May be null.

last_delivery_atstring (date-time)

May be null.

created_atstring (date-time)
signing_secretstring

The 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

StatusDescription
200Secret rotated. The full new signing secret is returned only in this response.
401Missing or invalid credentials.
404Resource not found.
409The request conflicts with the current state of the resource.