For developers

Public API

A versioned REST API at /api/v1 for reading and creating reservations, quotes, customers and more from your own systems.

Updated

Authentication

Server-to-server calls authenticate with a secret key in the X-API-Key header. Secret keys start with rsk_live_, belong to one company, and are stored only as a SHA-256 hash, so a key is shown once when it is created. Keys are revoked individually: create the new key, deploy it, then revoke the old one.

Each key carries scopes, and every route checks for one of the scopes listed in the endpoint table below. A key without the scope gets 403. The API is available on plans that include API access; the webhook routes also need the webhooks feature.

Publishable keys (rpk_live_) are a different thing. They are locked to allowed browser origins and can only open a guest session for the hosted web booker at POST /api/v2/public/auth/web-booker/session. They cannot call /api/v1.

An authenticated request
curl https://api.renaroapp.com/api/v1/bookings?limit=20 \
  -H "X-API-Key: rsk_live_..." \
  -H "Renaro-Version: 2026-07-16"

Versioning

The contract is date-versioned. Send Renaro-Version (or X-Renaro-Version) on every request. Supported versions are 2026-07-04 and 2026-07-16. A request without the header resolves to 2026-07-04, so an integration never changes behavior without opting in. New integrations should pin 2026-07-16. An unsupported value is rejected with 400 VALIDATION_ERROR.

Every response echoes the resolved Renaro-Version and sets X-Renaro-API-Version: v1, Cache-Control: no-store and X-Content-Type-Options: nosniff.

What changes between versions
Behavior2026-07-04 (default)2026-07-16
Error body Flat: { status, code, message, details?, request_id } Wrapped: { error: { code, message, details? }, meta }
Free-form objects (metadata, fare_breakdown, pricing_snapshot) Keys are camelCased on the way out Returned exactly as stored

Endpoints

Request bodies and query strings use snake_case. Response bodies use camelCase inside a { data, meta } envelope. Routes marked idempotent accept an Idempotency-Key header.

Public API v1 routes and the key scopes they accept
Method and pathScopes (any one)Idempotent
GET /api/v1/bookings bookings.read —
GET /api/v1/bookings/:id bookings.read —
GET /api/v1/bookings/:id/status bookings.read —
GET /api/v1/bookings/:id/tracking bookings.read —
POST /api/v1/bookings bookings.write, bookings.create Yes
PATCH /api/v1/bookings/:id bookings.write, bookings.update Yes
POST /api/v1/bookings/:id/cancel bookings.write, bookings.update Yes
POST /api/v1/bookings/:id/assign bookings.write, dispatch.create Yes
GET /api/v1/drivers drivers.read —
GET /api/v1/drivers/:id drivers.read —
GET /api/v1/drivers/:id/location drivers.read, gps.read —
GET /api/v1/passengers passengers.read —
GET /api/v1/passengers/:id passengers.read —
POST /api/v1/passengers passengers.write, passengers.create Yes
GET /api/v1/vehicles fleet.read —
GET /api/v1/vehicles/:id fleet.read —
POST /api/v1/quotes quotes.write, bookings.create Yes
GET /api/v1/quotes/:id quotes.read, quotes.write, bookings.create —
GET /api/v1/zones zones.read, config.read —
Webhook endpoints (see Webhooks) webhooks.manage or the matching webhooks.* scope Writes: yes

Quote, then book at the quoted price

POST /api/v1/quotes prices a trip and returns 201 with a quote that holds its price for 15 minutes (expiresAt). The response says whether it can still be used: status is open or accepted, with acceptReady and expiresInSeconds. Once a quote expires, fetching it returns 404 NOT_FOUND.

To book at that price, send quote_id with the booking. The pickup and drop-off coordinates must match the quote's to within 0.0002 degrees. A quote books once; reusing an accepted quote returns 409 PUBLIC_QUOTE_ALREADY_ACCEPTED.

Create a quote
curl https://api.renaroapp.com/api/v1/quotes \
  -H "X-API-Key: rsk_live_..." \
  -H "Renaro-Version: 2026-07-16" \
  -H "Idempotency-Key: 5d0c7c9e-2f1a-4b7e-9f55-0c3f1a2b9e10" \
  -H "Content-Type: application/json" \
  -d '{
    "pickup_address": "Chicago O'\''Hare International Airport (ORD), Chicago, IL",
    "pickup_lat": 41.97420,
    "pickup_lng": -87.90730,
    "dropoff_address": "100 Oakbrook Center, Oak Brook, IL 60523",
    "dropoff_lat": 41.84876,
    "dropoff_lng": -87.95301,
    "vehicle_type_id": "184f7424-6654-453e-9374-328346b0124d",
    "scheduled_pickup_at": "2026-10-02T14:30:00Z",
    "passenger_count": 2
  }'
