API essentials

Idempotency

Send an Idempotency-Key on every write so a retry after a timeout cannot charge twice, and learn what happens when you reuse a key with a different body.

A network timeout tells you nothing: your request may have been processed, or it may not. Retry blindly and you risk charging a customer twice. Idempotency keys remove the ambiguity.

Send an Idempotency-Key header on every write:

curl -X POST $TMBILLING_URL/v1/businesses/$BIZ/customers \
  -H "Authorization: Bearer $TMBILLING_API_KEY" \
  -H "Idempotency-Key: 8f14e45f-ea6c-4a1f-b0ba-9a1d1c4b2f30" \
  -H "Content-Type: application/json" \
  -d '{ "email": "[email protected]" }'

Use any unique string — most people use a UUID per logical operation. Generate it before your first attempt and reuse the same value for every retry.

Retry safely

tmbilling runs the first request with a given key normally and stores its response. A later request with the same key does not run the handler again. You get the stored response, with:

Idempotent-Replayed: true

So when your client times out and retries, it gets the original answer: the same customer id, the same invoice, the same charge.

Retry within 24 hours

tmbilling keeps a finished request's key and response for at least 24 hours. Retry within that time and you get the stored response. After it, the key can be removed, and a request that uses it again runs as a new request. A retry loop that gives up within minutes is well inside the window; never reuse a key for a different operation days later.

Understand how tmbilling scopes a key

tmbilling scopes each key to the organization that used it, so two organizations can use the same string without colliding.

Within that scope, it binds the key to a fingerprint of the request — the method, the path and the body. Reuse a key with a different payload and you get 409 with an idempotency_error, because your client believes it is retrying while actually asking for something else.

A key named after the code path, like create-customer, is identical for every caller and collides. Derive it from the thing you are creating, or generate a fresh UUID per attempt.

Handle two replies that surprise people

A replay of a secret-minting request returns 409, not 200. Some endpoints return a value that exists only once: an API key secret, a checkout link carrying a bearer token, a rotated webhook signing secret. tmbilling deliberately does not store those responses, so a replay cannot hand the secret out again. The 409 tells you the original request succeeded — look the resource up rather than retrying.

A crashed first attempt returns 409 with instructions. If the first request using your key died after making a durable change but before recording its response, tmbilling can neither replay it nor safely re-run it. It returns 409 explaining the situation and asks for a fresh key, because the durable change already happened.

Know which requests need one

Every write: POST, PATCH, PUT and DELETE. Reads are already idempotent and ignore the header.

tmbilling deduplicates inbound provider webhooks separately, by the provider's own event id, so a gateway that delivers the same notification twice never settles an invoice twice.

Copyright © 2026