0Bit Documentation

Send a webhook test

POST /webhooks/test - Send a signed synthetic webhook event to verify handler setup.

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.

Endpoint

FieldValue
MethodPOST
Path/v1/webhooks/test
AreaWebhooks
Operation idsendTestWebhook
Auth boundarySecret key from your server.

Use it for

Enqueue a synthetic webhook.test event to your configured webhook_url so you can verify signature handling and endpoint reachability end-to-end without waiting for a real event. The call returns the queued delivery; poll GET /webhooks/deliveries for its outcome.

The test event is signed and carries exactly the same headers as a real event, so a handler that accepts the test is wired correctly for production traffic. The payload's data.livemode is always false, and the platform never emits a webhook.test event on its own.

Set your webhook_url first

If your account has no webhook_url configured yet, this call returns 400. Configure the endpoint, then send a test.

Request

No request body and no path or query parameters. Authenticate with your sk_* secret key. This route is not idempotent: every successful call enqueues a brand-new webhook.test event with its own event_id, so calling it twice delivers two test events. Send one test, then poll GET /webhooks/deliveries for its outcome rather than re-firing.

Server-side, secret key only

This endpoint requires an sk_* secret key. pk_* publishable keys are browser-safe and only bootstrap the embed; they cannot send a test event.

Response

Returns the queued WebhookDelivery with event_type: webhook.test and status: pending. For the full field list, see the WebhookDelivery record.

FieldTypeUse it for
objectstringAlways webhook_delivery.
idstringDelivery id; poll its outcome on the deliveries list.
event_idstringStable event id, mirrored in X-0bit-Event-Id; your dedupe key.
event_typestringAlways webhook.test.
statusenumpending until the worker attempts delivery.

Signature and headers

The test POST to your webhook_url carries the same signature and companion headers as every real event:

HeaderValue
Gate-Signaturet=<unix-ts>,v1=<hex> — the timestamped HMAC (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-Typewebhook.test.

Verify the signature

The v1 value is an HMAC-SHA256 (hex) over the string formed by the timestamp, a literal ., then the raw request body — sign the exact bytes you received. Reject anything outside 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'),
  );
}

The same verification applies to every real event in the event catalogue.

Examples

curl -X POST https://gate-api.0bit.app/v1/webhooks/test \
  -H "Authorization: Bearer sk_test_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
{
  "object": "webhook_delivery",
  "id": "whd_test_67a1f3b9e4b0c10001230001",
  "event_id": "c0ffee00-1111-2222-3333-444455556666",
  "event_type": "webhook.test",
  "target_url": "https://partner.example/webhooks/0bit",
  "status": "pending",
  "attempts": 0,
  "last_response_status": null,
  "last_error": null,
  "next_attempt_at": "2026-01-01T00:00:01Z",
  "delivered_at": null,
  "created_at": "2026-01-01T00:00:01Z",
  "updated_at": "2026-01-01T00:00:01Z"
}

This is the body POSTed to your webhook_url:

{
  "id": "c0ffee00-1111-2222-3333-444455556666",
  "type": "webhook.test",
  "created_at": 1716729600,
  "data": {
    "livemode": false,
    "message": "This is a test event from 0Bit."
  }
}

Delivery and retry

The test event follows the same delivery rules as real events: at-least-once delivery, a 10-second timeout per attempt, retries at 1m, 5m, 30m, 2h, then dead-letter after 5 attempts. See delivery and retry for the full semantics.

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": "invalid_request",
  "code": "invalid_request",
  "message": "Example error using fake data.",
  "request_id": "req_test_000000000123",
  "doc_url": null,
  "statusCode": 400
}
StatustypeWhen it happens
400invalid_requestNo webhook_url is configured for your account yet. Set one, then send a test.
401unauthorizedMissing or invalid secret key.
429rate_limitedThrottled. This route allows up to 10 requests per minute. Back off and retry.
5xxserver_errorTransient server failure. Retry with bounded backoff.

Production rules

  • Keep secret keys on your server. This endpoint requires an sk_* key.
  • Configure webhook_url before testing; an unset endpoint returns 400.
  • Verify Gate-Signature over the raw body, enforce the 300-second skew, and compare in constant time — the test is the safe place to prove this works.
  • This route is not idempotent: each call enqueues a new test event. Send one and poll its outcome rather than re-firing on a slow response.
  • 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.

On this page