Developer API

Build delivery into your product

The Raute Partner API lets your platform push orders into Raute and pull live driver tracking and ETAs. REST and JSON, authenticated with a partner API key you create yourself. Ideal for POS systems, ecommerce, and delivery marketplaces.

Authentication

Every request is authenticated with a partner API key sent in the x-api-key header over HTTPS. A manager on the account creates one under Settings, Developer API, picks what it is allowed to do, and copies it once. Keys are scoped to a single account, and the secret is only ever stored as a hash, so a lost key is rotated rather than recovered.

curl https://www.raute.io/api/v1/orders/ORD-10482/tracking?source=my-pos \
  -H "x-api-key: rk_live_YOUR_PARTNER_KEY"

Base URL: https://www.raute.io/api · All responses are JSON.

Orders

Push delivery orders into Raute and keep them in sync. Raute geocodes the address, places it on a route, and a dispatcher assigns the driver.

POST/v1/orders
Live

Create a delivery order. Send external_id and a repeat of the same call returns the original order instead of creating a second delivery, so a retry after a timeout is safe.

Requires the orders:write scope on your key.

Request body

{
  "external_id": "ORD-10482",
  "source": "my-pos",
  "customer_name": "Jane Doe",
  "phone": "+13105550142",
  "address": "123 Main St",
  "city": "Anytown",
  "state": "CA",
  "zip_code": "90210",
  "delivery_date": "2026-08-24",
  "notes": "Gate code 4417"
}

Response

{
  "data": {
    "id": "6f1c9d84-...",
    "order_number": "MY-POS-ORD-10482",
    "external_id": "ORD-10482",
    "external_source": "my-pos",
    "status": "pending",
    "status_label": "Order received",
    "customer": { "name": "Jane Doe", "phone": "+13105550142" },
    "destination": {
      "address": "123 Main St, Anytown, CA, 90210",
      "lat": 34.0522, "lng": -118.2437
    },
    "delivery_date": "2026-08-24",
    "driver": null
  },
  "created": true,
  "warning": null
}
GET/v1/orders
Live

List this account's orders, newest first. Filter with status, delivery_date, or source. Paginate with limit (max 200) and offset.

Requires the orders:read scope on your key.

Response

{
  "data": [ { "id": "6f1c9d84-...", "status": "in_progress", "..." : "..." } ],
  "pagination": { "limit": 50, "offset": 0, "total": 128 }
}
GET/v1/orders/{id}
Live

Retrieve one order, including proof of delivery: the signature, the photo, and where the driver stood when they closed the stop. {id} accepts either Raute's order id or your own external_id, so you never have to store ours.

Requires the orders:read scope on your key.

Response

{
  "data": {
    "id": "6f1c9d84-...",
    "status": "in_progress",
    "status_label": "Out for delivery",
    "customer": { "name": "Jane Doe", "phone": "+13105550142" },
    "destination": { "address": "123 Main St, Anytown, CA, 90210" },
    "driver": { "name": "Alex Rivera", "location": { "lat": 34.05, "lng": -118.24 } },
    "stop_number": 4,
    "tracking_url": "https://www.raute.io/track/8f3c...",
    "proof_of_delivery": {
      "signature_required": true,
      "captured": true,
      "received_by": "Maria Doe (daughter)",
      "signature_url": "https://.../sig-....png",
      "photo_url": "https://.../proof-....jpg",
      "delivered_at": "2026-08-14T18:41:02Z",
      "delivered_location": { "lat": 34.0521, "lng": -118.2436 }
    }
  }
}
PATCH/v1/orders/{id}
Live

Update customer_name, phone, notes, delivery_date, the time window, or set status to pending, rescheduled, or cancelled. Assignment and delivery are recorded by the dispatcher and the driver, not over the API. To move an address, cancel and create, so the order is re-geocoded rather than left pointing at the old pin.

Requires the orders:write scope on your key.

Request body

{ "delivery_date": "2026-08-25", "notes": "Leave with front desk" }

Response

{
  "data": { "id": "6f1c9d84-...", "status": "pending", "delivery_date": "2026-08-25" }
}
DELETE/v1/orders/{id}
Live

Cancel an order. It is cancelled, never erased: a deleted order re-imports on the next POS sync and reappears, and the delivery history is worth keeping. Pass ?reason= to record why.

Requires the orders:write scope on your key.

Response

{
  "data": { "id": "6f1c9d84-...", "status": "cancelled" },
  "cancelled": true
}

Live Tracking

Power a branded, real-time tracking page inside your own app or website: order status, an ETA window, and the driver's live position. The narrowest scope in the API, so this is the key to put behind a customer-facing page.

GET/v1/orders/{id}/tracking
Live

Status, ETA window, position in the route, a ready-made customer tracking link, and the assigned driver. {id} takes your external_id or Raute's order id (order_number is a display label, not a lookup key). estimated_arrival is a wall-clock window in the store's local timezone (America/Los_Angeles today); estimated_arrival_at is the same moment as an ISO-8601 UTC timestamp, so build countdowns from that. stops_before is how many stops are still ahead of this one, and is null until the route has actually been sequenced. tracking_url is null until a dispatcher assigns a driver, because a customer link means nothing before there is a driver and a route behind it, so poll rather than caching the first null you see. The driver's position is returned only when the fleet has live-location sharing switched on; the name and status always are. Delivered and cancelled orders return no ETA.

