API Documentation

Complete reference for the Route Optimization API. Base URL: http://localhost:8000

v2.8.0

Getting Started

The Route Optimization API solves vehicle routing problems (VRP) with time windows, capacities, skills, and multi-objective optimization. There are two authentication methods and two execution modes:

API Key
Public API

For programmatic access. Pass your key via the Authorization header. Use for optimization, matrix, jobs, and re-optimization endpoints.

JWT Auth
Portal API

For web portal access. Register, then login to receive a JWT token. Pass it as Bearer <token> in the Authorization header.

Synchronous Mode

POST /v1/optimize blocks until the solver finishes and returns the result directly. Best for small problems (< 50 nodes).

Asynchronous Mode

POST /v1/solve returns a job ID immediately. Poll GET /v1/jobs/{id} until status is COMPLETED. Best for large problems or production use. solve:write is enough to poll jobs created with the same key.

Quick Start (cURL)

# 1. Register an account
curl -X POST http://localhost:8000/v1/auth/register \
  -H "Content-Type: application/json" \
  -d '{"email": "user@example.com", "password": "securepassword"}'

# Response: {"access_token": "eyJ...", "token_type": "bearer"}

# 2. Create an API key (using JWT from step 1)
curl -X POST http://localhost:8000/v1/portal/api-keys \
  -H "Authorization: Bearer eyJ..." \
  -H "Content-Type: application/json" \
  -d '{"name": "my-key", "scopes": ["solve:write"]}'

# 3. Synchronous optimization (blocks until complete)
curl -X POST http://localhost:8000/v1/optimize \
  -H "Authorization: sk-abc123..." \
  -H "Content-Type: application/json" \
  -d @optimize-request.json

# 4. Async optimization (returns job ID, poll for result)
JOB_ID=$(curl -s -X POST http://localhost:8000/v1/solve \
  -H "Authorization: sk-abc123..." \
  -H "Content-Type: application/json" \
  -d @optimize-request.json | jq -r '.id')

# Poll until complete
curl http://localhost:8000/v1/jobs/$JOB_ID \
  -H "Authorization: sk-abc123..."

Quick Start (Python)

import requests, time

BASE_URL = "http://localhost:8000"

# Register & get token
resp = requests.post(f"{BASE_URL}/v1/auth/register", json={
    "email": "user@example.com",
    "password": "securepassword"
})
token = resp.json()["access_token"]

# Create API key
resp = requests.post(
    f"{BASE_URL}/v1/portal/api-keys",
    headers={"Authorization": f"Bearer {token}"},
    json={"name": "my-key", "scopes": ["solve:write"]}
)
api_key = resp.json()["key"]
headers = {"Authorization": api_key}

# Define the problem
problem = {
    "problem_type": "vrptw",
    "objectives": {"primary": "minimize_total_duration"},
    "vehicles": [{
        "id": "v1",
        "start_location": {"lat": 50.85, "lng": 4.35},
        "capacity": {"weight": 500},
        "available_time_windows": [
            {"start": "2025-01-15T08:00:00", "end": "2025-01-15T17:00:00"}
        ]
    }],
    "tasks": [{
        "id": "t1",
        "location": {"lat": 50.83, "lng": 4.37},
        "service_duration_minutes": 30,
        "demand": {"weight": 10},
        "time_windows": [
            {"start": "2025-01-15T09:00:00", "end": "2025-01-15T12:00:00"}
        ]
    }],
    "options": {
        "include_route_geometry": True,
        "calculate_carbon_footprint": True
    }
}

# Option A: Synchronous (blocks)
result = requests.post(f"{BASE_URL}/v1/optimize", headers=headers, json=problem).json()

# Option B: Async (submit + poll)
job = requests.post(f"{BASE_URL}/v1/solve", headers=headers, json=problem).json()
while job["status"] in ("PENDING", "RUNNING"):
    time.sleep(1)
    job = requests.get(f"{BASE_URL}/v1/jobs/{job['id']}", headers=headers).json()
result = job["result"]

# Print results
print(f"Routes: {len(result['routes'])}")
for route in result["routes"]:
    stops = [s for s in route["stops"] if s["type"] == "task"]
    print(f"  {route['vehicle_id']}: {len(stops)} tasks, "
          f"{route['total_distance_km']:.1f} km, "
          f"{route['total_duration_minutes']:.0f} min")

Authentication

API Key Scopes
ScopeDescription
solve:writeSubmit optimization jobs (sync and async)
optimize:writeAlias for solve:write (backward compatibility)
jobs:readRead/list jobs without solve permissions (optional for read-only monitoring keys)
analytics:readAccess route analytics
webhooks:writeManage webhook endpoints
reports:writeManage reports and schedules

Optimization

Async Jobs

