API-documentatie
Alles wat je nodig hebt om je bezorgplatform met RouteMate te integreren.
Inhoud
Aan de slag
Met de RouteMate Integration API kun je leveringsjobs programmatisch importeren, routes automatisch optimaliseren en jobstatus opvragen vanuit je bestaande systemen.
Basis-URL
https://api.routemate.app/v1Hoe het werkt
- Maak een integratie aan — Ga in de RouteMate-webapp naar Integraties en klik op Integratie aanmaken. Je ontvangt een client_id en client_secret.
- Haal een toegangstoken op — Wissel je gegevens in voor een tijdelijk bearer token dat 1 uur geldig is.
- Importeer een job — Verstuur je bezorgstops via POST. RouteMate geocodeert de adressen, optimaliseert de routevolgorde en wijst de job toe aan een chauffeur.
- Controleer de status — Bekijk op elk moment de voortgang van je job om te zien welke stops zijn voltooid.
Vereisten
- Een RouteMate-account op het Team- of Enterprise-plan
- Een organisatie die is aangemaakt in de RouteMate-webapp
- Een actieve integratie met client credentials
Client credentials flow
De API gebruikt OAuth2 client credentials. Je secret wordt nooit opgeslagen; alleen een SHA-256-hash wordt op onze servers bewaard.
Beveiligingsmodel
- Client secrets worden met SHA-256 gehasht en nooit in platte tekst opgeslagen
- Access tokens zijn opaque (geen JWT's) en 1 uur geldig
- Tokens zijn eenmalig bruikbaar en beperkt tot je integratie
- Alle requests moeten via HTTPS worden gedaan
- Stuur het token met elke request mee: Authorization: Bearer <token>
Belangrijk: Je client_secret wordt maar één keer getoond wanneer je de integratie aanmaakt. Bewaar het veilig, bijvoorbeeld in omgevingsvariabelen of een secrets manager. Als je het verliest, moet je het opnieuw genereren vanuit het RouteMate-dashboard.
Toegangstoken ophalen
/v1/integration-tokenRequest body
| Field | Type | Required | Description |
|---|---|---|---|
| client_id | string | Yes | De client-ID van je integratie (begint met rm_ci_) |
| client_secret | string | Yes | Het client secret van je integratie (begint met rm_cs_) |
Voorbeeldverzoek
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..."
}'Succesrespons 200
{
"access_token": "rm_at_e5988dd1b91a4ff3827c5d2ed416ccbe37ae227a",
"token_type": "Bearer",
"expires_in": 3600
}Foutresponsen
400client_id of client_secret ontbreekt
401Ongeldige credentials (verkeerd secret of onbekende client_id)
403De integratie is uitgeschakeld
Job en stops importeren
/v1/integration-importRequest headers
| Field | Type | Required | Description |
|---|---|---|---|
| Authorization | string | Yes | Bearer <access_token> |
| Content-Type | string | Yes | application/json |
| Idempotency-Key | string | No | Unieke sleutel om dubbele imports te voorkomen (24 uur geldig) |
Request body — Jobvelden
| Field | Type | Required | Description |
|---|---|---|---|
| external_job_id | string | Yes | Je unieke identifier voor deze job (moet uniek zijn per integratie) |
| title | string | Yes | Titel van de job (bijvoorbeeld Morning Deliveries - Zone A) |
| driver_email | string | Yes | E-mailadres van de chauffeur die je wilt toewijzen. Als de chauffeur een RouteMate-account heeft, ziet die de route in de app. Anders wordt er een uitnodiging aangemaakt. |
| scheduled_date | string | No | Geplande datum in ISO-formaat (YYYY-MM-DD) |
| timezone | string | No | IANA-tijdzone (bijvoorbeeld Australia/Brisbane of America/New_York) |
| metadata | object | No | Aangepaste sleutel-waardedata gekoppeld aan de job |
| stops | array | Yes | Array met stopobjecten (zie Stopvelden hieronder) |
Voorbeeldverzoek
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
}
]
}'Succesrespons 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" }
]
}Opmerking: Opmerking: de stops-array wordt teruggegeven in geoptimaliseerde aflevervolgorde volgens sort_order, niet in de volgorde waarin je deze hebt ingestuurd.
Waarschuwingscodes
| Field | Type | Required | Description |
|---|---|---|---|
| ROUTE_OPTIMIZED | info | No | Route is succesvol geoptimaliseerd met afstand en duur |
| ADDRESS_GEOCODED | info | No | Een of meer stops zijn gegeocodeerd op basis van adrestekst |
| DRIVER_AUTO_ADDED | info | No | De chauffeur had al een RouteMate-account en is automatisch aan je organisatie toegevoegd |
| DRIVER_NOT_FOUND | warning | No | Geen RouteMate-account gevonden voor dit e-mailadres. Er is een uitnodiging aangemaakt. |
| GEOCODE_FAILED | warning | No | Een stopadres kon niet worden gegeocodeerd. Geef in plaats daarvan latitude/longitude op. |
Jobstatus opvragen
/v1/integration-jobsQueryparameters
| Field | Type | Required | Description |
|---|---|---|---|
| external_job_id | string | No | Je externe job-ID (de waarde die je bij import hebt meegestuurd) |
| job_id | string | No | De interne RouteMate job-UUID |
Ten minste een van external_job_id of job_id moet worden opgegeven.
Voorbeeldverzoek
curl "https://api.routemate.app/v1/integration-jobs?external_job_id=BATCH-2026-03-09-A" \
-H "Authorization: Bearer rm_at_your_token_here"Succesrespons 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"
}Jobstatussen
| Field | Type | Required | Description |
|---|---|---|---|
| pending | status | No | Job is aangemaakt, maar de chauffeur is nog niet toegewezen of opgelost |
| assigned | status | No | De chauffeur is toegewezen en kan de route in de app zien |
| in_progress | status | No | De chauffeur is met de route gestart |
| completed | status | No | Alle stops zijn afgeleverd |
Stopvelden
Volledige lijst met beschikbare velden voor elke stop in het importverzoek.
| Field | Type | Required | Description |
|---|---|---|---|
| external_stop_id | string | Yes | Je unieke identifier voor deze stop (moet uniek zijn binnen de job) |
| address | string | Yes | Volledig straatadres. Wordt gegeocodeerd als latitude/longitude niet zijn meegegeven. |
| latitude | number | No | Breedtegraad in decimale graden. Slaat geocodering over als ook longitude is opgegeven. |
| longitude | number | No | Lengtegraad in decimale graden. Slaat geocodering over als ook latitude is opgegeven. |
| label | string | No | Zichtbaar label (bijvoorbeeld klantnaam of bestelnummer) |
| notes | string | No | Bezorginstructies voor de chauffeur |
| service_type | string | No | Type service (bijvoorbeeld delivery, pickup of service_call) |
| duration_minutes | number | No | Verwachte servicetijd op deze stop in minuten |
| time_window_start | string | No | Vroegste bezorgtijd (ISO 8601 datetime) |
| time_window_end | string | No | Laatste bezorgtijd (ISO 8601 datetime) |
| priority | number | No | Prioriteitsniveau (lager getal = hogere prioriteit). Standaard: 0 |
| parcel_count | number | No | Aantal pakketten voor deze stop. Standaard: 1 |
| metadata | object | No | Aangepaste sleutel-waardedata gekoppeld aan deze stop |
Tip: Tip: latitude en longitude direct meesturen is sneller en betrouwbaarder dan geocodering.
Routeoptimalisatie
Elke import optimaliseert automatisch de bezorgvolgorde voor minimale rijtijd en afstand.
Hoe optimalisatie werkt
- Alle stops zonder coördinaten worden gegeocodeerd op basis van hun adrestekst
- De eerste stop wordt behandeld als vertrekpunt en de laatste als bestemming
- Alle tussenliggende stops worden geoptimaliseerd voor de kortste rijroute met de Google Routes API
- Het veld sort_order van elke stop wordt bijgewerkt om de geoptimaliseerde volgorde weer te geven
- De routepolyline, totale afstand en geschatte duur worden berekend
Responsvelden
| Field | Type | Required | Description |
|---|---|---|---|
| optimization.total_distance_km | number | No | Totale routeafstand in kilometers |
| optimization.total_duration_minutes | number | No | Geschatte totale rijtijd in minuten |
| optimization.encoded_polyline | string | No | Google Encoded Polyline om de route op een kaart te tonen |
Opmerking: Opmerking: optimalisatie vereist minstens 2 stops met geldige coördinaten. Als geocodering voor sommige stops mislukt, wordt de route alleen geoptimaliseerd met de stops die wel succesvol zijn gegeocodeerd.
Idempotentie
Herhaal mislukte requests veilig zonder dubbele jobs aan te maken.
Voeg een Idempotency-Key-header met een unieke waarde toe aan importrequests. Als dezelfde sleutel binnen 24 uur opnieuw wordt verzonden, retourneert de API de gecachte response in plaats van een dubbele job te maken.
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: Gebruik altijd een Idempotency-Key in productie. Gebruik een deterministische sleutel zodat netwerkretries geen duplicaten maken.
Foutafhandeling
Alle fouten geven een JSON-body terug met een error-veld.
{
"error": "Rate limit exceeded",
"retry_after_seconds": 45
}
// of voor validatiefouten:
{
"error": {
"code": "VALIDATION_ERROR",
"messages": [
"external_job_id is required",
"stops[0].address is required"
]
}
}| Status | Betekenis | Actie |
|---|---|---|
| 200 | Succes | Request is succesvol voltooid |
| 400 | Ongeldige request | Controleer de foutmeldingen en corrigeer je request body |
| 401 | Niet geautoriseerd | Je token is ongeldig of verlopen. Vraag een nieuwe aan. |
| 403 | Verboden | Je integratie is uitgeschakeld. Neem contact op met je beheerder. |
| 404 | Niet gevonden | De job of endpoint bestaat niet |
| 405 | Methode niet toegestaan | Gebruik de juiste HTTP-methode (POST of GET) |
| 429 | Te veel requests | Wacht retry_after_seconds voordat je opnieuw probeert |
| 500 | Serverfout | Probeer opnieuw met exponential backoff. Neem contact op met support als het probleem aanhoudt. |
Rate limits
Rate limits worden per integratie toegepast in een rollend venster van 1 minuut.
| Plan | Requests / minuut | Max stops / import |
|---|---|---|
| Team | 60 | 200 |
| Enterprise | Custom | Custom |
Bij rate limiting retourneert de API HTTP 429 met een retry_after_seconds-veld.
Volledige codevoorbeelden
Kopieer-en-plakvoorbeelden voor veelgebruikte talen. Alle voorbeelden tonen de volledige flow: authenticeren, importeren en opvragen.
#!/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 .Webhooks
Ontvang realtime meldingen wanneer leveringen zijn voltooid.
Webhook-ondersteuning komt binnenkort. Je kunt dan een URL registreren en POST-meldingen ontvangen voor gebeurtenissen zoals:
- job.completed — Alle stops binnen een job zijn afgeleverd
- stop.completed — Een enkele stop is afgeleverd (inclusief bezorgbewijs)
- stop.failed — Een bezorgpoging is mislukt
- driver.location — Realtime GPS-updates van chauffeurs
Interesse in early access? Neem contact op.