Skip to main content
Every state change of a subscription is delivered to the webhook URL you gave at creation as an HTTP POST with a JSON body and an HMAC signature. Webhooks are the way to learn about renewals: they happen while your customer is not on your site, so there is no redirect to catch.
Always verify the signature before acting on an event, and de-duplicate on event_id: a delivery is retried until your endpoint answers with a 2xx, so the same event can reach you more than once.

Delivery

The one-off payment webhook is unchanged: a payment created with Generate Payment still receives the legacy GET <webhook>?payment_id=<id>&success=<true|false> call. Only subscription events use the signed POST described here.

Payload

string
required
One of the event names below.
string
required
Unique per event. Store it and ignore a body you have already processed.
timestamp
required
When the event happened.
string
required
The subscription concerned.
string | null
The charge concerned, when the event is about a charge. null for lifecycle-only events such as subscription.updated.
string
required
The subscription’s status after the event.
string
required
Your reference for the subscription.
integer
required
Amount of the charge in millimes.
string
required
Always TND.
date | null
First day of the period the charge pays for.
date | null
Last day (exclusive) of that period.
integer | null
0 for the first attempt of a period, 1–3 for retries.
string
required
flouci (wallet) or card.

Events

A single charge can produce two events (for example subscription.charge.succeeded then subscription.reactivated). Each carries its own event_id.

Verifying the signature

The signature is HMAC-SHA256 over the string "<t>.<raw body>" — the timestamp from the header, a dot, and the exact bytes of the request body — keyed with your app’s private token (the second half of your Authorization: Bearer <public>:<private> credential). Compare it to v1 in constant time and reject timestamps older than a few minutes.
Sign over the raw request body, not a re-serialized JSON object — any whitespace or key-order difference changes the digest.

Handling events safely

1

Acknowledge fast

Verify the signature, store the event, answer 200, then process. Slow handlers are retried and produce duplicates.
2

De-duplicate on event_id

Retries and the occasional double delivery mean you must treat event_id as the idempotency key.
3

Trust the status, confirm the money

status is authoritative for access. For accounting, confirm a charge with Verify Payment using its payment_id — the same rule as for one-off payments.