Бұл құжат қазір тек English тілінде қолжетімді. Аудармалар дайын болған кезде жарияланады.

Webhooks

POST to your endpoint on customer events: signature, retries, delivery log.

We POST to your endpoint when something happens to your customers. Configured in the dashboard under "Webhooks".

Events

EventWhen
account.createdyou created a customer — via the API or your storefront
account.suspendeda customer was switched off
account.restoreda customer was switched back on
payment.succeededa confirmed payment by your customer
payment.refundeda refund or chargeback of such a payment

A subscription is any subset of that list. An event outside it is rejected at registration: a typo in payment.suceeded has to fail immediately rather than become a subscription that never fires.

There is also webhook.test, which cannot be subscribed to — it arrives only from the "Send test event" button in the dashboard.

The body

POST <your url>
Content-Type: application/json
X-Logrus-Event: payment.succeeded
X-Logrus-Delivery: 6f1d...
X-Logrus-Idempotency-Key: payment.succeeded:6b0c...
X-Logrus-Signature: sha256=<hex>

{
  "event": "payment.succeeded",
  "idempotency_key": "payment.succeeded:6b0c...",
  "created_at": "2026-09-01T10:00:00Z",
  "data": {
    "payment_id": "6b0c...",
    "account_id": "9a1e...",
    "subscription_id": "2f77...",
    "plan_code": "base",
    "amount_cents": 29900,
    "currency": "RUB",
    "status": "succeeded",
    "occurred_at": "2026-09-01T10:00:00Z"
  }
}

amount_cents is always in its currency's minor units and currency always travels with it. Never read the amount without the currency.

For account.* the data block carries account_id, email, external_ref and status.

Signature

X-Logrus-Signature is sha256= followed by the HMAC-SHA256 of the raw request body, lowercase hex, keyed with your endpoint's secret. The secret is shown once at creation and once at each rotation.

Verify before acting on the event, with a constant-time comparison:

import hmac, hashlib

def verify(secret: str, raw_body: bytes, header: str) -> bool:
    mac = hmac.new(secret.encode(), raw_body, hashlib.sha256)
    return hmac.compare_digest("sha256=" + mac.hexdigest(), header)

Hash the bytes you received. If your framework parsed the JSON first and you re-serialise it, the signature will not match, and the HMAC will not be what is wrong.

Idempotency

idempotency_key is stable for the fact, not the attempt: retries and a re-emission of the same event carry the same key. Store it and skip duplicates and nothing is ever applied twice.

Retries

Any 2xx counts as success. Otherwise:

  • 5xx, timeout, connection error — retried after 1, 2, 4, 8, 16, 32, 64, 128 minutes: eight attempts, a little over four hours in total. After that the delivery is marked dead.
  • 4xx (other than 408 and 429) — not retried. That answer means the endpoint understood us and refused, and four hours will not change it.
  • 408 and 429 are retried as temporary.

Answer fast: the timeout is 10 seconds. Put the event on your own queue and return 200; process it afterwards.

Auto-disable

After 20 consecutive failed deliveries the endpoint is switched off, with the reason visible in the dashboard. Fix it and switch it back on — the counter resets.

The log

In the dashboard, under the endpoint list: event, status, attempt count, response code and when the next attempt is due. It is also the only way to see our side of the conversation without asking us.

What a webhook does not replace

A notification can be late, or (after eight failures) never arrive. If reconciliation matters to you, poll metrics and payouts periodically. Webhooks are fast, polling is reliable; serious integrations do both.