Webhook subscriptions (hooks)
A subscription tells anymail to send a signed POST to a URL when one kind of event happens in the workspace. The endpoints follow the REST Hooks contract that Zapier uses for instant triggers, and any client can call them.
For the delivery format, the signature and the retries, see Webhooks.
| Method | Path | Action |
|---|---|---|
POST |
/api/v1/hooks |
Subscribe a URL to an event |
GET |
/api/v1/hooks |
List subscriptions |
DELETE |
/api/v1/hooks/{id} |
Unsubscribe |
Events#
event |
Fires when |
|---|---|
new_reply |
A prospect replied. It fires before the reply is classified. |
positive_reply |
The classifier marks a reply as interested. It fires once the classification is saved. |
bounce |
A message to a prospect bounced. |
unsubscribe |
A prospect used the one-click unsubscribe link. |
Subscriptions have these four keys only. There is no subscription for sent, clicked, opened or delivered. The single workspace webhook in Settings, Integrations, Webhooks delivers sent, replied, bounced, unsubscribed and clicked.
Subscribe#
The endpoint honours an Idempotency-Key. The call is also idempotent by itself. The same pair of event and target_url returns the existing subscription with 200.
Body (JSON, up to 4 KiB)
| Field | Type | Required | Notes |
|---|---|---|---|
event |
string | Yes | One of new_reply, positive_reply, bounce, unsubscribe. |
target_url |
string | Yes | An https:// URL on a public host. Local and private addresses are rejected after a DNS lookup, and so is a URL with user:pass. |
curl -X POST "https://app.anymail.watch/api/v1/hooks" \
-H "Authorization: Bearer $ANYMAIL_TOKEN" \
-H "Content-Type: application/json" \
-d '{"event":"new_reply","target_url":"https://hooks.example.com/anymail"}'
Response 201 for a new subscription, 200 for an existing one
{
"id": "e6f7a8b9-0000-4000-8000-000000000001",
"event": "new_reply",
"target_url": "https://hooks.example.com/anymail"
}
The first subscription also creates the signing secret of the workspace. Read it in Settings, Integrations, Webhooks. The API does not return it.
Errors
| Status | Message |
|---|---|
400 |
The body errors listed on Errors. |
400 |
`event` must be one of: new_reply, positive_reply, bounce, unsubscribe. |
400 |
`target_url` must be a non-empty string. |
400 |
The reason the URL was rejected, for example The webhook URL must start with https://. |
500 |
A database failure. |
List subscriptions#
| Parameter | Type | Notes |
|---|---|---|
limit |
integer | 1 to 100. Default 50. |
cursor |
string | See Pagination. |
curl "https://app.anymail.watch/api/v1/hooks" \
-H "Authorization: Bearer $ANYMAIL_TOKEN"
Response 200
{
"data": [
{
"id": "e6f7a8b9-0000-4000-8000-000000000001",
"event": "new_reply",
"target_url": "https://hooks.example.com/anymail",
"created_at": "2026-09-01T00:00:00.000+00:00"
}
],
"next_cursor": null
}
The list holds no secrets. The workspace webhook URL and its signing secret belong to the app. The API neither returns nor changes them.
Unsubscribe#
DELETE /api/v1/hooks/{id} removes a subscription. The response is 204 with no body. Deliveries that are already queued for the URL are not cancelled.
curl -X DELETE "https://app.anymail.watch/api/v1/hooks/e6f7a8b9-0000-4000-8000-000000000001" \
-H "Authorization: Bearer $ANYMAIL_TOKEN"
Errors: 404 with {"error":"No subscription with this id."} for an unknown id, an id from another workspace and an id that is not a UUID.