Guides
Webhooks
Webhooks send your server a signed
POST request for every followed event. They follow the Standard Webhooks specification, whose official library verifies the signatures.- In the console, on the Webhooks screen, register an endpoint: URL, mode and followed events (owner and admins only).
- Copy the signing secret (
whsec_…), shown only once. - Verify every signature and respond
2xxwithin 10 seconds. - Validate with Send a test.
Endpoint rules:
- HTTPS URL on port 443, without credentials or fragment. The host must resolve to public IP addresses, at registration and on every delivery.
- Mode set at creation: a test endpoint receives the events of test keys; a live endpoint, those of live keys and of the balance.
- 20 endpoints at most per organization, both modes included.
- An event that no active endpoint of the right mode follows is not produced, nor caught up later.
type | Trigger |
|---|---|
generation.succeeded | Generation completed successfully. |
generation.failed | Generation failed, refunded. |
generation.canceled | Generation canceled, refunded. |
balance.low | Balance below the auto recharge threshold, or below €5.00 excluding tax without it, at each crossing. |
balance.recharge_failed | Auto recharge payment refused, or 3 recharges reached within 24 hours: auto recharge now disabled. |
balance.expiring | Part of the balance expires within 30 days (once per top-up or granted credit). |
Balance events are always live mode (see Billing).
Every delivery carries
Content-Type: application/json and User-Agent: RenderThink-Webhooks/1.0:json{"id": "evt_4Kp9WmT2xQr7LbN3vZc8Hd","type": "balance.low","created_at": 1791453648,"livemode": true,"data": {"object": {"object": "balance","amount": 420,"currency": "eur","unit": "cents","low_balance_threshold": 500}}}
| Field | Type | Description |
|---|---|---|
id | string | evt_…, equal to the webhook-id header. |
type | string | Event type. |
created_at | integer | Unix timestamp in seconds. |
livemode | boolean | false in test mode. |
data.object | object | The generation, as GET /v1/generations/{id} returns it, or the balance (balance). |
- The generation is read again for every delivery: its
output[].urlare valid for 15 minutes from the delivery. - The balance, in cents excluding tax, carries
amountand, depending on the event,low_balance_threshold,failed_recharge(amount,auto_recharge_enabled) orexpiring(amount,expires_at). - Ignore unknown fields: new fields can be added, in events as in API responses.
webhook-signature is v1, followed by the base64 HMAC-SHA256 of {webhook-id}.{webhook-timestamp}.{body}, computed with the endpoint secret; during a secret change, two signatures separated by a space. The library verifies the signature and refuses a webhook-timestamp more than 5 minutes off. Verify the raw body, before parsing the JSON. Without the library, compare signatures in constant time (crypto.timingSafeEqual, hmac.compare_digest), never with ==. RENDERTHINK_WEBHOOK_SECRET holds the full secret, whsec_ prefix included.// npm install standardwebhooks expressimport express from 'express'import { Webhook } from 'standardwebhooks'const webhook = new Webhook(process.env.RENDERTHINK_WEBHOOK_SECRET)const app = express()app.post('/webhooks/renderthink',express.raw({ type: 'application/json' }),(req, res) => {let eventtry {event = webhook.verify(req.body, req.headers)} catch {return res.sendStatus(400)}res.sendStatus(204)handleEvent(event)},)app.listen(3000)
Respond
2xx within 10 seconds, then process the event. Any other status, a redirect (never followed), a timeout or a failed connection counts as a failure.A failed delivery is retried automatically, at increasing intervals, for 72 hours after the event, about 16 attempts in all.
| Attempt | Delay after the previous failure |
|---|---|
| 2 | 30 seconds |
| 3 | 1 minute |
| 4 | 2 minutes |
| 5 to 12 | Twice the previous delay: 4 min, 8 min, 16 min, 32 min, 1h04, 2h08, 4h16, 8h32 |
| 13 and later | 12 hours |
- Delays vary by up to 10%. A
Retry-Afterheader in your response is honored, up to 12 hours. - After 72 hours, the event is no longer resent automatically; it stays in the console's delivery log.
- An event may arrive more than once, with the same
webhook-id: ignore an identifier already processed, still responding2xx. Order is not guaranteed: rely oncreated_at. - After 72 hours of failures without any success, or on a
410 Goneresponse, the endpoint is disabled, its scheduled deliveries canceled, and the owner and admins receive an email. Once enabled again, it only receives new events.
Each endpoint has a delivery log, kept for 30 days, and can be disabled or enabled again by hand.
- Resend sends a delivery once more, with the same
webhook-id. - Send a test sends a sample event of a followed type, with fictitious identifiers and no image.
- Change the secret shows the new secret once. For 24 hours, every delivery carries both signatures. A second change within that delay removes the first secret at once.
Resend and test: active endpoint required, never retried.