Skip to main content

Endpoints and events

Recurso pushes events (invoice.paid, subscription.updated, usage.alert.triggered, …) to HTTP endpoints you register. This guide covers managing those endpoints through the SDK: creating them, listing them, pausing/resuming, inspecting delivery attempts, and redelivering an event. The webhooks surface manages endpoints. Delivery inspection and redelivery of an event live on the events surface (events / Events in Node/Go, the redeliver_event and list_event_deliveries operations in Python), because an event fans out to every subscribed endpoint.
This guide is about managing outbound webhook endpoints — the SDK is an API client, so every call here is a request to the Recurso API.Receiving webhooks is different: that is an HTTP handler you build in your own app to accept Recurso’s POSTs. The SDK does not run a server or parse inbound deliveries for you. See Verifying inbound deliveries.
The signing secret is returned only in the create response. Store it when you register the endpoint — you cannot fetch it again later. Listing endpoints omits the secret.

Register an endpoint

Pass the destination URL and the event types to deliver. Fetch the full list of valid event types from GET /v1/events/types (events.types() in Node, client.Events.Types(ctx) in Go, list_event_types.sync(...) in Python).
The field name differs by SDK. Node’s WebhookInput uses event_types, while Go (WebhookCreateParams.Events) and the Python CreateWebhookEndpointBody use events. This is a real inconsistency in the current SDKs — use each SDK’s own field name.

List endpoints

Listing returns every endpoint for the tenant, without the signing secret.

Pause and resume

Pausing sets the endpoint to inactive: it stops receiving deliveries but keeps its secret and configuration, so you can resume without re-registering.
1

Pause

Node and Go expose named helpers; Python sends the status body directly.
2

Resume

To remove an endpoint permanently instead, use recurso.webhooks.delete(id) / client.Webhooks.Delete(ctx, id) / delete_webhook_endpoint.sync(id, client=client).

Inspect deliveries

Every attempt to reach an endpoint is a delivery record with an attempt count, the last HTTP status code, and the next retry time. List them per endpoint, newest first, filtered by derived status (pending, succeeded, failed) and paginated with limit/offset.

Redeliver an event

If your handler was down or returned an error, re-enqueue the event to every active subscribed endpoint. Redelivery is keyed on the event ID, not the endpoint, and is idempotent — repeated calls reset the existing delivery records for a fresh attempt rather than creating duplicates. It returns 202 with the count of deliveries queued.
In Python, the redeliver and event-delivery operations live in the recurso.api.webhooks package (redeliver_event, list_event_deliveries) even though they act on an event. IDs passed to Python operations are typed as uuid.UUID — pass a UUID (or a value that stringifies to one).

Verifying inbound deliveries

Receiving a webhook is code you write — an HTTP route in your own app that Recurso POSTs to. None of the three SDKs ship an inbound-signature helper or a server; the webhooks/events surfaces only manage endpoints. Verify signatures yourself:
1

Read the raw request body

Compute the signature over the exact bytes Recurso sent. Do not re-serialize a parsed JSON object — key order or whitespace changes break the HMAC. Capture the raw body before any JSON middleware touches it.
2

Recompute the HMAC with the endpoint's signing secret

Recurso signs each delivery with an HMAC keyed by the signing secret returned when you created the endpoint (the whsec_… value). Recompute the HMAC-SHA256 of the raw body with that secret.
3

Compare in constant time

Compare your computed digest against the signature header on the request using a constant-time comparison (crypto.timingSafeEqual in Node, hmac.Equal in Go, hmac.compare_digest in Python) to avoid timing leaks. Reject the request on any mismatch.
Treat the signing secret like any other credential: keep it server-side, never in a browser or mobile client. If it leaks, delete and re-register the endpoint to rotate it (a new secret is issued on the next create).