Documentación API
Todo lo que necesitas para integrar tu plataforma de entregas con RouteMate.
Contenido
Primeros pasos
La API de integración de RouteMate te permite importar trabajos de entrega por programación, optimizar rutas automáticamente y consultar el estado de los trabajos desde tus sistemas existentes.
URL base
https://api.routemate.app/v1Cómo funciona
- Crear una integración — En la app web de RouteMate, ve a Integraciones y haz clic en Crear integración. Recibirás un client_id y un client_secret.
- Obtener un token de acceso — Intercambia tus credenciales por un bearer token temporal con validez de 1 hora.
- Importar un trabajo — Envía tus paradas de entrega por POST. RouteMate geocodificará las direcciones, optimizará el orden de la ruta y asignará el trabajo a un conductor.
- Consultar estado — Revisa el progreso de tu trabajo en cualquier momento para ver qué paradas se han completado.
Requisitos
- Una cuenta de RouteMate en el plan Team o Enterprise
- Una organización creada en la app web de RouteMate
- Una integración activa con credenciales de cliente
Flujo de credenciales de cliente
La API usa credenciales de cliente OAuth2. Tu secreto nunca se almacena: solo conservamos un hash SHA-256 en nuestros servidores.
Modelo de seguridad
- Los secrets de cliente se hashean con SHA-256 y nunca se almacenan en texto plano
- Los tokens de acceso son opacos (no JWT) y son válidos durante 1 hora
- Los tokens son de un solo uso y están limitados a tu integración
- Todas las solicitudes deben hacerse por HTTPS
- Incluye el token en cada solicitud: Authorization: Bearer <token>
Importante: Tu client_secret solo se muestra una vez cuando creas la integración. Guárdalo de forma segura, por ejemplo en variables de entorno o un gestor de secretos. Si lo pierdes, tendrás que regenerarlo desde el panel de RouteMate.
Obtener token de acceso
/v1/integration-tokenCuerpo de la solicitud
| Field | Type | Required | Description |
|---|---|---|---|
| client_id | string | Yes | El client ID de tu integración (empieza por rm_ci_) |
| client_secret | string | Yes | El client secret de tu integración (empieza por rm_cs_) |
Ejemplo de solicitud
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..."
}'Respuesta correcta 200
{
"access_token": "rm_at_e5988dd1b91a4ff3827c5d2ed416ccbe37ae227a",
"token_type": "Bearer",
"expires_in": 3600
}Respuestas de error
400Falta client_id o client_secret
401Credenciales no válidas (secret incorrecto o client_id desconocido)
403La integración ha sido deshabilitada
Importar trabajo y paradas
/v1/integration-importCabeceras de la solicitud
| Field | Type | Required | Description |
|---|---|---|---|
| Authorization | string | Yes | Bearer <access_token> |
| Content-Type | string | Yes | application/json |
| Idempotency-Key | string | No | Clave única para evitar importaciones duplicadas (válida durante 24 horas) |
Cuerpo de la solicitud — Campos del trabajo
| Field | Type | Required | Description |
|---|---|---|---|
| external_job_id | string | Yes | Tu identificador único para este trabajo (debe ser único por integración) |
| title | string | Yes | Título del trabajo (por ejemplo, Morning Deliveries - Zone A) |
| driver_email | string | Yes | Email del conductor asignado. Si tiene una cuenta de RouteMate, verá la ruta en su app. Si no, se crea una invitación. |
| scheduled_date | string | No | Fecha programada en formato ISO (YYYY-MM-DD) |
| timezone | string | No | Zona horaria IANA (por ejemplo, Australia/Brisbane o America/New_York) |
| metadata | object | No | Datos personalizados en formato clave-valor adjuntos al trabajo |
| stops | array | Yes | Array de objetos de parada (ver Campos de parada más abajo) |
Ejemplo de solicitud
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
}
]
}'Respuesta correcta 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" }
]
}Nota: Nota: el array stops se devuelve en orden de entrega optimizado según sort_order, no en el orden enviado.
Códigos de advertencia
| Field | Type | Required | Description |
|---|---|---|---|
| ROUTE_OPTIMIZED | info | No | La ruta se optimizó correctamente con distancia y duración |
| ADDRESS_GEOCODED | info | No | Una o más paradas se geocodificaron a partir del texto de la dirección |
| DRIVER_AUTO_ADDED | info | No | El conductor tenía una cuenta de RouteMate y se añadió automáticamente a tu organización |
| DRIVER_NOT_FOUND | warning | No | No se encontró ninguna cuenta de RouteMate para ese email. Se creó una invitación. |
| GEOCODE_FAILED | warning | No | No se pudo geocodificar la dirección de una parada. Proporciona latitude/longitude en su lugar. |
Consultar estado del trabajo
/v1/integration-jobsParámetros de consulta
| Field | Type | Required | Description |
|---|---|---|---|
| external_job_id | string | No | Tu ID externo del trabajo (el que proporcionaste durante la importación) |
| job_id | string | No | El UUID interno del trabajo en RouteMate |
Debes proporcionar al menos external_job_id o job_id.
Ejemplo de solicitud
curl "https://api.routemate.app/v1/integration-jobs?external_job_id=BATCH-2026-03-09-A" \
-H "Authorization: Bearer rm_at_your_token_here"Respuesta correcta 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"
}Estados del trabajo
| Field | Type | Required | Description |
|---|---|---|---|
| pending | status | No | Trabajo creado, pero el conductor aún no ha sido asignado o resuelto |
| assigned | status | No | El conductor ha sido asignado y puede ver la ruta en su app |
| in_progress | status | No | El conductor ha comenzado la ruta |
| completed | status | No | Todas las paradas han sido entregadas |
Campos de parada
Lista completa de campos disponibles para cada parada en la solicitud de importación.
| Field | Type | Required | Description |
|---|---|---|---|
| external_stop_id | string | Yes | Tu identificador único para esta parada (debe ser único dentro del trabajo) |
| address | string | Yes | Dirección completa. Se geocodificará si no se proporcionan latitude/longitude. |
| latitude | number | No | Latitud en grados decimales. Omite geocodificación si se proporciona junto con longitude. |
| longitude | number | No | Longitud en grados decimales. Omite geocodificación si se proporciona junto con latitude. |
| label | string | No | Etiqueta visible (por ejemplo, nombre del cliente o número de pedido) |
| notes | string | No | Instrucciones de entrega para el conductor |
| service_type | string | No | Tipo de servicio (por ejemplo, delivery, pickup o service_call) |
| duration_minutes | number | No | Duración estimada del servicio en esta parada, en minutos |
| time_window_start | string | No | Hora más temprana de entrega (datetime ISO 8601) |
| time_window_end | string | No | Hora límite de entrega (datetime ISO 8601) |
| priority | number | No | Nivel de prioridad (un número menor implica mayor prioridad). Por defecto: 0 |
| parcel_count | number | No | Número de paquetes para esta parada. Por defecto: 1 |
| metadata | object | No | Datos personalizados en formato clave-valor adjuntos a esta parada |
Consejo: Consejo: proporcionar latitude y longitude directamente es más rápido y fiable que geocodificar.
Optimización de rutas
Cada importación optimiza automáticamente la secuencia de entrega para minimizar distancia y tiempo.
Cómo funciona la optimización
- Todas las paradas sin coordenadas se geocodifican a partir del texto de la dirección
- La primera parada se trata como origen y la última como destino
- Todas las paradas intermedias se optimizan para obtener la ruta de conducción más corta usando Google Routes API
- El campo sort_order de cada parada se actualiza para reflejar la secuencia optimizada
- Se calculan la polilínea de la ruta, la distancia total y la duración estimada
Campos de respuesta
| Field | Type | Required | Description |
|---|---|---|---|
| optimization.total_distance_km | number | No | Distancia total de la ruta en kilómetros |
| optimization.total_duration_minutes | number | No | Tiempo total estimado de conducción en minutos |
| optimization.encoded_polyline | string | No | Google Encoded Polyline para renderizar la ruta en un mapa |
Nota: La optimización requiere al menos 2 paradas con coordenadas válidas. Si la geocodificación falla en algunas paradas, la ruta se optimizará solo con las que sí tengan coordenadas válidas.
Idempotencia
Reintenta solicitudes fallidas sin crear trabajos duplicados.
Incluye una cabecera Idempotency-Key con un valor único en las solicitudes de importación. Si se repite la misma clave dentro de 24 horas, la API devolverá la respuesta almacenada en caché en lugar de crear un trabajo duplicado.
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 '{ ... }'Buena práctica: Incluye siempre un Idempotency-Key en producción. Usa una clave determinista para evitar duplicados en reintentos de red.
Gestión de errores
Todos los errores devuelven un cuerpo JSON con un campo error.
{
"error": "Rate limit exceeded",
"retry_after_seconds": 45
}
// o para errores de validación:
{
"error": {
"code": "VALIDATION_ERROR",
"messages": [
"external_job_id is required",
"stops[0].address is required"
]
}
}| Estado | Significado | Acción |
|---|---|---|
| 200 | Éxito | La solicitud se completó correctamente |
| 400 | Solicitud incorrecta | Revisa los mensajes de error y corrige el cuerpo de la solicitud |
| 401 | No autorizado | Tu token no es válido o ha expirado. Obtén uno nuevo. |
| 403 | Prohibido | Tu integración ha sido deshabilitada. Contacta con tu administrador. |
| 404 | No encontrado | El trabajo o el endpoint no existe |
| 405 | Método no permitido | Usa el método HTTP correcto (POST o GET) |
| 429 | Demasiadas solicitudes | Espera retry_after_seconds antes de reintentar |
| 500 | Error del servidor | Reintenta con backoff exponencial. Contacta soporte si persiste. |
Límites de tasa
Los límites se aplican por integración en una ventana móvil de 1 minuto.
| Plan | Solicitudes / minuto | Máx. paradas / importación |
|---|---|---|
| Team | 60 | 200 |
| Enterprise | Custom | Custom |
Cuando se aplique límite de tasa, la API devolverá HTTP 429 con un campo retry_after_seconds.
Ejemplos de código completos
Ejemplos listos para copiar y pegar para lenguajes comunes. Todos muestran el flujo completo: autenticar, importar y consultar.
#!/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
Recibe notificaciones en tiempo real cuando se completen entregas.
La compatibilidad con webhooks llegará pronto. Podrás registrar una URL y recibir notificaciones POST para eventos como:
- job.completed — Todas las paradas del trabajo se han entregado
- stop.completed — Se completó una sola parada (incluye prueba de entrega)
- stop.failed — Falló un intento de entrega
- driver.location — Actualizaciones GPS del conductor en tiempo real
¿Te interesa el acceso anticipado? Contáctanos.