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.confirmedtransaction.failed
transaction.rejectedtransaction.cancelled
transaction.awaiting_approvalaccount.provisioned
account.provisioning_failedaccount.status_changed
customer.status_changedapproval.requested
approval.grantedapproval.rejected
approval.expiredwebhook.test

Each destination is signed under one scheme, fixed at registration:

signatureSchemeHow you verify
HMAC_SHA256 (the default)With a shared secret the platform generates and returns once, in the registration response
JWS_ES256Against 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-9c0e5d2f7b63

The 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

  1. Read the raw request body bytes before parsing the JSON.
  2. Compute HMAC-SHA256 with the secret from registration over the Webhook-Timestamp value, a full stop, and the raw body. Base64-encode the result. Webhooks has verification code in Python and Node.js.
  3. Compare your result with each v1 value in Webhook-Signature in constant time. Reject the request if none matches.
  4. During a secret rotation, both the new and the previous signature are sent on every delivery until previousSecretExpiresAt. Accept the delivery if either verifies.
  5. Reject a delivery whose Webhook-Timestamp is 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.0

Illustrative 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 outcomeWhenWhat to do
400 on registrationURL not https://; host resolves to a private, loopback or link-local address; unknown signatureScheme; no eventTypes; missing Idempotency-KeyFix the request
409 on registrationYour organisation already registered this URLUpdate the existing destination with PATCH /api/webhooks/{id}
409 on testThe destination is INACTIVE, or a request with the same Idempotency-Key is still runningActivate the destination, or wait and retry with the same key
Delivery RETRY_PENDINGAn attempt failed and is waiting for its next attempt. failureClass is UNREACHABLE, TIMEOUT or REJECTEDFix your endpoint before nextRetryAt
Delivery PARKEDThe attempt budget ran outFix your endpoint, then replay with POST /api/webhook-events/{id}/redeliver
Destination SUSPENDEDThe platform suspended it after repeated failuresA 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 twiceDeliveries can repeatDeduplicate 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

Webhooks code recipes.

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.


Did this page help you?