Skip to main content
Routal

Webhooks

Subscribe your endpoint to plan, route, and stop lifecycle events. Routal pushes them as they happen.

Webhooks let Routal notify your systems when something changes — without you polling. Subscribe an HTTPS endpoint to one or more event types and Routal will POST a JSON envelope to it.

Webhooks are configured per project in the planner dashboard at planner.routal.com/h/settings/developers/webhooks. The API endpoints POST /v2/webhook, PUT /v2/webhook/{id}, DELETE /v2/webhook/{id} are dashboard-only today and are not part of the public REST surface.

Configuration

A webhook record has three fields you control:

FieldTypeDescription
urlstringThe HTTPS endpoint that will receive the POST.
enabled_eventsstring[]The event types this webhook should fire for (see Event catalog).
enabledbooleanWhether the webhook is active. Disabled webhooks receive nothing.

A single project can have multiple webhooks, each subscribed to a different subset of events.

Delivery

When a subscribed event happens, Routal sends an HTTPS POST to your url with a JSON body. There are no custom Routal headers today — no X-Routal-Signature, no X-Routal-Event, no distinguishing User-Agent. Identify the call by the body's event_id and project_id, not by headers.

Envelope

{
  "created_at": "2026-05-21T10:30:00.000Z",
  "project_id": "4f75d991ac359f8c4c79d762",
  "event_id": "routal.planner.2.plan.created",
  "meta": { /* event metadata */ },
  "data": { /* event payload */ }
}
FieldTypeDescription
created_atISO 8601 stringWhen the event was emitted server-side.
project_idstringThe project the event belongs to. Useful if you proxy multiple projects through a single endpoint.
event_idstringThe event type (despite the name). One of the values in the Event catalog.
metaobjectEvent-type-specific metadata. Schema varies by event_id.
dataobjectEvent-type-specific payload — the resource that changed. Schema varies by event_id.

event_id is the event type, not a unique delivery identifier. There is no per-delivery ID in the envelope today. Idempotency by event ID is not a viable pattern — see Idempotency below for what to do instead.

Event catalog

These are the eight event types currently emitted to webhooks:

event_idWhen it fires
routal.planner.2.plan.createdA plan was created.
routal.planner.2.plan.updatedA plan was updated (metadata, status, or contents).
routal.planner.2.plan.deletedA plan was deleted.
routal.planner.2.stop.createdA stop was created.
routal.planner.2.stop.deletedA stop was deleted.
routal.planner.2.stop.reportedA stop received a report (proof of delivery, completion, or failure).
routal.drivers.2.route.startedA driver opened the route in the driver app.
routal.drivers.2.route.finishedA driver marked the route finished.

More domain events fire inside Routal (route created/updated/deleted, optimization started/succeeded, vehicle CRUD, etc.) but are not delivered via webhooks today. If you need one of them for your integration, tell us.

Acknowledgement

Your endpoint should respond with any 2xx HTTP status code. Anything else (including timeouts) counts as a failure.

There is no documented timeout SLA for webhook responses today. Aim to respond in under a few seconds and offload heavy work to a background queue.

Failure handling and disable

Failures are tracked per webhook with a failure_count that resets on the first successful delivery.

ThresholdWhat happens
5 consecutive failuresRoutal sends an email to the organization owner warning that the webhook is failing.
50 consecutive failuresThe webhook is automatically disabled (enabled is set to false) and another email is sent to the owner. Future events are not delivered until you re-enable it from the dashboard.

There is no scheduled retry queue with documented intervals (e.g. 30s → 5m → 30m). The failure counter simply increments each time an event fires and the delivery fails; the next attempt happens whenever the next subscribed event fires.

You can manually replay failed deliveries from the webhook delivery log in the planner dashboard.

Idempotency

Because the envelope does not carry a unique per-delivery ID, you have two practical options:

  1. Dedup using fields inside data. Most event payloads include the resource id and a timestamp. For example, two routal.planner.2.plan.updated events for the same plan can be deduped by data.id + data.updated_at.
  2. Make your handler naturally idempotent. Instead of "INSERT a new row", "UPSERT on (project_id, resource_id, event_id)". The exact pattern depends on what your handler does.

A future iteration may add a unique delivery_id field; track the changelog for updates.

Signature verification

Routal does not sign webhook payloads today. There is no HMAC, no X-Routal-Signature header, no shared secret stored on the webhook record. If you need to authenticate that a request came from Routal, options today:

  • IP allowlist. Restrict your endpoint to accept only requests from Routal's egress IPs (ask support for the current list).
  • Secret in the URL. Subscribe a URL containing an opaque token, e.g. https://your-host/webhooks/routal?token=.... Verify the token server-side. Lower-trust than HMAC but workable.
  • mTLS or VPC private link. If you have stronger requirements, talk to us.

Signed deliveries with HMAC-SHA256 are on the roadmap.

Local development

To test webhooks against a local server, use a tunnel like ngrok or Cloudflare Tunnel to expose your localhost URL. Subscribe the tunnel URL from the planner dashboard.

What's not supported today

  • HMAC signing — see above.
  • Per-delivery unique ID in the envelope — see Idempotency.
  • Configurable retry intervals — only the failure-count → disable lifecycle.
  • Public REST endpoints for webhook CRUD — manage from the dashboard.
  • Webhook events for routes (route.created/updated/deleted) and stops other than created/deleted/reported — not delivered today.
  • Webhook events for vehicles — not delivered today.

When any of the above ships, the changelog is the canonical place to find out.