RouteMateRouteMate
Developer Guide

API Documentation

Everything you need to integrate your delivery platform with RouteMate. Import jobs, optimize routes, and track deliveries via our REST API.

Overview

Getting Started

The RouteMate Integration API allows you to programmatically import delivery jobs, have routes automatically optimized, and query job status — all from your existing systems (WMS, TMS, e-commerce platforms, or custom software).

Base URL

https://api.routemate.app/v1

How It Works

  1. Create an Integration — In the RouteMate web app, go to Integrations and click Create Integration. You'll receive a client_id and client_secret.
  2. Get an Access Token — Exchange your credentials for a short-lived bearer token (valid 1 hour).
  3. Import a Job — POST your delivery stops. RouteMate will geocode addresses, optimize the route order, and assign the job to a driver.
  4. Query Status — Check the progress of your job at any time to see which stops have been completed.

Requirements

  • A RouteMate account on the Team or Enterprise plan
  • An organization created in the RouteMate web app
  • An active integration with client credentials
Authentication

Client Credentials Flow

The API uses OAuth2 client credentials. Your secret is never stored — only a SHA-256 hash is kept on our servers.

Security Model

  • Client secrets are hashed with SHA-256 and never stored in plaintext
  • Access tokens are opaque (not JWTs), valid for 1 hour
  • Tokens are single-use and scoped to your integration
  • All requests must be made over HTTPS
  • Include the token in every request: Authorization: Bearer <token>

Important: Your client_secret is shown only once when you create the integration. Store it securely (for example in environment variables or a secrets manager). If lost, you must regenerate it from the RouteMate dashboard.

Endpoint

Get Access Token

POST/v1/integration-token
Exchange credentials for a bearer token
Call this endpoint to receive a short-lived access token. Include the token in the Authorization header of all subsequent requests.

Request Body

FieldTypeRequiredDescription
client_idstringYesYour integration's client ID (starts with rm_ci_)
client_secretstringYesYour integration's client secret (starts with rm_cs_)

Example Request

curl -X POST https://api.routemate.app/v1/integration-token \
  -H "Content-Type: application/json" \
  -d '{
    "client_id": "rm_ci_abc123...",
    "client_secret": "rm_cs_xyz789..."
  }'

Success Response 200

{
  "access_token": "rm_at_e5988dd1b91a4ff3827c5d2ed416ccbe37ae227a",
  "token_type": "Bearer",
  "expires_in": 3600
}

Error Responses

400Missing client_id or client_secret

401Invalid credentials (wrong secret or unknown client_id)

403Integration has been disabled

Endpoint

Import Job & Stops

POST/v1/integration-import
Create a delivery job with stops
Import a delivery job with one or more stops. RouteMate will geocode addresses without coordinates, optimize the delivery order using Google Routes API, create a route for the assigned driver, and return the optimized stop sequence.

Request Headers

FieldTypeRequiredDescription
AuthorizationstringYesBearer <access_token>
Content-TypestringYesapplication/json
Idempotency-KeystringNoUnique key to prevent duplicate imports (valid for 24 hours)

Request Body — Job Fields

FieldTypeRequiredDescription
external_job_idstringYesYour unique identifier for this job (must be unique per integration)
titlestringYesJob title (e.g., "Morning Deliveries - Zone A")
driver_emailstringYesEmail of the driver to assign. If the driver has a RouteMate account, they'll see the route in their app. If not, an invitation is created.
scheduled_datestringNoScheduled date in ISO format (YYYY-MM-DD)
timezonestringNoIANA timezone (e.g., "Australia/Brisbane", "America/New_York")
metadataobjectNoCustom key-value data attached to the job
stopsarrayYesArray of stop objects (see Stop Fields below)

Example Request

