API-Dokumentation
Alles, was du brauchst, um deine Lieferplattform mit RouteMate zu integrieren.
Inhalt
Erste Schritte
Mit der RouteMate-Integrations-API kannst du Lieferjobs programmatisch importieren, Routen automatisch optimieren und den Jobstatus aus deinen bestehenden Systemen abfragen.
Basis-URL
https://api.routemate.app/v1So funktioniert es
- Integration erstellen — Gehe in der RouteMate-Web-App zu Integrationen und klicke auf Integration erstellen. Du erhältst eine client_id und ein client_secret.
- Zugriffstoken abrufen — Tausche deine Zugangsdaten gegen ein kurzlebiges Bearer-Token mit 1 Stunde Gültigkeit.
- Job importieren — Sende deine Lieferstopps per POST. RouteMate geokodiert die Adressen, optimiert die Routenreihenfolge und weist den Job einem Fahrer zu.
- Status abfragen — Prüfe den Fortschritt deines Jobs jederzeit, um zu sehen, welche Stopps abgeschlossen wurden.
Voraussetzungen
- Ein RouteMate-Konto im Team- oder Enterprise-Tarif
- Eine in der RouteMate-Web-App erstellte Organisation
- Eine aktive Integration mit Client Credentials
Client-Credentials-Flow
Die API verwendet OAuth2 Client Credentials. Dein Secret wird niemals gespeichert, sondern nur als SHA-256-Hash auf unseren Servern hinterlegt.
Sicherheitsmodell
- Client-Secrets werden mit SHA-256 gehasht und nie im Klartext gespeichert
- Access Tokens sind opak (keine JWTs) und 1 Stunde gültig
- Tokens sind einmalig nutzbar und auf deine Integration beschränkt
- Alle Anfragen müssen über HTTPS erfolgen
- Sende das Token bei jeder Anfrage mit: Authorization: Bearer <token>
Wichtig: Dein client_secret wird nur einmal angezeigt, wenn du die Integration erstellst. Speichere es sicher, zum Beispiel in Umgebungsvariablen oder einem Secret-Manager. Wenn es verloren geht, musst du es im RouteMate-Dashboard neu generieren.
Zugriffstoken abrufen
/v1/integration-tokenRequest-Body
| Field | Type | Required | Description |
|---|---|---|---|
| client_id | string | Yes | Die Client-ID deiner Integration (beginnt mit rm_ci_) |
| client_secret | string | Yes | Das Client-Secret deiner Integration (beginnt mit rm_cs_) |
Beispielanfrage
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..."
}'Erfolgsantwort 200
{
"access_token": "rm_at_e5988dd1b91a4ff3827c5d2ed416ccbe37ae227a",
"token_type": "Bearer",
"expires_in": 3600
}Fehlerantworten
400client_id oder client_secret fehlt
401Ungültige Zugangsdaten (falsches Secret oder unbekannte client_id)
403Die Integration wurde deaktiviert
Job und Stopps importieren
/v1/integration-importRequest-Header
| Field | Type | Required | Description |
|---|---|---|---|
| Authorization | string | Yes | Bearer <access_token> |
| Content-Type | string | Yes | application/json |
| Idempotency-Key | string | No | Eindeutiger Schlüssel zur Vermeidung doppelter Importe (24 Stunden gültig) |
Request-Body — Job-Felder
| Field | Type | Required | Description |
|---|---|---|---|
| external_job_id | string | Yes | Deine eindeutige Kennung für diesen Job (muss pro Integration eindeutig sein) |
| title | string | Yes | Jobtitel (zum Beispiel Morning Deliveries - Zone A) |
| driver_email | string | Yes | E-Mail des zugewiesenen Fahrers. Wenn der Fahrer ein RouteMate-Konto hat, sieht er die Route in seiner App. Andernfalls wird eine Einladung erstellt. |
| scheduled_date | string | No | Geplantes Datum im ISO-Format (YYYY-MM-DD) |
| timezone | string | No | IANA-Zeitzone (zum Beispiel Australia/Brisbane oder America/New_York) |
| metadata | object | No | Benutzerdefinierte Schlüssel-Wert-Daten, die dem Job zugeordnet sind |
| stops | array | Yes | Array mit Stopp-Objekten (siehe Stopp-Felder unten) |
Beispielanfrage
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
}
]
}'Erfolgsantwort 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" }
]
}Hinweis: Hinweis: Das Array stops wird in optimierter Lieferreihenfolge anhand von sort_order zurückgegeben, nicht in der ursprünglich gesendeten Reihenfolge.
Warncodes
| Field | Type | Required | Description |
|---|---|---|---|
| ROUTE_OPTIMIZED | info | No | Die Route wurde erfolgreich mit Distanz und Dauer optimiert |
| ADDRESS_GEOCODED | info | No | Ein oder mehrere Stopps wurden aus dem Adresstext geokodiert |
| DRIVER_AUTO_ADDED | info | No | Der Fahrer hatte bereits ein RouteMate-Konto und wurde automatisch zu deiner Organisation hinzugefügt |
| DRIVER_NOT_FOUND | warning | No | Für diese E-Mail wurde kein RouteMate-Konto gefunden. Eine Einladung wurde erstellt. |
| GEOCODE_FAILED | warning | No | Die Adresse eines Stopps konnte nicht geokodiert werden. Gib stattdessen latitude/longitude an. |
Jobstatus abfragen
/v1/integration-jobsQuery-Parameter
| Field | Type | Required | Description |
|---|---|---|---|
| external_job_id | string | No | Deine externe Job-ID (die beim Import übermittelt wurde) |
| job_id | string | No | Die interne UUID des Jobs in RouteMate |
Mindestens einer der Parameter external_job_id oder job_id muss angegeben werden.
Beispielanfrage
curl "https://api.routemate.app/v1/integration-jobs?external_job_id=BATCH-2026-03-09-A" \
-H "Authorization: Bearer rm_at_your_token_here"Erfolgsantwort 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-Status
| Field | Type | Required | Description |
|---|---|---|---|
| pending | status | No | Job wurde erstellt, aber der Fahrer ist noch nicht zugewiesen oder aufgelöst |
| assigned | status | No | Der Fahrer wurde zugewiesen und kann die Route in seiner App sehen |
| in_progress | status | No | Der Fahrer hat die Route begonnen |
| completed | status | No | Alle Stopps wurden zugestellt |
Stopp-Felder
Vollständige Liste der verfügbaren Felder für jeden Stopp in der Importanfrage.
| Field | Type | Required | Description |
|---|---|---|---|
| external_stop_id | string | Yes | Deine eindeutige Kennung für diesen Stopp (muss innerhalb des Jobs eindeutig sein) |
| address | string | Yes | Vollständige Straßenadresse. Wird geokodiert, wenn latitude/longitude nicht mitgegeben werden. |
| latitude | number | No | Breitengrad in Dezimalgrad. Überspringt Geokodierung, wenn longitude ebenfalls vorhanden ist. |
| longitude | number | No | Längengrad in Dezimalgrad. Überspringt Geokodierung, wenn latitude ebenfalls vorhanden ist. |
| label | string | No | Anzeigelabel (zum Beispiel Kundenname oder Bestellnummer) |
| notes | string | No | Lieferhinweise für den Fahrer |
| service_type | string | No | Art der Leistung (zum Beispiel delivery, pickup oder service_call) |
| duration_minutes | number | No | Erwartete Servicezeit an diesem Stopp in Minuten |
| time_window_start | string | No | Frühester Lieferzeitpunkt (ISO 8601 datetime) |
| time_window_end | string | No | Spätester Lieferzeitpunkt (ISO 8601 datetime) |
| priority | number | No | Prioritätsstufe (kleinere Zahl = höhere Priorität). Standard: 0 |
| parcel_count | number | No | Anzahl der Pakete für diesen Stopp. Standard: 1 |
| metadata | object | No | Benutzerdefinierte Schlüssel-Wert-Daten, die diesem Stopp zugeordnet sind |
Tipp: Tipp: latitude und longitude direkt mitzuliefern ist schneller und zuverlässiger als Geokodierung.
Routenoptimierung
Jeder Import optimiert automatisch die Lieferreihenfolge für minimale Fahrzeit und Distanz.
So funktioniert die Optimierung
- Alle Stopps ohne Koordinaten werden aus dem Adresstext geokodiert
- Der erste Stopp gilt als Ursprung und der letzte als Ziel
- Alle Zwischenstopps werden für die kürzeste Fahrstrecke mit der Google Routes API optimiert
- Das Feld sort_order jedes Stopps wird aktualisiert, um die optimierte Reihenfolge abzubilden
- Routen-Polylinie, Gesamtdistanz und geschätzte Dauer werden berechnet
Antwortfelder
| Field | Type | Required | Description |
|---|---|---|---|
| optimization.total_distance_km | number | No | Gesamtdistanz der Route in Kilometern |
| optimization.total_duration_minutes | number | No | Geschätzte Gesamtfahrzeit in Minuten |
| optimization.encoded_polyline | string | No | Google Encoded Polyline zur Darstellung der Route auf einer Karte |
Hinweis: Für die Optimierung sind mindestens 2 Stopps mit gültigen Koordinaten erforderlich. Wenn die Geokodierung bei einigen Stopps fehlschlägt, wird die Route nur mit den erfolgreich geokodierten Stopps optimiert.
Idempotenz
Fehlgeschlagene Requests sicher wiederholen, ohne doppelte Jobs zu erzeugen.
Füge Importanfragen einen Idempotency-Key mit einem eindeutigen Wert hinzu. Wird derselbe Schlüssel innerhalb von 24 Stunden erneut gesendet, liefert die API die zwischengespeicherte Antwort statt einen doppelten Job zu erstellen.
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: Verwende in Produktion immer einen Idempotency-Key. Nutze einen deterministischen Schlüssel, damit Netzwerk-Wiederholungen keine Duplikate erzeugen.
Fehlerbehandlung
Alle Fehler liefern einen JSON-Body mit einem error-Feld zurück.
{
"error": "Rate limit exceeded",
"retry_after_seconds": 45
}
// oder bei Validierungsfehlern:
{
"error": {
"code": "VALIDATION_ERROR",
"messages": [
"external_job_id is required",
"stops[0].address is required"
]
}
}| Status | Bedeutung | Aktion |
|---|---|---|
| 200 | Erfolg | Die Anfrage wurde erfolgreich abgeschlossen |
| 400 | Ungültige Anfrage | Prüfe die Fehlermeldungen und korrigiere den Request-Body |
| 401 | Nicht autorisiert | Dein Token ist ungültig oder abgelaufen. Hole ein neues. |
| 403 | Verboten | Deine Integration wurde deaktiviert. Wende dich an deinen Admin. |
| 404 | Nicht gefunden | Der Job oder der Endpunkt existiert nicht |
| 405 | Methode nicht erlaubt | Verwende die richtige HTTP-Methode (POST oder GET) |
| 429 | Zu viele Anfragen | Warte retry_after_seconds, bevor du es erneut versuchst |
| 500 | Serverfehler | Erneut mit exponentiellem Backoff versuchen. Bei anhaltenden Problemen Support kontaktieren. |
Rate Limits
Rate Limits gelten pro Integration in einem rollierenden 1-Minuten-Fenster.
| Tarif | Anfragen / Minute | Max. Stopps / Import |
|---|---|---|
| Team | 60 | 200 |
| Enterprise | Custom | Custom |
Bei Überschreitung des Limits gibt die API HTTP 429 mit dem Feld retry_after_seconds zurück.
Vollständige Codebeispiele
Kopierfertige Beispiele für gängige Sprachen. Alle zeigen den vollständigen Ablauf: authentifizieren, importieren und abfragen.
#!/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
Erhalte Benachrichtigungen in Echtzeit, wenn Lieferungen abgeschlossen werden.
Webhook-Unterstützung kommt bald. Du kannst dann eine URL registrieren und POST-Benachrichtigungen für Ereignisse wie diese erhalten:
- job.completed — Alle Stopps eines Jobs wurden zugestellt
- stop.completed — Ein einzelner Stopp wurde zugestellt (inklusive Zustellnachweis)
- stop.failed — Ein Zustellversuch ist fehlgeschlagen
- driver.location — GPS-Updates des Fahrers in Echtzeit
Interesse an Early Access? Kontaktiere uns.