201 response, trimmed (2026-07-16)
{
  "data": {
    "id": "9f0e8400-5c1b-4e9a-9d0b-2d7f3c1a8e21",
    "createdAt": "2026-10-01T15:30:00.000Z",
    "expiresAt": "2026-10-01T15:45:00.000Z",
    "quote": {
      "vehicleTypeId": "184f7424-6654-453e-9374-328346b0124d",
      "currency": "USD",
      "distanceMeters": 21400,
      "durationMinutes": 28,
      "estimatedFareCents": 9500,
      "fareBreakdown": { "...": "..." },
      "pricingSnapshot": { "...": "..." }
    },
    "acceptedAt": null,
    "bookingId": null,
    "status": "open",
    "acceptReady": true,
    "expiresInSeconds": 900
  },
  "meta": {
    "requestId": "3b5e1f0a-8c2d-4f4e-a1b7-6d9c2e0f7a44",
    "timestamp": "2026-10-01T15:30:00.000Z"
  }
}
POST /api/v1/bookings body that books the quote
{
  "quote_id": "9f0e8400-5c1b-4e9a-9d0b-2d7f3c1a8e21",
  "pickup_address": "Chicago O'Hare International Airport (ORD), Chicago, IL",
  "pickup_lat": 41.97420,
  "pickup_lng": -87.90730,
  "dropoff_address": "100 Oakbrook Center, Oak Brook, IL 60523",
  "dropoff_lat": 41.84876,
  "dropoff_lng": -87.95301,
  "vehicle_type_id": "184f7424-6654-453e-9374-328346b0124d",
  "scheduled_pickup_at": "2026-10-02T14:30:00Z",
  "passenger_count": 2,
  "passenger_name": "Dana Whitfield",
  "passenger_phone": "+13125550148",
  "passenger_email": "dana.whitfield@example.com",
  "flight_number": "UA 1542"
}

Idempotency

Every idempotent route accepts an optional Idempotency-Key header. Send a new random key per logical operation and reuse it on retries. A retry with the same key and the same body returns the stored response instead of creating a second record. Keys are scoped to your company, fingerprinted on method, path and body, and kept for 24 hours. Only responses below 400 are stored, so a failed request can be retried with the same key.

  • Same key, different body: 422 IDEMPOTENCY_KEY_REUSED.
  • Same key while the first request is still running: 409 IDEMPOTENCY_REQUEST_IN_FLIGHT. Back off and retry.
  • These two errors currently arrive in the flat 2026-07-04 error shape whatever version you pin, so read code from either shape.

Pagination

List routes take cursor and limit (1 to 200, default 50; the drivers list allows up to 250). The response carries pagination: { cursor, hasMore, total? }, where cursor is the value to send for the next page. Cursors stay stable while new records arrive. GET /api/v1/zones is the exception: it returns every zone in one response.

Errors

Errors carry a stable code, a human-readable message and, for validation failures, details.fieldErrors keyed by field. Every response, success or failure, has a request id in the X-Request-Id header and in the body; send your own X-Request-Id and it is used instead of a generated UUID. Quote it when you contact us. A missing, revoked or malformed key returns 401 UNAUTHORIZED.

Validation error (2026-07-16)
{
  "error": {
    "code": "VALIDATION_ERROR",
    "message": "Invalid request body",
    "details": {
      "fieldErrors": { "pickup_lat": ["Required"] }
    }
  },
  "meta": {
    "requestId": "3b5e1f0a-8c2d-4f4e-a1b7-6d9c2e0f7a44",
    "timestamp": "2026-10-01T15:30:00.000Z"
  }
}

Rate limits

Limits are per key, in fixed 60-second windows: 400 requests for a secret key and 120 for a publishable key. Every response reports X-RateLimit-Limit, X-RateLimit-Remaining and X-RateLimit-Reset (Unix seconds). A 429 adds Retry-After in seconds. Separately, each IP address is throttled before authentication at 600 requests a minute, or 400 for requests that carry X-API-Key.

Conventions

The dashboard carries the full OpenAPI description for your account under Settings, Developer, API Docs, with a Download OpenAPI specification button. Use it to generate a client.

  • Money is integer cents with an ISO 4217 currency.
  • Timestamps are ISO 8601 in UTC with a Z suffix.
  • Ids are UUIDs.
  • The request id travels with the work it starts: webhook deliveries caused by your request carry it as X-Renaro-Request-Id.