Webhooks
Lyzi webhooks let you be notified in real time, on your own server, whenever an event happens on a transaction (payment, refund, KYC status change, etc.). Instead of polling the API in a loop, you receive an HTTP POST on the URL of your choice for each event.
A webhook is a plain HTTP POST that the Lyzi server sends to your URL. The request body is a signed JSON payload, and every delivery is retried automatically if it fails.
Configuration
There are two things to set up:
The destination URL (
webhookUrl): provided when you create the transaction or the payment link. This URL receives the notifications related to that transaction.The signing secret (
webhookSigningSecret): used to verify the authenticity of every webhook you receive. It is specific to your merchant account.
The URL must be public and use the http or https scheme. Internal addresses (localhost, private networks, cloud metadata endpoints) are blocked for security reasons.
Retrieve your signing secret
The secret is available from your developer space / back office, or via the API:
GET https://api.lyzi.fr/api/users/webhook-secret
Authorization: Bearer <your_token>Response:
{
"success": true,
"data": { "webhookSigningSecret": "whsec_2b7f...e91a" },
"status": 200
}The secret always starts with the whsec_ prefix. Store it securely: it must never be exposed on the client side (browser, mobile app).
Available events
Every webhook carries an event field in the resource.event format.
Order events (order.*)
order.initial
The order has just been created.
order.pending
The order has been accepted.
order.awaiting
Awaiting payment (forwarded to the exchange).
order.waiting_chain_confirmation
Awaiting on-chain confirmation.
order.paid
The order has been paid.
order.cancelled
The order has been cancelled.
order.error
An error occurred on the order.
order.expired
The payment window expired.
order.kyt_failed
The KYT check failed.
order.refunding
A refund is in progress.
order.refunded
Partial refund completed.
order.full_refunded
Full refund completed.
KYC events (kyc.*)
kyc.pending
The payer's identity verification is in progress.
kyc.approved
The identity verification was approved.
kyc.refused
The identity verification was refused.
kyc.edd_completed
The enhanced due diligence (EDD) review reached a decision.
No webhook is emitted for AI compliance reports, detailed KYT, or KYA.
Default behavior
By default, your account receives payment and refund events:
order.paidorder.refundingorder.refundedorder.full_refunded
The other events (intermediate statuses, order.cancelled, order.error, order.expired, kyc.* events, etc.) are optional: they must be enabled for your account. Contact your Lyzi representative to subscribe to the additional events you need.
Request structure
Each delivery is a POST with a JSON body. The following headers are present:
Content-Type
Always application/json.
Lyzi-Event
The event type, e.g. order.paid.
Lyzi-Webhook-Id
Unique delivery identifier (32 characters). Use it for idempotency.
Lyzi-Signature
HMAC signature of the payload (see below).
Example payload (order.paid)
The body reuses the same fields as the order status response, plus the added event field.
Example payload (kyc.approved)
kyc.* events send a compact payload, tied to the related order.
For kyc.edd_completed, the payload additionally contains eddStatus, eddOutcome (approved or refused) and spendingLimit.
Verify the signature
Every webhook is signed so you can guarantee it comes from Lyzi and has not been tampered with.
The Lyzi-Signature header has the following format:
t: the Unix timestamp (in seconds) at signing time.v1: theHMAC-SHA256signature, in hexadecimal.
The signature is computed over the string ${t}.${raw_body}, using your webhookSigningSecret as the key. The raw_body is the request body exactly as received, without re-serializing it.
Verification steps
Read the raw request body (before any JSON parsing).
Extract
tandv1from theLyzi-Signatureheader.Recompute
HMAC-SHA256("${t}.${raw_body}", webhookSigningSecret).Compare the result to
v1using a constant-time comparison.Reject the request if the timestamp
tdiffers from the current time by more than 5 minutes (replay protection).
Example (Node.js)
With Express, use express.raw({ type: 'application/json' }) (or otherwise keep the raw body) so you have the exact bytes. A body that has been parsed and then re-serialized will produce a different signature and verification will fail.
Idempotency
Lyzi guarantees a single delivery per (order, event) pair. However, because of retries, your server may receive the same delivery more than once if your response is slow or fails.
Use the Lyzi-Webhook-Id header as an idempotency key: store the identifiers you have already processed and ignore duplicates. Your handlers must be idempotent.
Retries
If your server does not respond with a 2xx status, Lyzi automatically retries the delivery.
Total number of attempts
5 (1 initial + 4 retries)
Retry strategy
Exponential backoff
Initial delay between retries
2 seconds (then 4 s, 8 s, 16 s)
Response timeout
10 seconds
A 2xx response marks the delivery as successful. Any other response (or a timeout) triggers a retry, until the attempts are exhausted, after which the delivery is considered permanently failed.
Respond quickly (ideally within a few seconds) with a 2xx status before running your long-running work. Process the payload asynchronously if needed, otherwise your deliveries will be retried unnecessarily.
Rotate the secret
You can regenerate your signing secret at any time. The previous secret is immediately invalidated.
Response:
After a rotation, update your secret on the server side: webhooks emitted afterwards will be signed with the new secret, and verifying with the old one will fail.
Best practices
Always verify the signature before processing a webhook.
Respond
2xxquickly, then process asynchronously.Deduplicate using
Lyzi-Webhook-Id.Tolerate replays by rejecting timestamps that are too old (5-minute window).
Never expose your
webhookSigningSecreton the client side.Do not assume delivery order of events: rely on the
statusfield and onupdatedAt.