List webhook deliveries
GET /webhooks/deliveries - List partner-scoped webhook delivery records for diagnostics.
0Gate is the primary public integration path for hosted payment, ramp, and swap experiences. Keep secret-key operations on your server and hand only browser-safe values to the widget.
This is the hub for webhook observability. It also carries the full event catalogue, the signature recipe, and the delivery and retry semantics that the replay and test endpoints refer back to.
Endpoint
| Field | Value |
|---|---|
| Method | GET |
| Path | /v1/webhooks/deliveries |
| Area | Webhooks |
| Operation id | listPartnerWebhookDeliveries |
| Auth boundary | Secret key from your server. |
Use it for
Read your own outbound webhook delivery log, most-recent first: which events delivered, which are still retrying, and which dead-lettered and why. The log is scoped to your partner — you never see another partner's deliveries.
Use it to diagnose a missed event, to confirm an endpoint outage has cleared, and to find the delivery id you then pass to replay. The payload_raw body is never returned here — the log is about delivery health, not payload inspection.
Server-side, secret key only
This endpoint requires an sk_* secret key sent from your server. pk_* publishable keys are browser-safe and only bootstrap the embed; they cannot read the delivery log.
Request
Query parameters
| Field | Required | Type | Use it for |
|---|---|---|---|
status | No | enum | Filter to one delivery state: pending, in_flight, succeeded, or dead_lettered. |
limit | No | integer | Page size, 1–200 (default 50). |
skip | No | integer | Number of records to skip for pagination, ≥ 0 (default 0). |
Response
A list envelope whose data is an array of WebhookDelivery records, most-recent first.
| Field | Type | Use it for |
|---|---|---|
object | string | Always list. |
data | array | Array of WebhookDelivery records (see below). |
has_more | boolean | true when more records exist past this page; advance skip. |
url | string | The path this list was read from. |
The WebhookDelivery record
| Field | Type | Use it for |
|---|---|---|
object | string | Always webhook_delivery. |
id | string | Delivery id. Pass to replay. |
event_id | string | Stable event id, also sent as the X-0bit-Event-Id header. Your cross-system dedupe key. |
event_type | string | The event that was delivered, e.g. gate_session.completed. See the event catalogue below. |
target_url | string | The webhook_url the attempt was POSTed to. |
status | enum | pending, in_flight, succeeded, or dead_lettered. |
attempts | integer | Number of delivery attempts so far (max 5 before dead-letter). |
last_response_status | integer | null | HTTP status your endpoint last returned, or null if none yet. |
last_error | string | null | Short reason the last attempt failed, or null on success. |
next_attempt_at | string | null | When the worker will retry next (ISO 8601), or null when terminal. |
delivered_at | string | null | When a 2xx was received, or null if not yet delivered. |
created_at | string | null | When the delivery record was created (ISO 8601). |
updated_at | string | null | When the record last changed (ISO 8601). |
Event catalogue
Every delivery carries one event_type. These are the partner-observable events:
event_type | Fired when… | Payload data |
|---|---|---|
gate_session.created | A session is created. | The full session object. |
gate_session.processing | A buy or sell linked to the session moves into processing. | The full session object. |
gate_session.completed | A buy or sell linked to the session succeeds. | The session plus a tx_refid to join to your order record. |
gate_session.failed | A buy or sell linked to the session fails. | The full session object. |
gate_session.expired | A session is first read past its expires_at. | The full session object. |
gate_session.cancelled | A session is cancelled. | The full session object. |
gate_session.kyc_package_accepted | A KYC-trusted partner's submitted KYC package is accepted (audit trail). | A redacted acceptance record — correlation ids and a timestamp, never raw KYC PII. |
kyc.required | A session is blocked pending KYC; the user completes it in the hosted flow. | gate_session_id, kyc_status, and your user_reference. |
customer.created | A customer record is created under your partner account. | The customer object. |
customer.updated | A customer record's details change. | The customer object. |
customer.deleted | A customer record is deleted. | The customer object (or its id reference). |
partner.quota.warning | Your account is approaching a usage quota. | Quota usage counters (operational notification). |
partner.quota.exhausted | A usage quota is exhausted and requests are being throttled or refused. | Quota usage counters (operational notification). |
Dedupe on the event id
Delivery is at-least-once, so the same event can arrive more than once. Use the event id (the envelope id, mirrored in the X-0bit-Event-Id header) as your idempotency key and ignore a repeat you have already processed.
Signature and headers
Every webhook POST to your webhook_url carries a signature plus three companion headers:
| Header | Value |
|---|---|
Gate-Signature | t=<unix-ts>,v1=<hex> — the timestamped HMAC (see recipe below). |
X-0bit-Timestamp | <unix-ts> — the same value as t= in Gate-Signature. |
X-0bit-Event-Id | <uuid> — the event id; your dedupe key. |
X-0bit-Event-Type | The event_type, e.g. gate_session.completed. |
Verify the signature
The v1 value is an HMAC-SHA256 (hex) computed over the string formed by the timestamp, a literal ., then the raw request body — sign the bytes you received, before any JSON re-serialization. Reject anything older than the 300-second skew tolerance, and compare with a constant-time equality check.
const crypto = require('crypto');
function verify(rawBody, header, secret, skewSeconds = 300) {
const parts = Object.fromEntries(
header.split(',').map((p) => p.split('=')),
);
const t = parseInt(parts.t, 10);
if (Math.abs(Date.now() / 1000 - t) > skewSeconds) return false;
const expected = crypto
.createHmac('sha256', secret)
.update(`${t}.${rawBody}`)
.digest('hex');
return crypto.timingSafeEqual(
Buffer.from(expected, 'hex'),
Buffer.from(parts.v1, 'hex'),
);
}Delivery and retry
- At-least-once. Acknowledge with any
2xx; dedupe on the event id. - 10-second timeout per attempt. A
408or429from your endpoint is treated as transient and retried. - Retry schedule:
1m,5m,30m,2h. After 5 attempts with no2xx, the delivery is dead-lettered. - A dead-lettered delivery surfaces here with
status: dead_letteredand alast_error. Fix your handler, then replay it.
Examples
curl -G https://gate-api.0bit.app/v1/webhooks/deliveries \
-H "Authorization: Bearer sk_test_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
--data-urlencode "status=dead_lettered" \
--data-urlencode "limit=20"{
"object": "list",
"data": [
{
"object": "webhook_delivery",
"id": "whd_test_67a1f3b9e4b0c10001234567",
"event_id": "f47ac10b-58cc-4372-a567-0e02b2c3d479",
"event_type": "gate_session.completed",
"target_url": "https://partner.example/webhooks/0bit",
"status": "succeeded",
"attempts": 1,
"last_response_status": 200,
"last_error": null,
"next_attempt_at": null,
"delivered_at": "2026-01-01T00:00:02Z",
"created_at": "2026-01-01T00:00:01Z",
"updated_at": "2026-01-01T00:00:02Z"
}
],
"has_more": false,
"url": "/v1/webhooks/deliveries"
}{
"object": "webhook_delivery",
"id": "whd_test_67a1f3b9e4b0c10009999999",
"event_id": "9b2d6c1a-1111-2222-3333-444455556666",
"event_type": "gate_session.completed",
"target_url": "https://partner.example/webhooks/0bit",
"status": "dead_lettered",
"attempts": 5,
"last_response_status": 500,
"last_error": "HTTP 500: (empty body)",
"next_attempt_at": null,
"delivered_at": null,
"created_at": "2026-01-01T00:00:01Z",
"updated_at": "2026-01-01T02:36:01Z"
}Fix your handler, then replay this delivery by id.
Errors
All errors use the unified envelope and carry an X-Request-Id response header. Branch on code/type/statusCode, not on the free-form message.
{
"type": "unauthorized",
"code": "unauthorized",
"message": "Example error using fake data.",
"request_id": "req_test_000000000123",
"doc_url": null,
"statusCode": 401
}| Status | type | When it happens |
|---|---|---|
400 | invalid_request | Bad query parameter — limit out of range, negative skip, or an unknown status. |
401 | unauthorized | Missing or invalid secret key. |
429 | rate_limited | Throttled. This route allows up to 60 requests per minute. Back off and retry. |
5xx | server_error | Transient server failure. Retry with bounded backoff. |
Production rules
- Keep secret keys on your server. This endpoint requires an
sk_*key. - Page with
limit(≤ 200) andskip; do not assume unbounded list reads. - Dedupe on the event id; treat delivery as at-least-once.
- Verify
Gate-Signatureover the raw body, enforce the 300-second skew, and compare in constant time. - Branch on machine-readable status, error code, object id, and request id.
- Treat examples and placeholder ids as fake data only.
Public boundary
This reference covers partner-scoped endpoint behavior, authentication, idempotency, webhook verification, and support-safe records. Internal operations, administrative routes, settlement venues, and unsupported availability claims are outside the public API contract.