Registering a webhook endpoint
Event types
| Event | Trigger |
|---|---|
transaction.created | New transaction submitted |
transaction.status_changed | Transaction state transition |
transaction.completed | Transaction confirmed on-chain |
transaction.failed | Transaction reverted or broadcast failure |
transaction.approval_required | Policy requires manual approval |
deposit.detected | Inbound transfer detected on a registered wallet |
deposit.confirmed | Inbound transfer confirmed with sufficient depth |
approval.decision | An approval or rejection decision was made |
* wildcards in event subscriptions — transaction.* subscribes to all transaction events.
Webhook payload format
Every delivery includes these headers:| Header | Description |
|---|---|
X-Webhook-ID | Unique delivery ID |
X-Webhook-Timestamp | Unix timestamp of the delivery attempt |
X-Webhook-Signature | HMAC-SHA256 signature for verification |
Content-Type | application/json |
HMAC-SHA256 verification
Always verify theX-Webhook-Signature header to confirm the payload came from QC Custody and hasn’t been tampered with.
The signature is computed as:
Retry behavior
Failed deliveries are retried with exponential backoff:| Attempt | Delay |
|---|---|
| 1 | Immediate |
| 2 | 30 seconds |
| 3 | 2 minutes |
| 4 | 10 minutes |
| 5 | 1 hour |
| 6 | 6 hours |
What counts as a failure?
- HTTP response status outside
200–299 - Connection timeout (10 seconds)
- DNS resolution failure
- TLS handshake failure
Managing endpoints
Best practices
Respond quickly
Return
200 OK immediately and process the event asynchronously. Slow responses trigger retries.Handle duplicates
Use the
id field to deduplicate — retries may deliver the same event multiple times.Verify signatures
Always validate HMAC-SHA256 before processing. Reject unsigned or invalid deliveries.
Monitor dead letters
Set up alerting on dead-letter events to catch configuration issues early.