For production use, submit jobs asynchronously. The solver runs in a background worker (Celery) and you poll for the result. The request body is the same OptimizeRequest format as the synchronous endpoint.

Distance Matrix

Matrix responses are cached (Redis + in-memory LRU). Tune withMATRIX_CACHE_ENABLED,MATRIX_CACHE_TTL_SECONDS, andMATRIX_CACHE_MEMORY_MAX_ENTRIES.

Re-optimization

Exports

Analytics

Webhooks

Subscribe to real-time event notifications. When an event occurs, a POST request is sent to your URL with the event payload. Deliveries are retried up to 3 times.

Supported Event Types
optimization.started
optimization.completed
optimization.failed
reoptimization.triggered
report.generated
route.completed
route.delayed
route.alert
task.completed
vehicle.unavailable
Signature Verification

If you provide a secret when creating the webhook, each delivery includes an X-Webhook-Signature header with an HMAC-SHA256 signature:

import hmac, hashlib

def verify_webhook(payload_bytes, signature_header, secret):
    expected = hmac.new(
        secret.encode(), payload_bytes, hashlib.sha256
    ).hexdigest()
    return hmac.compare_digest(
        signature_header.replace("sha256=", ""), expected
    )

Reports

Create report templates and schedules for automated report generation. Reports are generated as CSV/JSON artifacts that can be downloaded.

AI Chat

Multi-turn AI conversation interface. Supports multiple context types for building optimization problems, explaining results, analyzing anomalies, and getting recommendations.

LLM Tools

Standalone AI-powered tools for explaining results, detecting anomalies, and parsing natural language problem descriptions. Each call is stateless (not part of a conversation).

Billing & Usage

Usage is metered by node count (vehicles + tasks per job). Free-tier accounts get FREE_TIER_NODE_LIMIT nodes per month. Paid plans are billed via Stripe.

Portal Management

All portal endpoints require JWT authentication. User roles: owner (full access), admin (manage team & keys), manager (view & configure), analyst (read-only analytics), member (basic access).

Profile & Organization

API Keys

Team Management

Jobs (Portal)

ML Models

Audit Logs

GDPR Data Management

Health Checks

Error Handling

All errors follow a consistent structured format with error codes, human-readable messages, and actionable suggestions.

{
  "error": {
    "code": "CAPACITY_EXCEEDED",
    "message": "Total task demand (750 kg) exceeds total vehicle capacity (500 kg).",
    "details": {
      "total_demand_weight": 750,
      "total_capacity_weight": 500
    },
    "request_id": "req-a1b2c3",
    "timestamp": "2025-01-15T10:30:00Z",
    "suggestions": [
      "Add more vehicles or increase vehicle capacity.",
      "Remove lower-priority tasks to reduce total demand."
    ]
  }
}
Error Codes
CodeHTTPDescription
INVALID_REQUEST400Missing required fields, invalid values, or malformed JSON
DUPLICATE_ID400Duplicate vehicle or task IDs in the request
AUTHENTICATION_FAILED401Missing, invalid, or expired API key / JWT token
AUTHORIZATION_FAILED403Valid credentials but insufficient permissions or scopes
RESOURCE_NOT_FOUND404Job, webhook, report, or other resource not found
RATE_LIMIT_EXCEEDED429Too many requests. Implement exponential backoff
CAPACITY_EXCEEDED422Total demand exceeds capacity, or node/unit limits exceeded
SKILL_MISMATCH422Tasks require skills not available on any vehicle
TIME_WINDOW_CONFLICT422Time windows are impossible to satisfy
INFEASIBLE_SOLUTION422Solver could not find any feasible solution
TIMEOUT408Job exceeded maximum computation time
INTERNAL_ERROR500Unexpected server error
SERVICE_UNAVAILABLE503Database or dependency unavailable
Warning Types

Warnings are returned in the response warnings array. They indicate non-fatal issues:

TypeDescription
tight_time_windowTask has a very narrow time window that may be hard to satisfy
skill_mismatchTask requires skills not available on all vehicles
route_geometry_unavailableOSRM geometry could not be fetched (routing engine not configured or unreachable)
zone_restrictions_partialZone restrictions without task_ids or allowed_vehicles were ignored
capacity_tightTotal demand is close to total capacity
Rate Limiting

API requests are rate-limited per API key (default: 60 requests/minute). LLM endpoints have a separate limit (default: 20 requests/minute). When exceeded, you receive a 429 response with a RATE_LIMIT_EXCEEDED error code.

# Recommended retry strategy: exponential backoff
import time

def call_api_with_retry(func, max_retries=3):
    for attempt in range(max_retries):
        response = func()
        if response.status_code == 429:
            wait = 2 ** attempt  # 1s, 2s, 4s
            time.sleep(wait)
            continue
        return response
    raise Exception("Rate limit exceeded after retries")