Developers

Webhooks

When an invoice changes state, CryBit sends a signed POST request with a JSON body to the webhook URL of your merchant.

Setup

Set the webhook URL and tick the events in the merchant settings, on the integration tab: paid, expired, refunded, cancelled. The URL must be a public HTTP or HTTPS address on port 80, 443, 8080 or 8443; redirects are not followed. It is set on the merchant, not in the invoice request.

Headers

HeaderValue
X-CryBit-EventThe event name, for example payment.paid.
X-CryBit-Signaturet=<unix time>,v1=<hex>: HMAC-SHA256 of "<t>.<raw body>" with your signing secret.

Verify the signature

The signing secret is shown in the merchant settings next to the webhook URL. Compute the signature over the raw body, compare in constant time and reject requests older than five minutes.

PHP

$secret = getenv('CRYBIT_WEBHOOK_SECRET');
$body   = file_get_contents('php://input');
$header = $_SERVER['HTTP_X_CRYBIT_SIGNATURE'] ?? '';

parse_str(str_replace(',', '&', $header), $p);   // t=…&v1=…
$t = (int) ($p['t'] ?? 0);
$expected = hash_hmac('sha256', $t . '.' . $body, $secret);

if (abs(time() - $t) > 300 || !hash_equals($expected, $p['v1'] ?? '')) {
    http_response_code(400);
    exit;
}
// the request is genuine: store the event, answer 200, do the rest later
http_response_code(200);

Node.js

import crypto from 'node:crypto'

export function verify(rawBody, header, secret) {
  const parts = Object.fromEntries(header.split(',').map((kv) => kv.split('=')))
  const t = Number(parts.t)
  const expected = crypto.createHmac('sha256', secret).update(t + '.' + rawBody).digest('hex')
  const ok = parts.v1 && parts.v1.length === expected.length
    && crypto.timingSafeEqual(Buffer.from(parts.v1), Buffer.from(expected))
  return ok && Math.abs(Date.now() / 1000 - t) <= 300
}

Delivery and retries

  • Answer with a 2xx status quickly: CryBit waits about twelve seconds.
  • If you do not answer, CryBit retries after 15 seconds, 1 minute, 5, 15 and 30 minutes, then 2, 6 and 24 hours.
  • A 4xx answer, except 429, stops the retries.
  • The same event can arrive more than once: handle it idempotently, keyed by order_id or the invoice uuid.
  • If you missed a webhook, read the status with GET /api/v1/payments/{uuid}: it is always the source of truth.

FAQ

Can I see what was delivered?

Yes. The API logs page in the merchant account lists requests and webhook deliveries with the status code and the body.

More

Get an API key