Webhook Endpoint & Deliveries
Set up the endpoint TensorRail delivers webhooks to and rotate its signing secret in the dashboard; read that configuration and inspect or retry deliveries through the API.
A new account has no webhook endpoint configured. Events still occur (payments succeed, refunds resolve), but nothing is delivered anywhere until you set a URL. Configuring your endpoint is the first integration step after your first test payment.
1. Configure your endpoint
In the dashboard, go to Developers → Webhooks, set your endpoint URL, and reveal your signing secret.
Requirements for the URL:
- Public HTTPS only. Plain
http://, private/internal addresses, and localhost are rejected (webhook_url must be a public https endpoint). - It must answer
2xxwithin 30 seconds (the delivery timeout), though you should respond in well under a second and do any heavy work asynchronously. See delivery and retries.
Changing the endpoint and rotating the secret are both dashboard operator actions, under Developers → Webhooks, requiring a signed-in session with fresh two-factor verification; there is no API call for either, under any credential, so a leaked key can never redirect your webhook traffic or swap your signing secret. The rotated secret is shown once. Every new event and every automatic retry is signed at send time with your current secret, so from the moment you rotate they all use the new one; there is no window in which the old secret still applies. A manual retry re-sends an event exactly as it was first signed, so a manual retry of an event from before the rotation fails verification against the new secret. Deploy the new secret to your verifier before you rotate.
Stopping delivery
To stop delivery, clear the endpoint URL: Developers → Webhooks, empty the field, and save.
The dashboard confirms delivery has stopped, and GET /v1/webhooks/config then reads back
webhook_url: null.
Nothing is lost while delivery is stopped: TensorRail still records every event, so GET /v1/webhooks/deliveries keeps listing them and each one can be retried once you set an endpoint again. Delivery stays stopped until you do.
2. Read your configuration
GET /v1/webhooks/config: scope webhooks:read. Works with your API key.
curl "https://api.tensorrail.com/v1/webhooks/config" \
-H "Authorization: Bearer rail_full_live_xxx"
The response contains your profile's webhook_details (including webhook_url) plus a payment_response_hash_key_set boolean that tells you whether a signing secret is configured. The signing secret's value is never returned by any read endpoint; it is shown only when (re)generated in the dashboard.
webhook_url is null when no endpoint is configured, either because you have not set one yet, or because you stopped delivery.
3. Inspect deliveries
GET /v1/webhooks/deliveries: scope webhooks:read
Lists recent webhook events for your account. Query parameters: limit (default 50, max 100) and event_type (e.g. payment_succeeded). History covers up to 90 days.
event_type takes one event name from the event types table, which lists every event your endpoint can receive. A name the platform does not recognise is rejected with 400 and ERR_2002.
curl "https://api.tensorrail.com/v1/webhooks/deliveries?limit=20&event_type=payment_succeeded" \
-H "Authorization: Bearer rail_full_live_xxx"
GET /v1/webhooks/deliveries/{event_id}/attempts: scope webhooks:read
Returns the full attempt chain for one event. This /v1 surface is gateway-built, not generated
from the engine's schema, so the fields are documented here, not in the reference. Each entry
carries the event's own fields, event_id, event_type, event_class, object_id,
is_delivery_successful, initial_attempt_id and created, and three more that describe the
delivery itself:
| Field | What it holds |
|---|---|
delivery_attempt | initial_attempt, automatic_retry or manual_retry. Absent on older records. |
request | What we sent: headers and body. |
response | What your endpoint answered: headers, body and status_code. |
delivery_attempt is the field to read when you want to know why a delivery happened:
automatic_retry is a step of the retry schedule, manual_retry is someone pressing Retry. It can be absent on
records written before it existed, and in that case the older rule still holds: an entry whose
initial_attempt_id equals its own event_id is the first delivery, and every other entry
in the chain is a retry of it. Order by created to read the chain in sequence, and use
is_delivery_successful to find the attempt that landed.
request.body is the payload as it was sent to you, with a few platform-internal fields
removed: client_secret, sdk_authorization, split_payments and connector are stripped
from this view. Everything your integration reads is present. Verify signatures against the
live webhook, not against this record.
The list endpoint above returns { "events": [...], "total_count": <n> }; total_count is the
total matching your filter, not the number in this page, so it is what you paginate against.
curl "https://api.tensorrail.com/v1/webhooks/deliveries/evt_1a2b3c/attempts" \
-H "Authorization: Bearer rail_full_live_xxx"
4. Retry a delivery
POST /v1/webhooks/deliveries/{event_id}/retry: scope webhooks:write. Works with your API key.
Manually re-sends one event to your endpoint, useful after fixing a bug on your side or after the automatic retry schedule (16 retries over about 24 hours) was exhausted.
Developers → Webhooks shows a Retry control, and it is disabled in live mode. That is deliberate, not a fault: re-driving a live delivery is a live-key action, and the dashboard session is view-only against live data. In live mode, use this endpoint with your live secret key.
::: The re-delivery is signed like any other and appears in the attempt chain as a further entry sharing the original's initial_attempt_id.
curl -X POST "https://api.tensorrail.com/v1/webhooks/deliveries/evt_1a2b3c/retry" \
-H "Authorization: Bearer rail_full_live_xxx"
Remember that your handler must be idempotent: a manual retry of an already-processed event will arrive with the same event_id.
Endpoint summary
On the API, with your rail_* key:
| Endpoint | Scope |
|---|---|
GET /v1/webhooks/config | webhooks:read |
GET /v1/webhooks/deliveries | webhooks:read |
GET /v1/webhooks/deliveries/{event_id}/attempts | webhooks:read |
POST /v1/webhooks/deliveries/{event_id}/retry | webhooks:write |
In the dashboard only, under Developers → Webhooks, with a fresh two-factor step-up; there is no API call for either:
| Action | Where |
|---|---|
| Change the endpoint (or clear it to stop delivery) | Developers → Webhooks: edit the URL field and click Save (or clear it and click Stop delivery) |
| Rotate the signing secret | Developers → Webhooks: click Rotate |
Related
- Webhooks: event catalog, signature verification, retry schedule
- Authentication: the scope table
- Reporting API: base URL and auth
- Troubleshooting