curl -X POST https://api.routemate.app/v1/integration-import \
  -H "Authorization: Bearer rm_at_your_token_here" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: order-batch-2026-03-09-001" \
  -d '{
    "external_job_id": "BATCH-2026-03-09-A",
    "title": "Morning Deliveries - Zone A",
    "driver_email": "driver@example.com",
    "scheduled_date": "2026-03-09",
    "timezone": "Australia/Brisbane",
    "stops": [
      {
        "external_stop_id": "ORD-1001",
        "address": "1 George St, Brisbane QLD 4000, Australia",
        "label": "John Smith",
        "notes": "Leave at front door",
        "priority": 1,
        "parcel_count": 2
      },
      {
        "external_stop_id": "ORD-1002",
        "address": "100 Queen St, Brisbane QLD 4000, Australia",
        "label": "Jane Doe",
        "notes": "Ring doorbell",
        "priority": 2
      },
      {
        "external_stop_id": "ORD-1003",
        "address": "South Bank Parklands, Brisbane QLD 4101, Australia",
        "label": "Bob Wilson",
        "priority": 3,
        "parcel_count": 1
      }
    ]
  }'

Success Response 200

{
  "job_id": "e7ad097b-bc51-4d01-9b04-0e372d80613a",
  "external_job_id": "BATCH-2026-03-09-A",
  "route_id": "1c2a8c0b-1b69-4a04-85b1-608b2ebe3878",
  "driver_email": "driver@example.com",
  "driver_resolved": true,
  "driver_user_id": "573b6bee-aa3d-4cb0-af87-620bba0db049",
  "stops_imported": 3,
  "stops_geocoded": 3,
  "stops_geocode_failed": 0,
  "optimization": {
    "total_distance_km": 12.4,
    "total_duration_minutes": 25,
    "encoded_polyline": "fvmfDqmfe\\bPtA..."
  },
  "stops": [
    {
      "external_stop_id": "ORD-1001",
      "address": "1 George St, Brisbane QLD 4000, Australia",
      "latitude": -27.4710,
      "longitude": 153.0234,
      "label": "John Smith",
      "notes": "Leave at front door",
      "sort_order": 0,
      "priority": 1,
      "parcel_count": 2,
      "status": "pending"
    },
    {
      "external_stop_id": "ORD-1003",
      "address": "South Bank Parklands, Brisbane QLD 4101, Australia",
      "latitude": -27.4753,
      "longitude": 153.0208,
      "label": "Bob Wilson",
      "notes": null,
      "sort_order": 1,
      "priority": 3,
      "parcel_count": 1,
      "status": "pending"
    },
    {
      "external_stop_id": "ORD-1002",
      "address": "100 Queen St, Brisbane QLD 4000, Australia",
      "latitude": -27.4688,
      "longitude": 153.0281,
      "label": "Jane Doe",
      "notes": "Ring doorbell",
      "sort_order": 2,
      "priority": 2,
      "parcel_count": 1,
      "status": "pending"
    }
  ],
  "deep_link": "https://app.routemate.app/app/jobs/e7ad097b-...",
  "warnings": [
    { "code": "ADDRESS_GEOCODED", "message": "3 stop(s) were geocoded from address text" },
    { "code": "ROUTE_OPTIMIZED", "message": "Route optimized: 12.4 km, 25 min" }
  ]
}

Note: Note: The stops array is returned in optimized delivery order (by sort_order), not the order you submitted them. The sort_order field indicates the recommended delivery sequence.

Warning Codes

FieldTypeRequiredDescription
ROUTE_OPTIMIZEDinfoNoRoute was successfully optimized with distance and duration
ADDRESS_GEOCODEDinfoNoOne or more stops were geocoded from address text
DRIVER_AUTO_ADDEDinfoNoDriver had a RouteMate account and was auto-added to your organization
DRIVER_NOT_FOUNDwarningNoNo RouteMate account found for the email. An invitation was created.
GEOCODE_FAILEDwarningNoCould not geocode a stop's address. Provide latitude/longitude instead.
Endpoint

Query Job Status

GET/v1/integration-jobs
Get job details and stop statuses
Query the current status of a job, including all stops with their delivery status. Results are scoped to your integration only.

Query Parameters

FieldTypeRequiredDescription
external_job_idstringNoYour external job ID (the one you provided during import)
job_idstringNoRouteMate's internal job UUID

