POST request with the event payload to your endpoint.
There is no “subscribe to everything” option. Every endpoint must list the specific event types it wants — this keeps your integration explicit about what it depends on.
Registering a webhook endpoint
Request
string
required
The HTTPS URL we’ll deliver events to. Must use
https:// — plain http:// endpoints are rejected. The destination must also resolve to a public IP address; private, loopback, and link-local addresses are refused when we attempt delivery.string
An optional label for the endpoint (1–100 characters), useful when you have more than one.
string[]
required
One to 50 event type strings to subscribe to. See Event types below for the full list. Requests referencing an unrecognized or ineligible event type are rejected with a 400.
All mutating requests (create, update, delete, rotate secret) require an
Idempotency-Key header, consistent with the rest of the API. Retried requests with the same key return the original result rather than creating a duplicate.Response
string
Unique identifier for the endpoint, e.g.
whk_2sB....string
string
string
string
One of
active, disabled, deleted.string[]
string
The secret used to sign deliveries to this endpoint, e.g.
whsec_....string
string
Response
Event types
Only the event types below can be subscribed to today. Requesting any other event type returns a400.
More event types are planned. If there’s a specific one you need, let us know.
Delivery format
Every delivery is an HTTPPOST with a JSON body shaped like this:
Verifying signatures
Each delivery is signed with the endpoint’s signing secret so you can verify it genuinely came from us and wasn’t tampered with in transit. The signature header looks like:- Take the raw, unparsed request body bytes — don’t re-serialize a parsed JSON object, since re-serialization can change byte-for-byte formatting and break the signature check. 2. Build the signed content string:
{Merchant-Webhook-Id}.{Merchant-Webhook-Timestamp}.{raw body}3. ComputeHMAC-SHA256of that string using your endpoint’s signing secret, hex-encoded. 4. Compare it (using a constant-time comparison) against the value afterv1=in theMerchant-Webhook-Signatureheader. 5. Reject the request if the timestamp is more than 5 minutes old, to guard against replay attacks.
Worked example
Worked example
Given: - Secret: Which produces:Use this to sanity-check your own implementation before going live.
whsec_test_secret - Merchant-Webhook-Id: whkdel_test123 - Merchant-Webhook-Timestamp: 1700000000 - Raw body: {"id":"whkdel_test123","type":"payments.PaymentCreated"}The signed content is:Retries and delivery status
Your endpoint should respond with a
2xx status code as quickly as possible. Do your event processing asynchronously and return a 200 immediately — we treat any non-2xx response, a timeout, or a connection error as a failed attempt.- Timeout: we wait up to 10 seconds for a response. - Retries: up to 5 attempts total, with a fixed 60-second delay between attempts (so the full retry window is a few minutes, not hours or days). - After the final attempt fails, the delivery is marked
exhaustedand is not retried automatically. We don’t currently auto-disable an endpoint after repeated failures — check your delivery history periodically if you want to catch a persistently failing endpoint.
Viewing delivery history
status, limit, and cursor query parameters. Each delivery includes its attempt history (attemptNumber, attemptedAt, responseStatusCode, durationMs, errorMessage) — useful for debugging why a delivery failed.
Manually redelivering
redeliveredFromId) — it doesn’t retry or mutate the original.
Managing endpoints
Updating an endpoint lets you change its
url, name, eventTypes, or status (active or disabled — disabling pauses deliveries without deleting the endpoint). There’s no way to fetch a single endpoint by ID directly; use the list endpoint and filter client-side.