Receive your first webhook
For integration engineers. Register an HTTPS destination, receive a signed test event, and read the event for the transfer you made in step 5.
Who this is for
Engineers who want their systems to learn operation outcomes without polling. By the end of this step your endpoint has received a signed test delivery, and you have read the transaction.confirmed event for the transfer in 5. Make a transfer.
How webhooks work
You register a destination: an HTTPS URL and the event types it subscribes to. When a subscribed event occurs in your organisation, the platform sends it to the destination as a signed POST. Every event and every delivery attempt is kept in delivery history, where you can read it and ask for it to be delivered again.
| Event types you can subscribe to | |
|---|---|
transaction.confirmed | transaction.failed |
transaction.rejected | transaction.cancelled |
transaction.awaiting_approval | account.provisioned |
account.provisioning_failed | account.status_changed |
customer.status_changed | approval.requested |
approval.granted | approval.rejected |
approval.expired | webhook.test |
Each destination is signed under one scheme, fixed at registration:
signatureScheme | How you verify |
|---|---|
HMAC_SHA256 (the default) | With a shared secret the platform generates and returns once, in the registration response |
JWS_ES256 | Against the platform's published public key. No shared secret. Obtain the trusted issuer and key-set URL for your environment before choosing this scheme; signing-key rotation is not currently documented as an available client operation. |
Treat webhooks as a notification and the API as the record. Your endpoint can receive the same event more than once. Deduplicate on the event id. Before you post to your book of record, you can read the operation with GET /api/transactions/{transactionId} to confirm its state.
Worked example
Register a destination
POST /api/webhooks HTTP/1.1
Host: api.qfp.quantnetwork.net
Authorization: Bearer eyJraWQiOiJ...
API-Version: 1.0.0
Content-Type: application/json
Idempotency-Key: 0e5c9a72-4b1d-4f36-8a90-3d7e2c6b1f58
{
"url": "https://bank.example.com/quant/events",
"eventTypes": [
"transaction.confirmed",
"transaction.failed"
],
"signatureScheme": "HMAC_SHA256"
}HTTP/1.1 201 Created
Content-Type: application/json
Location: /api/webhooks/9f2a1c44-6e3b-4f8a-9d21-7c5e0b8a4d13
{
"id": "9f2a1c44-6e3b-4f8a-9d21-7c5e0b8a4d13",
"url": "https://bank.example.com/quant/events",
"status": "ACTIVE",
"signatureScheme": "HMAC_SHA256",
"eventTypes": [
"transaction.confirmed",
"transaction.failed"
],
"secret": "8Kd2ZqL0vXmB7nR4tYcJ1sWfA6gH3pE9uQ5xN0iVrTk",
"createdAt": "2026-08-28 09:15:11.584",
"updatedAt": "2026-08-28 09:15:11.584"
}Store the secret now. No later read returns it. If you lose it, rotate it with POST /api/webhooks/{id}/secret/rotate.
The URL must use https:// and its host must resolve to a publicly routable address. Redirects are not followed at delivery time. A signed test delivery with the webhook.test event type is queued automatically on registration.
Send a test delivery on demand
POST /api/webhooks/9f2a1c44-6e3b-4f8a-9d21-7c5e0b8a4d13/test HTTP/1.1
Host: api.qfp.quantnetwork.net
Authorization: Bearer eyJraWQiOiJ...
API-Version: 1.0.0
Idempotency-Key: 6a3f8c15-2e7b-4d90-b4a1-9c0e5d2f7b63The attempt is made before the response returns, so the response carries the outcome:
HTTP/1.1 200 OK
Content-Type: application/json
{
"eventId": "7c31d6b0-4f2e-4a8c-9b15-8d0a3e6c2f47",
"webhookId": "9f2a1c44-6e3b-4f8a-9d21-7c5e0b8a4d13",
"status": "DELIVERED",
"attempts": 1
}If your endpoint could not be reached:
{
"eventId": "7c31d6b0-4f2e-4a8c-9b15-8d0a3e6c2f47",
"webhookId": "9f2a1c44-6e3b-4f8a-9d21-7c5e0b8a4d13",
"status": "RETRY_PENDING",
"attempts": 1,
"failureClass": "UNREACHABLE",
"failureReason": "Connection refused",
"nextRetryAt": "2026-09-07 10:15:41.221"
}The test is signed under the destination's scheme with the same headers as any delivery, so verification that works for the test works for real events.
What your endpoint receives
The body of a delivery is the event: id, type, occurredAt, resource and data, plus correlationId and idempotencyKey where the originating request had them. For the transfer in step 5:
Illustrative example built from the schema.
POST /quant/events HTTP/1.1
Host: bank.example.com
Content-Type: application/json
Content-Digest: sha-256=:<base64 SHA-256 of the body>:
Webhook-Event-Id: 0198c2f7-aadd-8fcc-e0a1-3c4d5e6f7a8b
Webhook-Timestamp: 1786445162
Webhook-Delivery-Attempt: 1
Webhook-Signature: v1,<base64 signature>
{
"id": "0198c2f7-aadd-8fcc-e0a1-3c4d5e6f7a8b",
"type": "transaction.confirmed",
"occurredAt": "2026-08-06 10:06:02.884",
"idempotencyKey": "a61e3b27-0d94-4c58-b7f2-6e8d1c0a4b39",
"resource": {
"type": "transaction",
"id": "5b2e8d41-9c07-4a63-8e15-7f3a0d6c2b98"
},
"data": {}
}data varies by event type and is shown empty here. Treat data as event-type-specific and validate the fields your handler uses. No complete per-event payload schema is published; retain transaction reads as recovery. The delivery headers are described on Webhooks. The notification integration guide defines the delivery headers and signing procedure used here. Verify a signed test delivery against your assigned environment before processing business events.
Verify the signature
- Read the raw request body bytes before parsing the JSON.
- Compute HMAC-SHA256 with the secret from registration over the
Webhook-Timestampvalue, a full stop, and the raw body. Base64-encode the result. Webhooks has verification code in Python and Node.js. - Compare your result with each
v1value inWebhook-Signaturein constant time. Reject the request if none matches. - During a secret rotation, both the new and the previous signature are sent on every delivery until
previousSecretExpiresAt. Accept the delivery if either verifies. - Reject a delivery whose
Webhook-Timestampis more than 300 seconds from your clock, as specified by the notification integration guide.
Respond with a 2xx after durable acceptance. A non-2xx response is recorded as a REJECTED attempt; the documented read timeout is five seconds. Temporary network failures, timeouts, 5xx, 408 and 429 are retried; other 4xx park after two attempts. See Webhooks for the retry schedule.
Read the event from delivery history
GET /api/webhook-events/0198c2f7-aadd-8fcc-e0a1-3c4d5e6f7a8b HTTP/1.1
Host: api.qfp.quantnetwork.net
Authorization: Bearer eyJraWQiOiJ...
API-Version: 1.0.0Illustrative example built from the schema.
{
"id": "0198c2f7-aadd-8fcc-e0a1-3c4d5e6f7a8b",
"type": "transaction.confirmed",
"occurredAt": "2026-08-06 10:06:02.884",
"idempotencyKey": "a61e3b27-0d94-4c58-b7f2-6e8d1c0a4b39",
"resource": {
"type": "transaction",
"id": "5b2e8d41-9c07-4a63-8e15-7f3a0d6c2b98"
},
"data": {
"operation": "TRANSFER",
"amount": "250.00"
},
"deliveries": [
{
"id": "7d1b4e02-3a95-4c17-8f6d-2b0e9a5c1d84",
"webhookId": "9f2a1c44-6e3b-4f8a-9d21-7c5e0b8a4d13",
"generation": 0,
"status": "DELIVERED",
"attempts": 1,
"lastAttemptAt": "2026-08-06 10:06:03.371",
"createdAt": "2026-08-06 10:06:02.912"
}
]
}idempotencyKey is the key you sent on the transfer, so you can match the event to your own request.
View in API Reference
Errors and edge cases
| Status or outcome | When | What to do |
|---|---|---|
400 on registration | URL not https://; host resolves to a private, loopback or link-local address; unknown signatureScheme; no eventTypes; missing Idempotency-Key | Fix the request |
409 on registration | Your organisation already registered this URL | Update the existing destination with PATCH /api/webhooks/{id} |
409 on test | The destination is INACTIVE, or a request with the same Idempotency-Key is still running | Activate the destination, or wait and retry with the same key |
Delivery RETRY_PENDING | An attempt failed and is waiting for its next attempt. failureClass is UNREACHABLE, TIMEOUT or REJECTED | Fix your endpoint before nextRetryAt |
Delivery PARKED | The attempt budget ran out | Fix your endpoint, then replay with POST /api/webhook-events/{id}/redeliver |
Destination SUSPENDED | The platform suspended it after repeated failures | A successful test delivery resets the failure count. After fixing the receiver, PATCH the registration to ACTIVE with a persisted idempotency key. This clears suspension and the failure count; a successful test alone is not the explicit resume action. |
| Same event received twice | Deliveries can repeat | Deduplicate on the event id |
A refused URL:
HTTP/1.1 400 Bad Request
Content-Type: application/json
{
"timestamp": "2026-08-28 09:15:12.411",
"status": 400,
"error": "Bad Request",
"message": "URL must use https, not 'http'"
}You have completed the Quickstart. To build on it with your own environment, request access.
Follow a Recipe
Step through the code in JavaScript or Python, inspect illustrative responses, and open the connected endpoint schemas.
API Reference
Open an endpoint for request fields, response examples and the interactive API console.
Related pages
Implementation resources
Use Implement a backend client for request handling, durable idempotency and outcome tracking. The developer integration coverage map links every public operation to its API Reference and contract-derived code recipe. Replace example identifiers with values from your assigned environment.
Updated 4 days ago