At least one of external_job_id or job_id must be provided.

Example Request

curl "https://api.routemate.app/v1/integration-jobs?external_job_id=BATCH-2026-03-09-A" \
  -H "Authorization: Bearer rm_at_your_token_here"

Success Response 200

{
  "job_id": "e7ad097b-bc51-4d01-9b04-0e372d80613a",
  "external_job_id": "BATCH-2026-03-09-A",
  "title": "Morning Deliveries - Zone A",
  "status": "assigned",
  "driver_email": "driver@example.com",
  "driver_resolved": true,
  "driver_user_id": "573b6bee-...",
  "route_id": "1c2a8c0b-...",
  "scheduled_date": "2026-03-09",
  "timezone": "Australia/Brisbane",
  "metadata": {},
  "stop_count": 3,
  "status_counts": {
    "pending": 2,
    "completed": 1
  },
  "deep_link": "https://app.routemate.app/app/jobs/e7ad097b-...",
  "stops": [
    {
      "external_stop_id": "ORD-1001",
      "address": "1 George St, Brisbane QLD 4000",
      "latitude": -27.4710,
      "longitude": 153.0234,
      "label": "John Smith",
      "notes": "Leave at front door",
      "status": "completed",
      "priority": 1,
      "parcel_count": 2
    },
    ...
  ],
  "created_at": "2026-03-09T08:30:00.000Z",
  "updated_at": "2026-03-09T10:15:00.000Z"
}

Job Statuses

FieldTypeRequiredDescription
pendingstatusNoJob created but driver not yet assigned or resolved
assignedstatusNoDriver has been assigned and can see the route in their app
in_progressstatusNoDriver has started the route
completedstatusNoAll stops have been delivered
Reference

Stop Fields

Complete list of fields available for each stop in the import request.

FieldTypeRequiredDescription
external_stop_idstringYesYour unique identifier for this stop (must be unique within the job)
addressstringYesFull street address. Will be geocoded if latitude/longitude are not provided.
latitudenumberNoLatitude in decimal degrees. Skips geocoding if provided with longitude.
longitudenumberNoLongitude in decimal degrees. Skips geocoding if provided with latitude.
labelstringNoDisplay label (e.g., customer name, order number)
notesstringNoDelivery instructions for the driver
service_typestringNoType of service (e.g., "delivery", "pickup", "service_call")
duration_minutesnumberNoExpected service duration at this stop in minutes
time_window_startstringNoEarliest delivery time (ISO 8601 datetime)
time_window_endstringNoLatest delivery time (ISO 8601 datetime)
prioritynumberNoPriority level (lower = higher priority). Default: 0
parcel_countnumberNoNumber of parcels for this stop. Default: 1
metadataobjectNoCustom key-value data attached to this stop

Tip: Tip: Providing latitude and longitude directly is faster and more reliable than geocoding. If you have coordinates in your system, always include them to avoid geocoding delays and potential failures.

Feature

Route Optimization

Every import automatically optimizes the delivery sequence for minimum driving distance and time.

How Optimization Works

  1. All stops without coordinates are geocoded from their address text
  2. The first stop is treated as the origin and the last stop as the destination
  3. All intermediate stops are optimized for the shortest driving route using Google Routes API
  4. The sort_order field on each stop is updated to reflect the optimized sequence
  5. The route polyline, total distance, and estimated duration are calculated

Response Fields

FieldTypeRequiredDescription
optimization.total_distance_kmnumberNoTotal route distance in kilometers
optimization.total_duration_minutesnumberNoEstimated total driving time in minutes
optimization.encoded_polylinestringNoGoogle Encoded Polyline for rendering the route on a map

Note: Note: Optimization requires at least 2 stops with valid coordinates. If geocoding fails for some stops, the route will be optimized using only the successfully geocoded stops. Stops without coordinates will still appear in the response but won't be part of the optimized sequence.

Reliability

Idempotency

Safely retry failed requests without creating duplicate jobs.

Include an Idempotency-Key header with a unique value (for example a UUID or your internal batch ID) on import requests. If the same key is sent within 24 hours, the API will return the cached response from the first successful call instead of creating a duplicate job.

