APERGREXDocumentation
Sections

Webhooks

Billing and task push events signed with HMAC-SHA256.

Webhooks

Webhooks push billing and task events to you. Register an HTTPS endpoint, subscribe to event types, and receive signed HMAC-SHA256 POST requests.

Quick start

# register an endpoint (the secret is shown exactly once!)
curl -X POST -H "X-API-Key: $FRANKLAB_KEY" -H "Content-Type: application/json" \
  -d '{"url":"https://example.com/franklab-hook","eventTypes":["billing.settled","task.succeeded"]}' \
  https://apergrex.ru/franklab/api/v1/webhooks/endpoints

# test delivery
curl -X POST -H "X-API-Key: $FRANKLAB_KEY" \
  https://apergrex.ru/franklab/api/v1/webhooks/endpoints/<id>/test

Event types

EventWhen
billing.settleda charge was confirmed (with the final amount)
billing.refundeda task reservation was refunded
billing.topup.confirmeda top-up was credited
billing.low_balancebalance dropped below 1,000 ₣ (at most once per 24h)
billing.invoice.status_changeda legal invoice changed status / was paid
task.succeeded / task.failed / task.refundeda generation task reached a terminal status
webhook.testa test event (on demand)

Delivery format

Headers:

X-Franklab-Event: billing.settled
X-Franklab-Event-Id: <event uuid — your idempotency key>
X-Franklab-Timestamp: <unix seconds>
X-Franklab-Signature: sha256=<HMAC-SHA256(secret, "{timestamp}.{body}")>

Body — one envelope:

{
  "eventId": "…",
  "eventType": "billing.settled",
  "occurredAt": "2026-09-04T12:00:00.000Z",
  "partnerId": "…",
  "schemaVersion": 1,
  "data": { "transactionId": "…", "amountFranks": 84 }
}

Verifying the signature

const expected = crypto.createHmac('sha256', secret)
  .update(`${timestamp}.${rawBody}`)
  .digest('hex');
// compare against X-Franklab-Signature (strip the sha256= prefix) and reject
// timestamps older than ~5 minutes (anti-replay)

Delivery guarantees

  • At-least-once: duplicates are possible — deduplicate by eventId.
  • Duplicates can also appear after a poller restart — the robust dedupe key is eventType + data.transactionId / data.taskId / data.invoiceId.
  • Latency is up to a minute (poller cadence).
  • Retries on failure: 1 min → 5 min → 30 min → 2h → 6h, then dead-letter (visible via GET /v1/webhooks/deliveries?status=dead_lettered).
  • The uncertain status means the request left but the outcome is unknown: those deliveries are never auto-retried — replay them via POST /v1/webhooks/deliveries/{id}/replay.
  • Answer 2xx quickly; the timeout is 10 seconds.

Management

OperationEndpoint
ListGET /v1/webhooks/endpoints
RegisterPOST /v1/webhooks/endpoints
DeleteDELETE /v1/webhooks/endpoints/{id}
Rotate secretPOST /v1/webhooks/endpoints/{id}/rotate-secret
TestPOST /v1/webhooks/endpoints/{id}/test
Delivery historyGET /v1/webhooks/deliveries?endpointId=…&status=…

Limits: up to 10 endpoints per partner; the URL must be public HTTPS (internal hosts and IP literals are rejected); redirects are never followed.

For AI agents/en/docs/webhooks.md