POST to your endpoint when something changes: a customer subscribes, a renewal is paid, credits are granted, a balance runs low. Use it to provision access, send emails or keep your own database in step without polling.
Each environment has its own endpoints: Sandbox events go to Sandbox endpoints, Live events to Live ones.
Add an endpoint
In the dashboard, open Settings › Webhooks, choose Add endpoint, enter your URL and pick the events. Store the signing secret (whsec_…) with your other server secrets: it is shown once.
Subscribing to all events (*) includes ones Ledger adds later. Live endpoints must be public https:// URLs. In the dashboard, Send test event sends a webhook_endpoint.test event to that endpoint alone.
Verify the signature
Every request carries aLedger-Signature header:
t is when Ledger signed it (Unix seconds) and v1 is the hex HMAC-SHA256 of {t}.{raw body} with your signing secret. After you rotate the secret, Ledger signs with both the new and the old secret for 24 hours, so the header carries two v1 values and either one verifies.
Verify the raw body: JSON that was parsed and serialized again won’t match. Refuse signatures older than five minutes, so a captured request can’t be replayed.
Handle the event
idis the same on every retry of an event. Store it and skip events you’ve already handled.createdis when the change happened, not when the request was sent.apiVersionnames the shape. A breaking change ships as a new version; an endpoint keeps the version it was created with.- Order isn’t guaranteed. A retry can arrive after a later event. Compare
created, or read the current state from the API, before acting on an old event. - Return a 2xx within 10 seconds. Do the work after you answer: anything else (a redirect, a 4xx, a 5xx, a timeout) counts as a failure.
Events
A paid plan’s
checkout.completed arrives before Ledger has verified the invoice; act on subscription.created to give access. Ledger doesn’t send failed-payment events yet: the subscription keeps its state until Stripe cancels it.
Low and depleted balances
A customer’s credits are one balance, whatever feature spends them, so both balance events are about the customer, with the feature that triggered them.balance.lowis off until you set a threshold in credits under Settings › Webhooks › Low balance alert (one per environment). It fires when a use crosses the threshold, once; it fires again only after the balance goes back above it (a renewal or a credit pack) and crosses again.balance.depletedfires on the first refused request in the customer’s billing period (the plan’s period, or the calendar month in UTC for a customer without a plan). Later refusals in that period send nothing.
Retries and turned-off endpoints
If a delivery fails, Ledger tries again after 1 minute, 5 minutes, 30 minutes, 2 hours, 5 hours, 10 hours twice, and then every 12 hours: 12 attempts over about three days, each a little later at random so endpoints that failed together don’t all retry together. The delivery log in Settings › Webhooks shows each delivery’s attempts and lets you resend one. When every delivery to an endpoint has failed for three days, Ledger turns the endpoint off and says so in Settings › Webhooks. No email is sent. Events that happen while it’s off aren’t sent to it. Fix the receiver, then turn it back on there or withdisabled: false; deliveries that were still retrying resume.
Ledger keeps events and their delivery log for 30 days.
Other ways. To manage endpoints from code, use the webhook endpoint API. Without an SDK, verify the
v1 HMAC described above with your language’s crypto library.