Webhooks

Levl can notify your system in real time when events occur (e.g., transfer status changes). This page explains how to register webhook endpoints, what payloads look like, how to verify signatures, retries, and best practices.

Register and manage endpoints

Use the API to manage webhook endpoints:

  • POST /webhook-endpoints — Register a webhook endpoint
  • GET /webhook-endpoints — List webhook endpoints
  • PUT /webhook-endpoints/{webhookId} — Update a webhook endpoint
  • DELETE /webhook-endpoints/{webhookId} — Delete a webhook endpoint

Your endpoint URL must be reachable over HTTPS in production.

Delivery format

Webhooks are sent as HTTP POST requests with JSON bodies.

Headers:

  • Content-Type: application/json
  • User-Agent: Levl-Webhooks/1.0
  • Levl-Signature: t={iso-8601-timestamp},v0={hex-hmac}

Body (example):

{
  "id": "550e8400-e29b-41d4-a716-446655440000",
  "version": "2025-08-08",
  "type": "transfer.status_changed",
  "data": {
    "id": "550e8400-e29b-41d4-a716-446655440001",
    "status": "SENT"
  },
  "created_at": "2025-08-12T10:48:12.123456"
}

Only status-change events are emitted; data contains the final state for the entity.

Signature verification

Every webhook includes a Levl-Signature header so you can verify authenticity.

Steps:

  1. Parse header into timestamp (t) and signature (v0).
  2. Create the signed payload string: t + '.' + raw_request_body.
  3. Compute HMAC-SHA256 of that string using your endpoint's secret.
  4. Compare your computed hex digest to v0 using a constant-time comparison.
  5. Reject if the timestamp is older than 5 minutes from your current time.

Pseudocode:

header = request.headers["Levl-Signature"]  // e.g. t=2025-08-12T10:48:12.123456,v0=abc123...
t, v0 = parse(header)
signed = t + "." + rawBody
expected = HMAC_SHA256_HEX(secret, signed)
if !constantTimeEquals(expected, v0) -> reject
if now() - parseIsoInstant(t) > 5 minutes -> reject

Secrets are supplied by you at registration time and are never returned by the API. To change a secret, register a new endpoint (you may reuse the same URL) with a different secret and switch over on your side.

Retries and idempotency

  • Levl retries failed deliveries with exponential backoff (immediate, 5m, 30m, 2h, 6h, 24h).
  • Use the event id to de-duplicate; process an event only once even if delivered multiple times.

Best practices

  • Respond quickly with 2xx and process asynchronously.
  • Verify signatures and timestamps on every request.
  • Log failures and monitor consecutive failures; manage your own secret rotation by registering a new endpoint when you change the secret.