Skip to Content
WebhooksManaging endpoints

Managing endpoints

Provision webhook endpoints from deploy scripts and infrastructure-as-code. For the receiver side, see Webhooks.

Every route here, reads included, needs the webhooks:manage scope. Without it: 403 insufficient_scope. Mint a new key; scopes cannot be added to an existing one.

An endpoint is a standing feed of every address you mail, pushed to a URL the caller chooses. A base key can read contacts but has to keep asking; a key that adds an endpoint gets them pushed forever, silently. The delivery log names every recipient, so reads are gated too.

The Webhook endpoint object

{ "id": "whe_2v0dz1k9x4m7", "object": "webhook_endpoint", "url": "https://yourapp.com/webhooks/day3", "description": "production", "events": ["email.bounced", "email.complained", "suppression.created"], "status": "active", "consecutive_failures": 0, "last_success_at": "2026-08-21T09:00:03.000Z", "last_failure_at": null, "last_error": null, "created_at": "2026-08-15T09:00:00.000Z" }

Endpoints are never auto-disabled. Monitor consecutive_failures, last_failure_at and last_error instead.

The signing secret is not here. See below.

List endpoints

GET/v1/webhooksneeds webhooks:manage

Create an endpoint

POST/v1/webhooksneeds webhooks:manage
FieldTypeRequiredNotes
urlstringYesPublic https, port 443 or 8443.
eventsstring[]YesWhich events to send.
descriptionstringNoFor your own bookkeeping.
curl -X POST https://go.day3.app/api/v1/webhooks \ -H "Authorization: Bearer $DAY3_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "url": "https://yourapp.com/webhooks/day3", "description": "production", "events": ["email.bounced", "email.complained", "suppression.created"] }'

This response is the only time the API returns the signing secret. Capture it now.

{ "id": "whe_2v0dz1k9x4m7", "object": "webhook_endpoint", "url": "https://yourapp.com/webhooks/day3", "secret": "whsec_...", "events": ["email.bounced", "email.complained", "suppression.created"], "status": "active" }

Retrieve an endpoint

GET/v1/webhooks/{webhook_id}needs webhooks:manage

Update an endpoint

PATCH/v1/webhooks/{webhook_id}needs webhooks:manage

Accepts url, description, events and status. events replaces the list; it does not append.

Delete an endpoint

DELETE/v1/webhooks/{webhook_id}needs webhooks:manage

Removes the endpoint and its delivery history.

Delivery log

GET/v1/webhooks/{webhook_id}/deliveriesneeds webhooks:manage

Newest first, cursor-paginated. Filter with ?status=. Rows are kept for 30 days.

{ "id": "whd_9x4v0dz1k2m7", "object": "webhook_delivery", "endpoint_id": "whe_2v0dz1k9x4m7", "event_id": "evt_2k4h9x4v0dz1", "event_type": "email.bounced", "status": "failed", "attempt": 3, "response_status": 502, "error": "Bad Gateway", "duration_ms": 10012, "next_attempt_at": "2026-08-21T09:40:00.000Z", "delivered_at": null, "created_at": "2026-08-21T09:00:00.000Z" }

The signed payload is not included. Over the API it would be a paginated dump of every address you mail. The app’s delivery log shows it.

The signing secret

Returned once, by POST, and never again at any scope. Reveal and rotate live in the app behind a session, because a key that could read the secret could forge our events into your receiver.

Lost it? Rotate in the app under API keys, then Webhooks, following the zero-downtime order.

Last updated on