For developers

Webhooks

Signed HTTPS callbacks when reservations, dispatch, drivers, customers and payments change, with retries and a delivery log.

Updated

Creating an endpoint

An owner or admin adds endpoints in the dashboard under Settings, Integrations, Webhooks, or you can manage them with the /api/v1/webhooks routes. Choose exact event names; wildcards such as booking.* are rejected. When the endpoint is created the dashboard shows its signing secret once. Store it in your secret manager.

Endpoints must be public https:// URLs. Private, loopback and link-local hosts are refused when the endpoint is saved, and the host is resolved again before each delivery.

Event types

Subscribe to any of these exact names.

  • To follow assignment, subscribe to dispatch.assignment_created or booking.status_changed.
Webhook event types by area
AreaEvents
Reservations booking.cancelled, booking.completed, booking.created, booking.status_changed, booking.updated
Dispatch dispatch.assignment_cancelled, dispatch.assignment_created, dispatch.assignment_transitioned, dispatch.manual_assignment, dispatch.offer_accepted, dispatch.offer_cancelled, dispatch.offer_created, dispatch.offer_expired, dispatch.offer_rejected, dispatch.offer_responded
Drivers driver.status_changed, driver.suspended, driver.unsuspended, driver.updated
Customers passenger.archived, passenger.blacklisted, passenger.created, passenger.unblacklisted, passenger.updated
Payments payment.captured, payment.created, payment.failed, payment.refund_requested, payment.refunded

Payload and headers

Each delivery is a POST with a JSON body in snake_case. event_id is the id of the record the event is about (the booking, driver, customer or payment), not a unique id for the event.

Headers on every delivery
HeaderMeaning
X-Renaro-Signature t=<ISO-8601 timestamp>,v1=<hex HMAC-SHA256>
X-Renaro-Timestamp The same timestamp as t
X-Renaro-Event-Type The event name, e.g. booking.status_changed
X-Renaro-Event-Id The id of the record the event is about
X-Renaro-Delivery-Id Unique per delivery, unchanged across its retries
X-Renaro-Attempt 1 for the first attempt, then 2, 3 and so on
X-Renaro-Request-Id The API request that caused the event, when there was one
User-Agent Renaro-Outbound-Webhooks/1.0
Body of a booking.status_changed delivery, trimmed
{
  "event_type": "booking.status_changed",
  "event_id": "b6f1c2d4-3e5a-4c7b-9d1e-2f3a4b5c6d7e",
  "occurred_at": "2026-10-02T14:05:12.481Z",
  "data": { "...": "..." }
}

Verifying the signature

The signature is a hex HMAC-SHA256, keyed with the endpoint's signing secret, over the string t + "." + rawBody. Unlike Stripe's scheme, t is an ISO-8601 timestamp, not Unix seconds. Verify against the raw request bytes before parsing the JSON, compare in constant time, and reject deliveries whose timestamp is more than five minutes from your clock. Checking the signature alone would let a captured request be replayed later; the tolerance window closes that.

Node.js verifier
import { createHmac, timingSafeEqual } from "node:crypto";

export function verifyRenaroWebhook(rawBody, signatureHeader, secret, toleranceSeconds = 300) {
  const parts = Object.fromEntries(
    signatureHeader.split(",").map((part) => {
      const index = part.indexOf("=");
      return [part.slice(0, index).trim(), part.slice(index + 1).trim()];
    }),
  );
  const sentAt = Date.parse(parts.t);
  if (!Number.isFinite(sentAt) || Math.abs(Date.now() - sentAt) > toleranceSeconds * 1000) return false;

  const expected = createHmac("sha256", secret).update(`${parts.t}.${rawBody}`).digest("hex");
  const received = Buffer.from(parts.v1 ?? "", "utf8");
  const wanted = Buffer.from(expected, "utf8");
  return received.length === wanted.length && timingSafeEqual(received, wanted);
}

Retries and failures

A delivery succeeds on any 2xx response within 30 seconds. Network errors, timeouts and 408, 409, 425, 429 or 5xx responses are retried with exponential backoff: one second, doubling each time, capped at 15 minutes. By default an endpoint gets five attempts in total (configurable from 0 to 20). Other 4xx responses are not retried. After 100 consecutive failures the endpoint is disabled.

The dashboard's delivery log and GET /api/v1/webhooks/:id/deliveries show each attempt with its status code, and you can send a test event with POST /api/v1/webhooks/:id/test.

Handling deliveries safely

  • Respond 2xx quickly and do the work afterwards; slow handlers hit the 30-second timeout and are retried.
  • Use X-Renaro-Delivery-Id to ignore a retry you have already processed.
  • Deliveries are keyed by endpoint, event type and record, so a later event of the same type for the same record may not be delivered to that endpoint again. Treat a webhook as a prompt to fetch the record's current state from the API, not as a complete change log.
  • Deliveries can arrive out of order. Compare occurred_at, or re-read the record, before overwriting newer data.