curl -X POST https://api.routemate.app/v1/integration-import \
  -H "Authorization: Bearer rm_at_your_token" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: batch-2026-03-09-zone-a" \
  -d '{ ... }'

# Safe to retry — same Idempotency-Key returns cached response
curl -X POST https://api.routemate.app/v1/integration-import \
  -H "Authorization: Bearer rm_at_your_token" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: batch-2026-03-09-zone-a" \
  -d '{ ... }'

Best Practice: Always include an Idempotency-Key in production. Use a deterministic key like your-system-batch-id so that network retries don't create duplicate data.

Reference

Error Handling

All errors return a JSON body with an error field.

{
  "error": "Rate limit exceeded",
  "retry_after_seconds": 45
}

// or for validation errors:
{
  "error": {
    "code": "VALIDATION_ERROR",
    "messages": [
      "external_job_id is required",
      "stops[0].address is required"
    ]
  }
}
StatusMeaningAction
200SuccessRequest completed successfully
400Bad RequestCheck the error messages and fix your request body
401UnauthorizedYour token is invalid or expired. Get a new one.
403ForbiddenYour integration has been disabled. Contact your admin.
404Not FoundThe job or endpoint doesn't exist
405Method Not AllowedUse the correct HTTP method (POST or GET)
429Too Many RequestsWait retry_after_seconds before retrying
500Server ErrorRetry with exponential backoff. Contact support if persistent.
Limits

Rate Limits

Rate limits are applied per integration on a rolling 1-minute window.

PlanRequests / minuteMax stops / import
Team60200
EnterpriseCustomCustom

When rate-limited, the API returns HTTP 429 with a retry_after_seconds field. Implement exponential backoff in your integration for best reliability.

Examples

Complete Code Examples

Copy-paste examples for common languages. All examples show the full flow: authenticate, import, and query.

#!/bin/bash
# RouteMate API Integration Example

API_BASE="https://api.routemate.app/v1"
CLIENT_ID="rm_ci_your_client_id"
CLIENT_SECRET="rm_cs_your_client_secret"

# Step 1: Get access token
TOKEN=$(curl -s -X POST "$API_BASE/integration-token" \
  -H "Content-Type: application/json" \
  -d "{
    \"client_id\": \"$CLIENT_ID\",
    \"client_secret\": \"$CLIENT_SECRET\"
  }" | jq -r '.access_token')

echo "Access Token: $TOKEN"

# Step 2: Import a job with stops
RESULT=$(curl -s -X POST "$API_BASE/integration-import" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: my-batch-001" \
  -d '{
    "external_job_id": "BATCH-001",
    "title": "Morning Deliveries",
    "driver_email": "driver@example.com",
    "scheduled_date": "2026-03-10",
    "timezone": "Australia/Brisbane",
    "stops": [
      {
        "external_stop_id": "STOP-1",
        "address": "1 George St, Brisbane QLD 4000",
        "label": "Customer A",
        "parcel_count": 2
      },
      {
        "external_stop_id": "STOP-2",
        "address": "100 Queen St, Brisbane QLD 4000",
        "label": "Customer B",
        "notes": "Leave at reception"
      }
    ]
  }')

echo "$RESULT" | jq .

# Step 3: Query job status
JOB_ID=$(echo "$RESULT" | jq -r '.job_id')
curl -s "$API_BASE/integration-jobs?job_id=$JOB_ID" \
  -H "Authorization: Bearer $TOKEN" | jq .
Coming Soon

Webhooks

Get notified in real-time when deliveries are completed.

Webhook support is coming soon. You'll be able to register a URL and receive POST notifications for events such as:

  • job.completed — All stops in a job have been delivered
  • stop.completed — A single stop has been delivered (includes proof of delivery)
  • stop.failed — A delivery attempt failed
  • driver.location — Real-time driver GPS updates

Interested in early access? Contact us.

Ready to Integrate?

Create your integration credentials in the RouteMate dashboard and start importing jobs in minutes.