Requires the tracking:read scope on your key.

Response

{
  "order_id": "6f1c9d84-...",
  "order_number": "MY-POS-ORD-10482",
  "external_id": "ORD-10482",
  "external_source": "my-pos",
  "status": "in_progress",
  "status_label": "Out for delivery",
  "estimated_arrival": { "from": "2:10 PM", "to": "2:25 PM" },
  "estimated_arrival_at": "2026-08-27T21:17:00.000Z",
  "stop_number": 4,
  "stops_before": 2,
  "tracking_url": "https://www.raute.io/track/8f3c...",
  "driver": {
    "name": "Alex Rivera",
    "location": { "lat": 34.0525, "lng": -118.2440 },
    "location_sharing": true
  },
  "destination": {
    "address": "123 Main St, Anytown, CA, 90210",
    "lat": 34.0522, "lng": -118.2437
  },
  "delivered_at": null,
  "updated_at": "2026-08-14T18:22:41Z"
}

Drivers

Link Raute driver accounts to the employee ids your POS assigns deliveries to. Once linked, an imported order carrying that employee id lands on the right driver automatically; unlinked, it arrives unassigned and waits for a dispatcher. Linking is by exact id only — never by name.

GET/v1/drivers
Live

List this account's drivers with their external identity, oldest first. Filter with ?source= or ?unlinked=true to see which drivers still need linking.

Requires the orders:read scope on your key.

Response

{
  "data": [
    {
      "id": "9c2e1b4a-...",
      "name": "Carlos Ramirez",
      "email": "carlos@example.com",
      "phone": "+13105550177",
      "status": "active",
      "external_source": "blaze",
      "external_id": "5f2b9c81e4b0a1...",
      "created_at": "2026-08-20T17:03:11Z"
    }
  ],
  "total": 6
}
PATCH/v1/drivers/{id}
Live

Attach a POS employee id to a driver, or send both fields as null to unlink. These are the only two driver fields the API writes; accounts themselves are managed in the dashboard. An id already linked to another driver in the account returns 409 rather than silently moving.

Requires the orders:write scope on your key.

Request body

{ "external_source": "blaze", "external_id": "5f2b9c81e4b0a1..." }

Response

{
  "data": { "id": "9c2e1b4a-...", "name": "Carlos Ramirez", "external_source": "blaze", "external_id": "5f2b9c81e4b0a1..." }
}

Webhooks

Not built yet, and listed here so nobody plans around it. Until it ships, poll the tracking endpoint with the external_id you already hold. Because external_id is your own reference, no id mapping is needed to do that.

POST/v1/webhooks
Not built yet

Register a URL to be pushed order events instead of polling for them. This endpoint does not exist yet. If your integration depends on being pushed to rather than polling, tell us and we will scope it with you.

Response

// Not available. Poll this instead:
GET /v1/orders/{your_external_id}/tracking?source=my-pos

Errors

Every failure returns the same shape, so you can branch on error.code instead of matching on text that may be reworded later.

{ "error": { "code": "insufficient_scope", "message": "..." } }
CodeHTTPWhat to do
unauthorized401The key is missing, mistyped, or revoked. Check the x-api-key header.
insufficient_scope403The key is valid but was not granted this. A manager can add the scope under Settings, Developer API.
not_found404No order with that id in your account. If you passed your own external_id, add ?source= as well.
rate_limited429Over this key's per-minute limit (120 reads / 30 writes by default). The 429 message states your key's actual limit. Back off and retry.
missing_field400A required field was empty. The message names it.
invalid_json400The body did not parse. Check the content-type header and the payload.
invalid_status400The status filter is not one we use. The message lists the valid values.
nothing_to_update400None of the fields sent are updatable. The message lists the ones that are.
plan_limit_reached402The account used every order in its plan. Upgrade, or wait for the next billing period.
account_frozen402The subscription is not active, so the account cannot take new orders.
duplicate_external_id409An order with that external_id and source already exists. Fetch it rather than creating it again.
invalid_status_transition409That status cannot follow the current one. Fetch the order to see where it is now.
status_not_settable422The API sets pending, rescheduled and cancelled. Assignment and delivery are recorded by the dispatcher and the driver.

Built for integrations, secured by design

  • • Per-partner API keys, scoped to one account. Rate limited per key at 120 reads and 30 writes a minute by default — higher limits can be provisioned per key, so tell us if your integration's arithmetic needs more (live tracking pages often do); over your limit you get a 429 with a rate_limited code.
  • • Proof of delivery is returned on the order endpoints, which need orders:read, and never on tracking. A signature is evidence for your business, not content for a page you show a customer.
  • • A driver’s live location is exposed only while an order is out for delivery, and only when the fleet enables live-location sharing.
  • • All traffic is encrypted with TLS; data is isolated per company with Postgres Row Level Security. See our security overview.
  • • The endpoints above are the partner surface. Internal admin, billing, and fleet-management APIs are never exposed.

Ready to integrate?

You do not need to wait on us. A manager creates a sandbox key in about a minute, and the same key works against every endpoint above.

Create an API key