V3 webhook notifications API
These routes back the dashboard's Notifications › Webhook page
(https://dashboard.blockwyre.com, sandbox https://dashboard-sandbox.blockwyre.com).
The admin.blockwyre.com and admin-sandbox.blockwyre.com hosts are BlockWyre's
staff consoles, not the dashboard. These routes do not accept API keys: an
integration manages its targets through the Webhook Targets API.
Tenant-admin management of outbound webhook endpoints lives under /v3/notifications/webhooks. It is open to every workspace, and it manages the same endpoints as the V2 targets API: an endpoint created or deleted through either shows on the other.
These routes serve the dashboard only: a workspace member signed in to the dashboard, with a role that grants the matching webhook permission (view, create, update, delete, enable, view deliveries, resend). API keys cannot call them: a request signed with an API key, or made with a customer session, is answered 401 with the message unauthorized; a member whose role lacks the permission, 403 with forbidden. To manage targets with an API key, use the Webhook Targets API (/api/v2/webhooks/targets) with a workspace-scoped key.
| Method | Path |
|---|---|
GET | /v3/notifications/webhooks/categories |
GET / POST | /v3/notifications/webhooks |
GET / PUT / DELETE | /v3/notifications/webhooks/{webhook_id} |
POST | /v3/notifications/webhooks/{webhook_id}/enable |
GET | /v3/notifications/webhooks/{webhook_id}/notifications |
GET | /v3/notifications/webhooks/{webhook_id}/notifications/{notification_id} |
POST | /v3/notifications/webhooks/{webhook_id}/notifications/{notification_id}/resend |
POST | /v3/notifications/webhooks/{webhook_id}/notifications/resend |
PUT replaces the endpoint's name, description, URL and categories — send all of them; a description left out is cleared. Wire IDs use TypeID prefixes whk_ (endpoint) and whn_ (notification). Create accepts name, description, categories[], and url (absolute http/https); up to 5 endpoints per workspace, each on its own URL. The URL is stored normalized — scheme and host lower-case, the default port dropped — with its path kept as typed (a trailing slash makes a different URL). The host must be reachable on the public internet: localhost, internal names and private, loopback or link-local addresses are refused, and deliveries follow at most three redirects, never to a private address. Categories are accounts, compliance, banking, crypto, wallets and trade; an endpoint receives every event type in the categories it selects (GET /v3/notifications/webhooks/categories lists them).
Deliveries keep the payload, headers and signature described in the webhook guide. Each event reaches each endpoint under one webhookId (also sent as X-BlockWyre-Webhook-ID): it stays the same across retries and resends, and in the rare case an event is delivered to the same endpoint twice it arrives with the same id both times — deduplicate on it. A delivery is tried up to 6 times — the first attempt and 5 retries — until the endpoint answers 2xx or 3xx, retrying with exponential backoff: about 7 to 14 minutes from the first attempt to the sixth. Each attempt times out after 30 seconds. When one delivery fails all six attempts the endpoint is disabled, and POST …/enable turns it back on. The delivery log keeps 60 days. Inbound provider webhooks stay on /v3/webhooks/{provider}/… and are unrelated to this surface.
A resend delivers a logged delivery again: the same payload, under the same notification id and webhookId, as a new attempt on it. POST …/notifications/{notification_id}/resend resends one and answers 204. POST …/notifications/resend resends, in one action, every failed delivery of the endpoint written in a range, newest first, spread out (about ten a second) so an endpoint just back from an outage is not flooded. Its body is created_at_gte (required) and created_at_lte (optional; absent is now), both RFC 3339 and inclusive, for example {"created_at_gte": "2026-10-01T00:00:00Z", "created_at_lte": "2026-10-02T00:00:00Z"}. It resends up to 500 per request and answers 200 with {"resent": 12, "has_more": false}: when has_more is true, send the same request again for the next batch. Both refuse a disabled endpoint: re-enable it first.
Errors come in three kinds:
422— a required field is missing. A create orPUTwithoutname,urlorcategories, or a delivery-log searchqshorter than 3 characters, answers422with the messageValidation failedand one entry per field indetails(for example{"field": "body.url", "message": "url is a required field"}).400— the request does not parse. A malformed id in the path (whk_…,whn_…), or a category or status the API does not know, answers400with the messagefailed to parse request.400— the form parses but breaks a rule, with a resource-specific code:webhook_invalid_name,webhook_invalid_description,webhook_invalid_url(not an absolutehttp/httpsURL),webhook_url_not_public(a well-formed URL whose host is not reachable on the public internet —localhost, an internal name, a private, loopback or link-local address),webhook_no_categories(an endpoint must listen to at least one category),notification_invalid_category(an empty category in the list) andwebhook_invalid_resend_range(a bulk resend withoutcreated_at_gte, or withcreated_at_ltebefore it).
401 unauthorized: no dashboard session, or an API key or customer session. 403 forbidden: the member's role lacks the permission. 404 codes: webhook_not_found and notification_message_not_found. 409 codes: webhook_limit_exceeded, webhook_duplicate_url, webhook_disabled (a resend, single or bulk, on a disabled endpoint) and notification_message_in_flight (resend while the delivery is still in progress).