Webhooks
Most of billing happens without an API call from you: a trial ends, a renewal invoice goes out, a card declines on the third retry, a buyer opens a dispute. Webhooks tell your systems about it.
invoice_paid costs you nothing per minute. A poller asking "is it paid
yet?" every second spends your whole rate-limit budget to learn nothing most of the time.Register an endpoint
/v1/webhook_endpointscurl -X POST $TMBILLING_URL/v1/webhook_endpoints \
-H "Authorization: Bearer $TMBILLING_API_KEY" \
-H "Idempotency-Key: $(uuidgen)" \
-H "Content-Type: application/json" \
-d '{
"url": "https://api.example.com/hooks/tmbilling",
"event_types": ["invoice_paid", "invoice_payment_failed", "subscription_cancelled"]
}'
Store the signing secret immediately — tmbilling returns it once, when you create the endpoint.
Use event_types to subscribe to a subset, or omit it to receive everything. You can also scope
an endpoint to one business, so a multi-business organization routes each business's events to a
different service.
Read the payload
tmbilling sends a POST with this body and nothing else:
{
"object": "event",
"id": "evt_8f14e45fea6c4a1f",
"event_type": "invoice_paid",
"occurred_at": "2026-09-16T10:31:07.412Z",
"api_version": "2026-06-20",
"source": "billing_clock",
"user": null,
"content": { }
}
| Field | Type | What it holds |
|---|---|---|
id | string | The event id. tmbilling also sends it as the Tmbilling-Event-Id header — deduplicate on it. |
event_type | string | What happened. See the event reference for the full catalogue. |
occurred_at | ISO-8601 | When the change happened, which is not always when you received it. |
source | string | What caused it: the billing clock, an API call, the dashboard, or a provider webhook. |
user | string or null | Who did it, when a person did. |
content | object | A full snapshot of the affected resources, not just an id. You can act on most events without fetching anything. |
Check the headers
Content-Type: application/json
Tmbilling-Signature: sha256=<hex>
Tmbilling-Event-Id: evt_8f14e45fea6c4a1f
Verify the signature on every request before you trust the body. Your URL is public, so anything can post to it. See Verifying signatures.
Build for three properties
| Property | What to do |
|---|---|
| Delivery is at-least-once | A retry after a network failure can redeliver an event you already handled. Record the Tmbilling-Event-Id values you have processed and skip repeats. |
| Events are not ordered | invoice_paid can arrive before invoice_posted. Compare occurred_at against what you have recorded rather than assuming arrival order. |
| New event types appear | tmbilling adds event types within an API version. Log an event_type you don't recognise and skip it — never throw. |
Answer fast, then do the work
Return a 2xx as soon as you have stored the event, and do the real work afterwards, out of band.
A handler that provisions an account, sends an email and only then answers will eventually time out and be redelivered — with the email sent twice.
Test and recover
| Call | What it does |
|---|---|
POST /webhook_endpoints/{id}/test | Sends a sample payload, so you can verify your signature checking before anything real depends on it. |
GET /webhook_endpoints/{id}/deliveries | Lists every attempt, its response and its outcome. |
POST /webhook_endpoints/{id}/deliveries/{del}/redeliver | Redelivers one event after you fix your handler. |
POST /webhook_endpoints/{id}/rotate_secret | Rotates the signing secret and returns the new one once. |
You can also read events directly, which is your backstop when an endpoint stayed down longer than the retry schedule:
/v1/businesses/{biz}/events/v1/events/{evt}The second gives you an event with its per-endpoint delivery record — what tmbilling attempted, and what your server answered.
Related
Money and currency
Send every amount as an integer in minor units with a currency code, expect half-up rounding per line, match currencies across levels, and handle zero amounts.
Signatures
Verify every webhook with HMAC-SHA256 over the raw request body, compared in constant time, and rotate the signing secret without dropping events in flight.