{
  "openapi": "3.0.0",
  "info": {
    "title": "Routal API",
    "version": "7.15.9",
    "description": "# Routal API\n\nRoutal plans, dispatches and tracks the daily operations of delivery and field-service teams.\nThis is the same API that powers the [Routal Planner](https://planner.routal.com) and the driver\nand customer apps, so anything you can do in the product you can automate from here.\n\n## Use cases\n\n- **Last-mile delivery**: import orders from your e-commerce, WMS or ERP, optimize the daily plan,\n  dispatch routes to drivers and notify customers with live tracking.\n- **Field service, installation and maintenance**: schedule technician visits with time windows,\n  required skills and service durations.\n- **Distribution and B2B routes**: multi-stop routes with vehicle capacity, working hours, break times\n  and depot constraints.\n- **System integration**: keep your TMS, ERP or marketplace in sync through CRUD on plans, routes,\n  stops and vehicles.\n\n## Core concepts\n\n| Concept     | What it represents                                                               |\n| ----------- | -------------------------------------------------------------------------------- |\n| **Project** | A workspace: a city, a warehouse, a business line. Everything lives in a project. |\n| **Plan**    | The routes to execute on a given date or shift.                                  |\n| **Route**   | An ordered sequence of stops assigned to a driver and vehicle.                    |\n| **Stop**    | A delivery, pickup or visit. The unit of work a driver performs.                  |\n| **Task**    | A sub-operation inside a stop: deliver parcel A, collect a signature, scan an SKU.|\n| **Vehicle** | A driver and vehicle profile: capacity, skills, working hours, start point.       |\n\n## Authentication\n\nSend your API key as the `private_key` query parameter on every request. Generate and rotate keys in\n[Developer Settings](https://planner.routal.com/h/settings/developers/api-keys).\n\nKeys stay server-side. Do not embed them in mobile, web or single-page apps.\n\n## Versioning\n\nProduction is `v2`, with endpoints under `/v2/...`. Breaking changes ship as a new major version;\nadditive changes land on the current one.\n\n## Rate limiting\n\n**2,000 requests per minute** per authenticated credential, API key or user session. Over the limit you\nget a `429 Too Many Requests`: back off and retry with exponential backoff.\n\n## Support\n\nWrite to **support@routal.com** or use the in-app chat.\n",
    "termsOfService": "https://routal.com/terms",
    "contact": {
      "name": "Routal Support",
      "email": "support@routal.com",
      "url": "https://routal.com"
    },
    "license": {
      "name": "Routal Tech SL Commercial License",
      "url": "https://routal.com/terms"
    },
    "x-logo": {
      "url": "https://developers.routal.com/routal-logo.png",
      "altText": "Routal"
    }
  },
  "tags": [
    {
      "name": "Plan",
      "description": "The routes, stops and vehicles for one execution date. Most integrations start here: create a plan, push stops, optimize, retrieve routes."
    },
    {
      "name": "Route",
      "description": "An ordered sequence of stops assigned to a driver and vehicle. Inspect, modify or dispatch a single route once the plan is optimized."
    },
    {
      "name": "Stop",
      "description": "The unit of work: a delivery, pickup or visit. Create stops in bulk, geocode them automatically and move them between plans and routes before execution starts."
    },
    {
      "name": "Task",
      "description": "Sub-operations performed at a stop: deliver, pick up, scan, sign, photo. Use tasks when one visit covers several SKUs or discrete actions."
    },
    {
      "name": "Vehicle",
      "description": "Driver and vehicle profiles: capacity, skills, working hours and start/end locations. The optimizer needs them to build feasible routes."
    },
    {
      "name": "Connectors"
    }
  ],
  "security": [
    {
      "api_key": []
    }
  ],
  "paths": {
    "/v2/plans": {
      "get": {
        "summary": "List plans",
        "operationId": "getV2Plans",
        "description": "Returns a paginated list of the project's plans, filterable by date range, status or label.",
        "parameters": [
          {
            "description": "Search string",
            "name": "text",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "Sort columns",
            "name": "sort",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "Number of plans to skip",
            "name": "offset",
            "in": "query",
            "schema": {
              "type": "number",
              "default": 0
            }
          },
          {
            "description": "Number of plans to fetch",
            "name": "limit",
            "in": "query",
            "schema": {
              "type": "number",
              "default": 20
            }
          },
          {
            "description": "with deleted plans",
            "name": "deleted",
            "in": "query",
            "schema": {
              "type": "boolean",
              "default": false
            }
          },
          {
            "description": "Plan project ID",
            "name": "project_id",
            "in": "query",
            "schema": {
              "type": "string",
              "pattern": "^[0-9a-f]{24}"
            }
          },
          {
            "description": "Plan status",
            "name": "status",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "planning",
                "in_progress",
                "finished",
                "draft"
              ]
            }
          }
        ],
        "tags": [
          "Plan"
        ],
        "responses": {
          "200": {
            "description": "Paginated list of plans matching the filters.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PaginationPlans"
                }
              }
            }
          },
          "400": {
            "description": "Bad request: validation failed or the payload is malformed",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized: the API key or token is missing or invalid"
          },
          "403": {
            "description": "Forbidden: your plan or permissions do not allow this action",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "404": {
            "description": "Not found: the requested resource does not exist",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests: rate limit reached, retry with exponential backoff"
          },
          "500": {
            "description": "Internal server error. Write to support@routal.com if it persists"
          }
        },
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "cURL",
            "source": "curl -X GET 'https://api.routal.com/v2/plans?private_key=YOUR_KEY' \\\n  -H 'Content-Type: application/json'"
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript",
            "source": "const res = await fetch('https://api.routal.com/v2/plans?private_key=YOUR_KEY', {\n  method: 'GET',\n  headers: { 'Content-Type': 'application/json' },\n});\nconst data = await res.json();"
          },
          {
            "lang": "Python",
            "label": "Python",
            "source": "import requests\n\nres = requests.get(\n    'https://api.routal.com/v2/plans',\n    params={ 'private_key': 'YOUR_KEY' },\n    headers={'Content-Type': 'application/json'},\n)\nprint(res.json())"
          }
        ]
      }
    },
    "/v2/vehicles": {
      "get": {
        "summary": "List and search vehicles",
        "operationId": "getV2Vehicles",
        "description": "Returns a paginated, sortable list of the vehicles in a project. Sort with `sort=<field>:<asc|desc>`, for example `status:asc` or `label:desc`.",
        "parameters": [
          {
            "description": "Search string",
            "name": "text",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "Sort columns",
            "name": "sort",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "Number of vehicles to skip",
            "name": "offset",
            "in": "query",
            "schema": {
              "type": "number",
              "default": 0
            }
          },
          {
            "description": "Number of vehicles to fetch",
            "name": "limit",
            "in": "query",
            "schema": {
              "type": "number",
              "default": 20
            }
          },
          {
            "description": "with deleted vehicles",
            "name": "deleted",
            "in": "query",
            "schema": {
              "type": "boolean",
              "default": false
            }
          },
          {
            "description": "Retrieve only enabled vehicles",
            "name": "enabled",
            "in": "query",
            "schema": {
              "type": "boolean",
              "default": false
            }
          },
          {
            "description": "Vehicle project ID",
            "name": "project_id",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string",
              "pattern": "^[0-9a-f]{24}"
            }
          }
        ],
        "tags": [
          "Vehicle"
        ],
        "responses": {
          "200": {
            "description": "Paginated list of vehicles in the project.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PaginatedVehicles"
                }
              }
            }
          },
          "400": {
            "description": "Bad request: validation failed or the payload is malformed",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized: the API key or token is missing or invalid"
          },
          "403": {
            "description": "Forbidden: your plan or permissions do not allow this action",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "404": {
            "description": "Not found: the requested resource does not exist",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests: rate limit reached, retry with exponential backoff"
          },
          "500": {
            "description": "Internal server error. Write to support@routal.com if it persists"
          }
        },
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "cURL",
            "source": "curl -X GET 'https://api.routal.com/v2/vehicles?private_key=YOUR_KEY&project_id=...' \\\n  -H 'Content-Type: application/json'"
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript",
            "source": "const res = await fetch('https://api.routal.com/v2/vehicles?private_key=YOUR_KEY&project_id=...', {\n  method: 'GET',\n  headers: { 'Content-Type': 'application/json' },\n});\nconst data = await res.json();"
          },
          {
            "lang": "Python",
            "label": "Python",
            "source": "import requests\n\nres = requests.get(\n    'https://api.routal.com/v2/vehicles',\n    params={ 'private_key': 'YOUR_KEY', 'project_id': '...' },\n    headers={'Content-Type': 'application/json'},\n)\nprint(res.json())"
          }
        ]
      }
    },
    "/v2/connectors/catalog": {
      "get": {
        "summary": "List the connector catalog available to Routal.",
        "operationId": "getV2ConnectorsCatalog",
        "tags": [
          "Connectors"
        ],
        "responses": {
          "default": {
            "description": "Successful",
            "content": {
              "application/json": {
                "schema": {
                  "type": "string"
                }
              }
            }
          }
        },
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "cURL",
            "source": "curl -X GET 'https://api.routal.com/v2/connectors/catalog?private_key=YOUR_KEY' \\\n  -H 'Content-Type: application/json'"
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript",
            "source": "const res = await fetch('https://api.routal.com/v2/connectors/catalog?private_key=YOUR_KEY', {\n  method: 'GET',\n  headers: { 'Content-Type': 'application/json' },\n});\nconst data = await res.json();"
          },
          {
            "lang": "Python",
            "label": "Python",
            "source": "import requests\n\nres = requests.get(\n    'https://api.routal.com/v2/connectors/catalog',\n    params={ 'private_key': 'YOUR_KEY' },\n    headers={'Content-Type': 'application/json'},\n)\nprint(res.json())"
          }
        ]
      }
    },
    "/v2/connectors/connections": {
      "get": {
        "summary": "List the connections a project has configured.",
        "operationId": "getV2ConnectorsConnections",
        "parameters": [
          {
            "name": "project_id",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "tags": [
          "Connectors"
        ],
        "responses": {
          "default": {
            "description": "Successful",
            "content": {
              "application/json": {
                "schema": {
                  "type": "string"
                }
              }
            }
          }
        },
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "cURL",
            "source": "curl -X GET 'https://api.routal.com/v2/connectors/connections?private_key=YOUR_KEY&project_id=...' \\\n  -H 'Content-Type: application/json'"
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript",
            "source": "const res = await fetch('https://api.routal.com/v2/connectors/connections?private_key=YOUR_KEY&project_id=...', {\n  method: 'GET',\n  headers: { 'Content-Type': 'application/json' },\n});\nconst data = await res.json();"
          },
          {
            "lang": "Python",
            "label": "Python",
            "source": "import requests\n\nres = requests.get(\n    'https://api.routal.com/v2/connectors/connections',\n    params={ 'private_key': 'YOUR_KEY', 'project_id': '...' },\n    headers={'Content-Type': 'application/json'},\n)\nprint(res.json())"
          }
        ]
      },
      "post": {
        "summary": "Create a new connection. Runs TestConnection first — refuses to persist unless it passes.",
        "operationId": "postV2ConnectorsConnections",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/Model5"
              }
            }
          }
        },
        "tags": [
          "Connectors"
        ],
        "responses": {
          "default": {
            "description": "Successful",
            "content": {
              "application/json": {
                "schema": {
                  "type": "string"
                }
              }
            }
          }
        },
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "cURL",
            "source": "curl -X POST 'https://api.routal.com/v2/connectors/connections?private_key=YOUR_KEY' \\\n  -H 'Content-Type: application/json' \\\n  -d '{ }'"
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript",
            "source": "const res = await fetch('https://api.routal.com/v2/connectors/connections?private_key=YOUR_KEY', {\n  method: 'POST',\n  headers: { 'Content-Type': 'application/json' },\n  body: JSON.stringify({ /* … */ }),\n});\nconst data = await res.json();"
          },
          {
            "lang": "Python",
            "label": "Python",
            "source": "import requests\n\nres = requests.post(\n    'https://api.routal.com/v2/connectors/connections',\n    params={ 'private_key': 'YOUR_KEY' },\n    headers={'Content-Type': 'application/json'},\n    json={},\n)\nprint(res.json())"
          }
        ]
      }
    },
    "/v2/plan/{id}": {
      "get": {
        "summary": "Retrieve a plan",
        "operationId": "getV2PlanId",
        "description": "Returns the full plan: its routes, stops, vehicles and metadata.\n\nFor lightweight polling use `GET /v2/plan/{id}/routes` or `GET /v2/plan/{id}/stops`.",
        "parameters": [
          {
            "description": "Plan ID",
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "pattern": "^[0-9a-f]{24}"
            }
          }
        ],
        "tags": [
          "Plan"
        ],
        "responses": {
          "200": {
            "description": "The full plan with its routes, stops and vehicles.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FullPlanData"
                }
              }
            }
          },
          "400": {
            "description": "Bad request: validation failed or the payload is malformed",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized: the API key or token is missing or invalid"
          },
          "403": {
            "description": "Forbidden: your plan or permissions do not allow this action",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "404": {
            "description": "Not found: the requested resource does not exist",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests: rate limit reached, retry with exponential backoff"
          },
          "500": {
            "description": "Internal server error. Write to support@routal.com if it persists"
          }
        },
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "cURL",
            "source": "curl -X GET 'https://api.routal.com/v2/plan/{id}?private_key=YOUR_KEY' \\\n  -H 'Content-Type: application/json'"
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript",
            "source": "const res = await fetch('https://api.routal.com/v2/plan/{id}?private_key=YOUR_KEY', {\n  method: 'GET',\n  headers: { 'Content-Type': 'application/json' },\n});\nconst data = await res.json();"
          },
          {
            "lang": "Python",
            "label": "Python",
            "source": "import requests\n\nres = requests.get(\n    'https://api.routal.com/v2/plan/{id}',\n    params={ 'private_key': 'YOUR_KEY' },\n    headers={'Content-Type': 'application/json'},\n)\nprint(res.json())"
          }
        ]
      },
      "delete": {
        "summary": "Delete a plan",
        "operationId": "deleteV2PlanId",
        "description": "Deletes the plan together with **all its routes and stops**. This cannot be undone.\n\nCompleted plans are the source of truth for proof of delivery, KPIs and analytics in Routal, so archive them on your side instead of deleting them.",
        "parameters": [
          {
            "description": "Plan ID",
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "pattern": "^[0-9a-f]{24}"
            }
          }
        ],
        "tags": [
          "Plan"
        ],
        "responses": {
          "200": {
            "description": "The deleted plan.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FullPlanData"
                }
              }
            }
          },
          "400": {
            "description": "Bad request: validation failed or the payload is malformed",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized: the API key or token is missing or invalid"
          },
          "403": {
            "description": "Forbidden: your plan or permissions do not allow this action",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "404": {
            "description": "Not found: the requested resource does not exist",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests: rate limit reached, retry with exponential backoff"
          },
          "500": {
            "description": "Internal server error. Write to support@routal.com if it persists"
          }
        },
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "cURL",
            "source": "curl -X DELETE 'https://api.routal.com/v2/plan/{id}?private_key=YOUR_KEY' \\\n  -H 'Content-Type: application/json'"
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript",
            "source": "const res = await fetch('https://api.routal.com/v2/plan/{id}?private_key=YOUR_KEY', {\n  method: 'DELETE',\n  headers: { 'Content-Type': 'application/json' },\n});\nconst data = await res.json();"
          },
          {
            "lang": "Python",
            "label": "Python",
            "source": "import requests\n\nres = requests.delete(\n    'https://api.routal.com/v2/plan/{id}',\n    params={ 'private_key': 'YOUR_KEY' },\n    headers={'Content-Type': 'application/json'},\n)\nprint(res.json())"
          }
        ]
      },
      "put": {
        "summary": "Update a plan",
        "operationId": "putV2PlanId",
        "description": "Updates plan-level attributes: label, execution date, status or optimizer configuration. Stops and vehicles are left untouched. Manage them through `/v2/stops` and `/v2/vehicles`.\n\n> Plan-level GeoFences are deprecated. They are inherited from the project and cannot be modified here.",
        "parameters": [
          {
            "description": "Plan ID",
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "pattern": "^[0-9a-f]{24}"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/PlanData1"
              }
            }
          }
        },
        "tags": [
          "Plan"
        ],
        "responses": {
          "200": {
            "description": "The updated plan with its routes, stops and vehicles.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FullPlanData"
                }
              }
            }
          },
          "400": {
            "description": "Bad request: validation failed or the payload is malformed",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized: the API key or token is missing or invalid"
          },
          "403": {
            "description": "Forbidden: your plan or permissions do not allow this action",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "404": {
            "description": "Not found: the requested resource does not exist",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests: rate limit reached, retry with exponential backoff"
          },
          "500": {
            "description": "Internal server error. Write to support@routal.com if it persists"
          }
        },
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "cURL",
            "source": "curl -X PUT 'https://api.routal.com/v2/plan/{id}?private_key=YOUR_KEY' \\\n  -H 'Content-Type: application/json' \\\n  -d '{ }'"
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript",
            "source": "const res = await fetch('https://api.routal.com/v2/plan/{id}?private_key=YOUR_KEY', {\n  method: 'PUT',\n  headers: { 'Content-Type': 'application/json' },\n  body: JSON.stringify({ /* … */ }),\n});\nconst data = await res.json();"
          },
          {
            "lang": "Python",
            "label": "Python",
            "source": "import requests\n\nres = requests.put(\n    'https://api.routal.com/v2/plan/{id}',\n    params={ 'private_key': 'YOUR_KEY' },\n    headers={'Content-Type': 'application/json'},\n    json={},\n)\nprint(res.json())"
          }
        ]
      }
    },
    "/v2/plans/routes": {
      "get": {
        "summary": "List routes of all plans for a day",
        "operationId": "getV2PlansRoutes",
        "description": "Returns every route across the project's plans whose execution date matches the requested day, each with its ordered stops and assigned vehicle.\n\nOne request instead of a per-plan fan-out for a day-level dispatch view.",
        "parameters": [
          {
            "description": "Project ID",
            "name": "project_id",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string",
              "pattern": "^[0-9a-f]{24}"
            }
          },
          {
            "description": "Execution day",
            "name": "date",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string",
              "format": "date"
            }
          },
          {
            "description": "Optional explicit start of the execution day window. Defaults to 00:00 UTC of \"date\".",
            "name": "start",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "date"
            }
          },
          {
            "description": "Optional explicit end of the execution day window. Defaults to 23:59:59.999 UTC of \"date\".",
            "name": "end",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "date"
            }
          }
        ],
        "tags": [
          "Plan"
        ],
        "responses": {
          "200": {
            "description": "All routes across the project's plans for a given execution day.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RoutesWithStops"
                }
              }
            }
          },
          "400": {
            "description": "Bad request: validation failed or the payload is malformed",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized: the API key or token is missing or invalid"
          },
          "403": {
            "description": "Forbidden: your plan or permissions do not allow this action",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "404": {
            "description": "Not found: the requested resource does not exist",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests: rate limit reached, retry with exponential backoff"
          },
          "500": {
            "description": "Internal server error. Write to support@routal.com if it persists"
          }
        },
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "cURL",
            "source": "curl -X GET 'https://api.routal.com/v2/plans/routes?private_key=YOUR_KEY&project_id=...&date=...' \\\n  -H 'Content-Type: application/json'"
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript",
            "source": "const res = await fetch('https://api.routal.com/v2/plans/routes?private_key=YOUR_KEY&project_id=...&date=...', {\n  method: 'GET',\n  headers: { 'Content-Type': 'application/json' },\n});\nconst data = await res.json();"
          },
          {
            "lang": "Python",
            "label": "Python",
            "source": "import requests\n\nres = requests.get(\n    'https://api.routal.com/v2/plans/routes',\n    params={ 'private_key': 'YOUR_KEY', 'project_id': '...', 'date': '...' },\n    headers={'Content-Type': 'application/json'},\n)\nprint(res.json())"
          }
        ]
      }
    },
    "/v2/plans/stops": {
      "get": {
        "summary": "List stops of all plans for a day",
        "operationId": "getV2PlansStops",
        "description": "Returns every stop across the project's plans whose execution date matches the requested day, with its proof-of-delivery payload.\n\nOne request instead of a per-plan fan-out. Responses can be large, so paginate on your side when displaying them.",
        "parameters": [
          {
            "description": "Project ID",
            "name": "project_id",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string",
              "pattern": "^[0-9a-f]{24}"
            }
          },
          {
            "description": "Execution day",
            "name": "date",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string",
              "format": "date"
            }
          },
          {
            "description": "Optional explicit start of the execution day window. Defaults to 00:00 UTC of \"date\".",
            "name": "start",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "date"
            }
          },
          {
            "description": "Optional explicit end of the execution day window. Defaults to 23:59:59.999 UTC of \"date\".",
            "name": "end",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "date"
            }
          }
        ],
        "tags": [
          "Plan"
        ],
        "responses": {
          "200": {
            "description": "All stops across the project's plans for a given execution day.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/StopsData"
                }
              }
            }
          },
          "400": {
            "description": "Bad request: validation failed or the payload is malformed",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized: the API key or token is missing or invalid"
          },
          "403": {
            "description": "Forbidden: your plan or permissions do not allow this action",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "404": {
            "description": "Not found: the requested resource does not exist",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests: rate limit reached, retry with exponential backoff"
          },
          "500": {
            "description": "Internal server error. Write to support@routal.com if it persists"
          }
        },
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "cURL",
            "source": "curl -X GET 'https://api.routal.com/v2/plans/stops?private_key=YOUR_KEY&project_id=...&date=...' \\\n  -H 'Content-Type: application/json'"
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript",
            "source": "const res = await fetch('https://api.routal.com/v2/plans/stops?private_key=YOUR_KEY&project_id=...&date=...', {\n  method: 'GET',\n  headers: { 'Content-Type': 'application/json' },\n});\nconst data = await res.json();"
          },
          {
            "lang": "Python",
            "label": "Python",
            "source": "import requests\n\nres = requests.get(\n    'https://api.routal.com/v2/plans/stops',\n    params={ 'private_key': 'YOUR_KEY', 'project_id': '...', 'date': '...' },\n    headers={'Content-Type': 'application/json'},\n)\nprint(res.json())"
          }
        ]
      }
    },
    "/v2/route/{id}": {
      "get": {
        "summary": "Retrieve a route",
        "operationId": "getV2RouteId",
        "description": "Returns a route with its ordered stops, assigned vehicle, total distance, total duration and ETAs.\n\nCheaper than pulling the full plan when you only need one route.",
        "parameters": [
          {
            "description": "Route ID",
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "pattern": "^[0-9a-f]{24}"
            }
          }
        ],
        "tags": [
          "Route"
        ],
        "responses": {
          "200": {
            "description": "The route with its ordered stops and vehicle.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Route_With_Stops"
                }
              }
            }
          },
          "400": {
            "description": "Bad request: validation failed or the payload is malformed",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized: the API key or token is missing or invalid"
          },
          "403": {
            "description": "Forbidden: your plan or permissions do not allow this action",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "404": {
            "description": "Not found: the requested resource does not exist",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests: rate limit reached, retry with exponential backoff"
          },
          "500": {
            "description": "Internal server error. Write to support@routal.com if it persists"
          }
        },
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "cURL",
            "source": "curl -X GET 'https://api.routal.com/v2/route/{id}?private_key=YOUR_KEY' \\\n  -H 'Content-Type: application/json'"
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript",
            "source": "const res = await fetch('https://api.routal.com/v2/route/{id}?private_key=YOUR_KEY', {\n  method: 'GET',\n  headers: { 'Content-Type': 'application/json' },\n});\nconst data = await res.json();"
          },
          {
            "lang": "Python",
            "label": "Python",
            "source": "import requests\n\nres = requests.get(\n    'https://api.routal.com/v2/route/{id}',\n    params={ 'private_key': 'YOUR_KEY' },\n    headers={'Content-Type': 'application/json'},\n)\nprint(res.json())"
          }
        ]
      },
      "delete": {
        "summary": "Delete a route",
        "operationId": "deleteV2RouteId",
        "description": "Deletes the route from its plan. Its stops are not deleted: they return to the plan as unassigned, ready to be moved to another route or re-optimized.\n\nUse it to drop a driver shift from a plan, a sick leave for example, without losing their orders.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "tags": [
          "Route"
        ],
        "responses": {
          "200": {
            "description": "The deleted route; its stops are returned to the plan as unassigned.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Route_With_Stops"
                }
              }
            }
          },
          "400": {
            "description": "Bad request: validation failed or the payload is malformed",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized: the API key or token is missing or invalid"
          },
          "403": {
            "description": "Forbidden: your plan or permissions do not allow this action",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "404": {
            "description": "Not found: the requested resource does not exist",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests: rate limit reached, retry with exponential backoff"
          },
          "500": {
            "description": "Internal server error. Write to support@routal.com if it persists"
          }
        },
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "cURL",
            "source": "curl -X DELETE 'https://api.routal.com/v2/route/{id}?private_key=YOUR_KEY' \\\n  -H 'Content-Type: application/json'"
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript",
            "source": "const res = await fetch('https://api.routal.com/v2/route/{id}?private_key=YOUR_KEY', {\n  method: 'DELETE',\n  headers: { 'Content-Type': 'application/json' },\n});\nconst data = await res.json();"
          },
          {
            "lang": "Python",
            "label": "Python",
            "source": "import requests\n\nres = requests.delete(\n    'https://api.routal.com/v2/route/{id}',\n    params={ 'private_key': 'YOUR_KEY' },\n    headers={'Content-Type': 'application/json'},\n)\nprint(res.json())"
          }
        ]
      },
      "put": {
        "summary": "Update a route",
        "operationId": "putV2RouteId",
        "description": "Updates route-level attributes: status, assigned vehicle or driver, label and constraints. The stop sequence is not touched, use `PUT /v2/route/{id}/stops` to reorder stops.\n\nChanging the vehicle of a route already dispatched rebroadcasts it to the new driver.",
        "parameters": [
          {
            "description": "Route ID",
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "pattern": "^[0-9a-f]{24}"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/RouteEdit"
              }
            }
          }
        },
        "tags": [
          "Route"
        ],
        "responses": {
          "200": {
            "description": "The updated route with its ordered stops.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Route_With_Stops"
                }
              }
            }
          },
          "400": {
            "description": "Bad request: validation failed or the payload is malformed",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized: the API key or token is missing or invalid"
          },
          "403": {
            "description": "Forbidden: your plan or permissions do not allow this action",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "404": {
            "description": "Not found: the requested resource does not exist",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests: rate limit reached, retry with exponential backoff"
          },
          "500": {
            "description": "Internal server error. Write to support@routal.com if it persists"
          }
        },
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "cURL",
            "source": "curl -X PUT 'https://api.routal.com/v2/route/{id}?private_key=YOUR_KEY' \\\n  -H 'Content-Type: application/json' \\\n  -d '{ }'"
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript",
            "source": "const res = await fetch('https://api.routal.com/v2/route/{id}?private_key=YOUR_KEY', {\n  method: 'PUT',\n  headers: { 'Content-Type': 'application/json' },\n  body: JSON.stringify({ /* … */ }),\n});\nconst data = await res.json();"
          },
          {
            "lang": "Python",
            "label": "Python",
            "source": "import requests\n\nres = requests.put(\n    'https://api.routal.com/v2/route/{id}',\n    params={ 'private_key': 'YOUR_KEY' },\n    headers={'Content-Type': 'application/json'},\n    json={},\n)\nprint(res.json())"
          }
        ]
      }
    },
    "/v2/stop/{stop_id}": {
      "get": {
        "summary": "Retrieve a stop",
        "operationId": "getV2StopStop_id",
        "description": "Returns a stop with its full execution payload: tasks, status, time windows, customer details, geocoded location, proof of delivery (signature, photos, scans), notes and timestamps.",
        "parameters": [
          {
            "description": "Stop ID",
            "name": "stop_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "pattern": "^[0-9a-f]{24}"
            }
          }
        ],
        "tags": [
          "Stop"
        ],
        "responses": {
          "200": {
            "description": "The stop with its tasks and proof-of-delivery payload.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/StopData"
                }
              }
            }
          },
          "400": {
            "description": "Bad request: validation failed or the payload is malformed",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized: the API key or token is missing or invalid"
          },
          "403": {
            "description": "Forbidden: your plan or permissions do not allow this action",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "404": {
            "description": "Not found: the requested resource does not exist",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests: rate limit reached, retry with exponential backoff"
          },
          "500": {
            "description": "Internal server error. Write to support@routal.com if it persists"
          }
        },
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "cURL",
            "source": "curl -X GET 'https://api.routal.com/v2/stop/{stop_id}?private_key=YOUR_KEY' \\\n  -H 'Content-Type: application/json'"
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript",
            "source": "const res = await fetch('https://api.routal.com/v2/stop/{stop_id}?private_key=YOUR_KEY', {\n  method: 'GET',\n  headers: { 'Content-Type': 'application/json' },\n});\nconst data = await res.json();"
          },
          {
            "lang": "Python",
            "label": "Python",
            "source": "import requests\n\nres = requests.get(\n    'https://api.routal.com/v2/stop/{stop_id}',\n    params={ 'private_key': 'YOUR_KEY' },\n    headers={'Content-Type': 'application/json'},\n)\nprint(res.json())"
          }
        ]
      },
      "put": {
        "summary": "Update a stop",
        "operationId": "putV2StopStop_id",
        "description": "Updates a stop: customer data, time windows, service duration, required skills or the tasks attached to it.\n\n**Warning.** Updating a stop that was already executed (delivered, failed, cancelled) rewrites its historical record. Do it only to correct data; model a retry as a new stop.",
        "parameters": [
          {
            "description": "Stop ID",
            "name": "stop_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "pattern": "^[0-9a-f]{24}"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/Stop1"
              }
            }
          }
        },
        "tags": [
          "Stop"
        ],
        "responses": {
          "200": {
            "description": "The updated stop.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/StopData"
                }
              }
            }
          },
          "400": {
            "description": "Bad request: validation failed or the payload is malformed",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized: the API key or token is missing or invalid"
          },
          "403": {
            "description": "Forbidden: your plan or permissions do not allow this action",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "404": {
            "description": "Not found: the requested resource does not exist",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests: rate limit reached, retry with exponential backoff"
          },
          "500": {
            "description": "Internal server error. Write to support@routal.com if it persists"
          }
        },
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "cURL",
            "source": "curl -X PUT 'https://api.routal.com/v2/stop/{stop_id}?private_key=YOUR_KEY' \\\n  -H 'Content-Type: application/json' \\\n  -d '{ }'"
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript",
            "source": "const res = await fetch('https://api.routal.com/v2/stop/{stop_id}?private_key=YOUR_KEY', {\n  method: 'PUT',\n  headers: { 'Content-Type': 'application/json' },\n  body: JSON.stringify({ /* … */ }),\n});\nconst data = await res.json();"
          },
          {
            "lang": "Python",
            "label": "Python",
            "source": "import requests\n\nres = requests.put(\n    'https://api.routal.com/v2/stop/{stop_id}',\n    params={ 'private_key': 'YOUR_KEY' },\n    headers={'Content-Type': 'application/json'},\n    json={},\n)\nprint(res.json())"
          }
        ]
      }
    },
    "/v2/vehicle/{vehicle_id}": {
      "get": {
        "summary": "Retrieve a vehicle",
        "operationId": "getV2VehicleVehicle_id",
        "description": "Returns the full vehicle profile: capacity (weight / volume / units), skills, working hours, break windows, start / end locations, transport mode, driver assignment and traffic options.",
        "parameters": [
          {
            "description": "Vehicle ID",
            "name": "vehicle_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "pattern": "^[0-9a-f]{24}"
            }
          }
        ],
        "tags": [
          "Vehicle"
        ],
        "responses": {
          "200": {
            "description": "The vehicle profile.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/VehicleData"
                }
              }
            }
          },
          "400": {
            "description": "Bad request: validation failed or the payload is malformed",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized: the API key or token is missing or invalid"
          },
          "403": {
            "description": "Forbidden: your plan or permissions do not allow this action",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "404": {
            "description": "Not found: the requested resource does not exist",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests: rate limit reached, retry with exponential backoff"
          },
          "500": {
            "description": "Internal server error. Write to support@routal.com if it persists"
          }
        },
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "cURL",
            "source": "curl -X GET 'https://api.routal.com/v2/vehicle/{vehicle_id}?private_key=YOUR_KEY' \\\n  -H 'Content-Type: application/json'"
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript",
            "source": "const res = await fetch('https://api.routal.com/v2/vehicle/{vehicle_id}?private_key=YOUR_KEY', {\n  method: 'GET',\n  headers: { 'Content-Type': 'application/json' },\n});\nconst data = await res.json();"
          },
          {
            "lang": "Python",
            "label": "Python",
            "source": "import requests\n\nres = requests.get(\n    'https://api.routal.com/v2/vehicle/{vehicle_id}',\n    params={ 'private_key': 'YOUR_KEY' },\n    headers={'Content-Type': 'application/json'},\n)\nprint(res.json())"
          }
        ]
      },
      "delete": {
        "summary": "Delete a vehicle",
        "operationId": "deleteV2VehicleVehicle_id",
        "description": "Deletes a vehicle from its project. Routes it was assigned to are kept, without a vehicle: reassign them with `PUT /v2/route/{id}` or re-run the optimizer for the plan. Deleting a vehicle also frees a vehicle slot in your subscription. This cannot be undone.",
        "parameters": [
          {
            "description": "Vehicle ID",
            "name": "vehicle_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "pattern": "^[0-9a-f]{24}"
            }
          }
        ],
        "tags": [
          "Vehicle"
        ],
        "responses": {
          "200": {
            "description": "The deleted vehicle.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/VehicleData"
                }
              }
            }
          },
          "400": {
            "description": "Bad request: validation failed or the payload is malformed",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized: the API key or token is missing or invalid"
          },
          "403": {
            "description": "Forbidden: your plan or permissions do not allow this action",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "404": {
            "description": "Not found: the requested resource does not exist",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests: rate limit reached, retry with exponential backoff"
          },
          "500": {
            "description": "Internal server error. Write to support@routal.com if it persists"
          }
        },
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "cURL",
            "source": "curl -X DELETE 'https://api.routal.com/v2/vehicle/{vehicle_id}?private_key=YOUR_KEY' \\\n  -H 'Content-Type: application/json'"
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript",
            "source": "const res = await fetch('https://api.routal.com/v2/vehicle/{vehicle_id}?private_key=YOUR_KEY', {\n  method: 'DELETE',\n  headers: { 'Content-Type': 'application/json' },\n});\nconst data = await res.json();"
          },
          {
            "lang": "Python",
            "label": "Python",
            "source": "import requests\n\nres = requests.delete(\n    'https://api.routal.com/v2/vehicle/{vehicle_id}',\n    params={ 'private_key': 'YOUR_KEY' },\n    headers={'Content-Type': 'application/json'},\n)\nprint(res.json())"
          }
        ]
      },
      "put": {
        "summary": "Update a vehicle",
        "operationId": "putV2VehicleVehicle_id",
        "description": "Updates the vehicle profile: capacity, skills, working hours, breaks, start and end locations, assigned driver and traffic options.\n\nChanges apply to future optimizations. Routes already optimized or dispatched keep their shape until you re-run the optimizer for their plan.",
        "parameters": [
          {
            "description": "Vehicle ID",
            "name": "vehicle_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "pattern": "^[0-9a-f]{24}"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/Vehicle1"
              }
            }
          }
        },
        "tags": [
          "Vehicle"
        ],
        "responses": {
          "200": {
            "description": "The updated vehicle profile.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/VehicleData"
                }
              }
            }
          },
          "400": {
            "description": "Bad request: validation failed or the payload is malformed",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized: the API key or token is missing or invalid"
          },
          "403": {
            "description": "Forbidden: your plan or permissions do not allow this action",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "404": {
            "description": "Not found: the requested resource does not exist",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests: rate limit reached, retry with exponential backoff"
          },
          "500": {
            "description": "Internal server error. Write to support@routal.com if it persists"
          }
        },
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "cURL",
            "source": "curl -X PUT 'https://api.routal.com/v2/vehicle/{vehicle_id}?private_key=YOUR_KEY' \\\n  -H 'Content-Type: application/json' \\\n  -d '{ }'"
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript",
            "source": "const res = await fetch('https://api.routal.com/v2/vehicle/{vehicle_id}?private_key=YOUR_KEY', {\n  method: 'PUT',\n  headers: { 'Content-Type': 'application/json' },\n  body: JSON.stringify({ /* … */ }),\n});\nconst data = await res.json();"
          },
          {
            "lang": "Python",
            "label": "Python",
            "source": "import requests\n\nres = requests.put(\n    'https://api.routal.com/v2/vehicle/{vehicle_id}',\n    params={ 'private_key': 'YOUR_KEY' },\n    headers={'Content-Type': 'application/json'},\n    json={},\n)\nprint(res.json())"
          }
        ]
      }
    },
    "/v2/plan/{id}/stops": {
      "get": {
        "summary": "List stops of a plan",
        "operationId": "getV2PlanIdStops",
        "description": "Returns every stop in the plan with its execution payload: tasks, custom fields, timestamps and the `reports` captured at the visit (signature, photos, comments and per-task results such as scanned barcodes).\n\nUse it to export delivery data back to your TMS. Responses can be large, so paginate on your side when displaying them.",
        "parameters": [
          {
            "description": "Plan ID",
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "pattern": "^[0-9a-f]{24}"
            }
          }
        ],
        "tags": [
          "Plan"
        ],
        "responses": {
          "200": {
            "description": "All stops belonging to the plan, with their proof-of-delivery payload.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/StopsData"
                }
              }
            }
          },
          "400": {
            "description": "Bad request: validation failed or the payload is malformed",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized: the API key or token is missing or invalid"
          },
          "403": {
            "description": "Forbidden: your plan or permissions do not allow this action",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "404": {
            "description": "Not found: the requested resource does not exist",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests: rate limit reached, retry with exponential backoff"
          },
          "500": {
            "description": "Internal server error. Write to support@routal.com if it persists"
          }
        },
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "cURL",
            "source": "curl -X GET 'https://api.routal.com/v2/plan/{id}/stops?private_key=YOUR_KEY' \\\n  -H 'Content-Type: application/json'"
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript",
            "source": "const res = await fetch('https://api.routal.com/v2/plan/{id}/stops?private_key=YOUR_KEY', {\n  method: 'GET',\n  headers: { 'Content-Type': 'application/json' },\n});\nconst data = await res.json();"
          },
          {
            "lang": "Python",
            "label": "Python",
            "source": "import requests\n\nres = requests.get(\n    'https://api.routal.com/v2/plan/{id}/stops',\n    params={ 'private_key': 'YOUR_KEY' },\n    headers={'Content-Type': 'application/json'},\n)\nprint(res.json())"
          }
        ]
      }
    },
    "/v2/plan/{id}/routes": {
      "get": {
        "summary": "List routes of a plan",
        "operationId": "getV2PlanIdRoutes",
        "description": "Returns every route in the plan with its ordered stops, assigned vehicle, total distance, total duration and estimated arrival times.\n\nRead it after `POST /v2/plan/{id}/optimize` to get the optimized assignment, or during execution to build a live dispatch view.",
        "parameters": [
          {
            "description": "Plan ID",
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "pattern": "^[0-9a-f]{24}"
            }
          }
        ],
        "tags": [
          "Plan"
        ],
        "responses": {
          "200": {
            "description": "All routes belonging to the plan, each with its ordered stop list.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RoutesWithStops"
                }
              }
            }
          },
          "400": {
            "description": "Bad request: validation failed or the payload is malformed",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized: the API key or token is missing or invalid"
          },
          "403": {
            "description": "Forbidden: your plan or permissions do not allow this action",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "404": {
            "description": "Not found: the requested resource does not exist",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests: rate limit reached, retry with exponential backoff"
          },
          "500": {
            "description": "Internal server error. Write to support@routal.com if it persists"
          }
        },
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "cURL",
            "source": "curl -X GET 'https://api.routal.com/v2/plan/{id}/routes?private_key=YOUR_KEY' \\\n  -H 'Content-Type: application/json'"
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript",
            "source": "const res = await fetch('https://api.routal.com/v2/plan/{id}/routes?private_key=YOUR_KEY', {\n  method: 'GET',\n  headers: { 'Content-Type': 'application/json' },\n});\nconst data = await res.json();"
          },
          {
            "lang": "Python",
            "label": "Python",
            "source": "import requests\n\nres = requests.get(\n    'https://api.routal.com/v2/plan/{id}/routes',\n    params={ 'private_key': 'YOUR_KEY' },\n    headers={'Content-Type': 'application/json'},\n)\nprint(res.json())"
          }
        ]
      }
    },
    "/v2/vehicles/{id}/badges": {
      "get": {
        "summary": "Retrieve achievement badges for a vehicle",
        "operationId": "getV2VehiclesIdBadges",
        "parameters": [
          {
            "description": "Vehicle ID",
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "pattern": "^[0-9a-f]{24}"
            }
          }
        ],
        "tags": [
          "Vehicle"
        ],
        "responses": {
          "default": {
            "description": "Successful",
            "content": {
              "application/json": {
                "schema": {
                  "type": "string"
                }
              }
            }
          }
        },
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "cURL",
            "source": "curl -X GET 'https://api.routal.com/v2/vehicles/{id}/badges?private_key=YOUR_KEY' \\\n  -H 'Content-Type: application/json'"
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript",
            "source": "const res = await fetch('https://api.routal.com/v2/vehicles/{id}/badges?private_key=YOUR_KEY', {\n  method: 'GET',\n  headers: { 'Content-Type': 'application/json' },\n});\nconst data = await res.json();"
          },
          {
            "lang": "Python",
            "label": "Python",
            "source": "import requests\n\nres = requests.get(\n    'https://api.routal.com/v2/vehicles/{id}/badges',\n    params={ 'private_key': 'YOUR_KEY' },\n    headers={'Content-Type': 'application/json'},\n)\nprint(res.json())"
          }
        ]
      }
    },
    "/v2/vehicles/{id}/ratings": {
      "get": {
        "summary": "Retrieve driver + customer ratings and comments for a vehicle",
        "operationId": "getV2VehiclesIdRatings",
        "parameters": [
          {
            "description": "Vehicle ID",
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "pattern": "^[0-9a-f]{24}"
            }
          },
          {
            "description": "Period start (filters on survey.created_at)",
            "name": "start",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "format": "date-time"
            }
          },
          {
            "description": "Period end (filters on survey.created_at)",
            "name": "end",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "format": "date-time"
            }
          }
        ],
        "tags": [
          "Vehicle"
        ],
        "responses": {
          "default": {
            "description": "Successful",
            "content": {
              "application/json": {
                "schema": {
                  "type": "string"
                }
              }
            }
          }
        },
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "cURL",
            "source": "curl -X GET 'https://api.routal.com/v2/vehicles/{id}/ratings?private_key=YOUR_KEY' \\\n  -H 'Content-Type: application/json'"
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript",
            "source": "const res = await fetch('https://api.routal.com/v2/vehicles/{id}/ratings?private_key=YOUR_KEY', {\n  method: 'GET',\n  headers: { 'Content-Type': 'application/json' },\n});\nconst data = await res.json();"
          },
          {
            "lang": "Python",
            "label": "Python",
            "source": "import requests\n\nres = requests.get(\n    'https://api.routal.com/v2/vehicles/{id}/ratings',\n    params={ 'private_key': 'YOUR_KEY' },\n    headers={'Content-Type': 'application/json'},\n)\nprint(res.json())"
          }
        ]
      }
    },
    "/v2/plan": {
      "post": {
        "summary": "Create a plan",
        "operationId": "postV2Plan",
        "description": "Creates a plan inside a project. The plan is the container for the routes and stops executed on a given date.\n\n**Recommended integration flow**\n1. `POST /v2/plan?project_id=...` creates the plan for the execution date.\n2. `POST /v2/stops?plan_id=...` (or `POST /v2/stops/geocode`) pushes the day's orders.\n3. `POST /v2/plan/{id}/optimize` builds the routes.\n4. `POST /v2/route/{id}/dispatch` sends each route to its driver.\n\nPayload size is capped at **3 MB**. To send more stops, split them across several `POST /v2/stops` calls once the plan exists.\n\n> Plan-level GeoFences are deprecated. The plan inherits the GeoFences defined at project level and cannot override them.",
        "parameters": [
          {
            "name": "project_id",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string",
              "pattern": "^[0-9a-f]{24}"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/PlanData"
              }
            }
          }
        },
        "tags": [
          "Plan"
        ],
        "responses": {
          "200": {
            "description": "The newly created plan.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PlanResponseData"
                }
              }
            }
          },
          "400": {
            "description": "Bad request: validation failed or the payload is malformed",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized: the API key or token is missing or invalid"
          },
          "403": {
            "description": "Forbidden: your plan or permissions do not allow this action",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "404": {
            "description": "Not found: the requested resource does not exist",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests: rate limit reached, retry with exponential backoff"
          },
          "500": {
            "description": "Internal server error. Write to support@routal.com if it persists"
          }
        },
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "cURL",
            "source": "curl -X POST 'https://api.routal.com/v2/plan?private_key=YOUR_KEY&project_id=...' \\\n  -H 'Content-Type: application/json' \\\n  -d '{ }'"
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript",
            "source": "const res = await fetch('https://api.routal.com/v2/plan?private_key=YOUR_KEY&project_id=...', {\n  method: 'POST',\n  headers: { 'Content-Type': 'application/json' },\n  body: JSON.stringify({ /* … */ }),\n});\nconst data = await res.json();"
          },
          {
            "lang": "Python",
            "label": "Python",
            "source": "import requests\n\nres = requests.post(\n    'https://api.routal.com/v2/plan',\n    params={ 'private_key': 'YOUR_KEY', 'project_id': '...' },\n    headers={'Content-Type': 'application/json'},\n    json={},\n)\nprint(res.json())"
          }
        ]
      }
    },
    "/v2/routes": {
      "post": {
        "summary": "Create multiple routes",
        "operationId": "postV2Routes",
        "description": "Creates several routes in a plan in one request. Each route can carry its assigned vehicle, constraints and stop sequence.\n\nUse it when the assignment is already computed elsewhere, by your own optimizer, a manual dispatch or a recurring template, and you only need to materialize the routes without calling `POST /v2/plan/{id}/optimize`.",
        "parameters": [
          {
            "description": "The plan where the routes will be created",
            "name": "plan_id",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string",
              "pattern": "^[0-9a-f]{24}"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/Routes"
              }
            }
          }
        },
        "tags": [
          "Route"
        ],
        "responses": {
          "200": {
            "description": "The newly created routes with their stops.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Route_With_Stops"
                }
              }
            }
          },
          "400": {
            "description": "Bad request: validation failed or the payload is malformed",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized: the API key or token is missing or invalid"
          },
          "403": {
            "description": "Forbidden: your plan or permissions do not allow this action",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "404": {
            "description": "Not found: the requested resource does not exist",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests: rate limit reached, retry with exponential backoff"
          },
          "500": {
            "description": "Internal server error. Write to support@routal.com if it persists"
          }
        },
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "cURL",
            "source": "curl -X POST 'https://api.routal.com/v2/routes?private_key=YOUR_KEY&plan_id=...' \\\n  -H 'Content-Type: application/json' \\\n  -d '{ }'"
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript",
            "source": "const res = await fetch('https://api.routal.com/v2/routes?private_key=YOUR_KEY&plan_id=...', {\n  method: 'POST',\n  headers: { 'Content-Type': 'application/json' },\n  body: JSON.stringify({ /* … */ }),\n});\nconst data = await res.json();"
          },
          {
            "lang": "Python",
            "label": "Python",
            "source": "import requests\n\nres = requests.post(\n    'https://api.routal.com/v2/routes',\n    params={ 'private_key': 'YOUR_KEY', 'plan_id': '...' },\n    headers={'Content-Type': 'application/json'},\n    json={},\n)\nprint(res.json())"
          }
        ]
      }
    },
    "/v2/stops": {
      "post": {
        "summary": "Create multiple stops",
        "operationId": "postV2Stops",
        "description": "Bulk-creates stops with known coordinates. No geocoding runs: pass the `location` (`lat` / `lng`) on each stop. Stops without a `location` are accepted, but the optimizer cannot place them until coordinates arrive.\n\nPass either `plan_id`, so the stops are added to that plan and ready to optimize, or `project_id`, so they are created unassigned at project level as an inbox of pending orders.\n\nPayload size is capped at **5 MB**, roughly 5,000 stops. Chunk larger imports. If your data has free-text addresses instead of coordinates, use `POST /v2/stops/geocode`.",
        "parameters": [
          {
            "description": "The plan where the stops will be created",
            "name": "plan_id",
            "in": "query",
            "schema": {
              "type": "string",
              "pattern": "^[0-9a-f]{24}"
            }
          },
          {
            "description": "The project where the stops will be created in case of creating unassigned plan",
            "name": "project_id",
            "in": "query",
            "schema": {
              "type": "string",
              "pattern": "^[0-9a-f]{24}"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/stops"
              }
            }
          }
        },
        "tags": [
          "Stop"
        ],
        "responses": {
          "200": {
            "description": "The newly created stops.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/StopsData"
                }
              }
            }
          },
          "400": {
            "description": "Bad request: validation failed or the payload is malformed",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized: the API key or token is missing or invalid"
          },
          "403": {
            "description": "Forbidden: your plan or permissions do not allow this action",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "404": {
            "description": "Not found: the requested resource does not exist",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests: rate limit reached, retry with exponential backoff"
          },
          "500": {
            "description": "Internal server error. Write to support@routal.com if it persists"
          }
        },
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "cURL",
            "source": "curl -X POST 'https://api.routal.com/v2/stops?private_key=YOUR_KEY' \\\n  -H 'Content-Type: application/json' \\\n  -d '{ }'"
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript",
            "source": "const res = await fetch('https://api.routal.com/v2/stops?private_key=YOUR_KEY', {\n  method: 'POST',\n  headers: { 'Content-Type': 'application/json' },\n  body: JSON.stringify({ /* … */ }),\n});\nconst data = await res.json();"
          },
          {
            "lang": "Python",
            "label": "Python",
            "source": "import requests\n\nres = requests.post(\n    'https://api.routal.com/v2/stops',\n    params={ 'private_key': 'YOUR_KEY' },\n    headers={'Content-Type': 'application/json'},\n    json={},\n)\nprint(res.json())"
          }
        ]
      }
    },
    "/v2/task": {
      "post": {
        "summary": "Create a task on a stop",
        "operationId": "postV2Task",
        "description": "Attaches a task to an existing stop. A task is a checklist item the driver ticks during the visit: it carries a `label`, optional `comments`, an optional `barcode` to scan, `custom_fields` and a `status` (`pending`, `completed` or `canceled`).\n\nUse tasks when a stop covers several SKUs or sub-operations the driver ticks one by one. Signatures, photos and free-form comments travel in the stop's `report` instead.",
        "parameters": [
          {
            "description": "Stop ID",
            "name": "stop_id",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string",
              "pattern": "^[0-9a-f]{24}"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/Task1"
              }
            }
          }
        },
        "tags": [
          "Task"
        ],
        "responses": {
          "200": {
            "description": "The newly created task.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/StopTask"
                }
              }
            }
          },
          "400": {
            "description": "Bad request: validation failed or the payload is malformed",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized: the API key or token is missing or invalid"
          },
          "403": {
            "description": "Forbidden: your plan or permissions do not allow this action",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "404": {
            "description": "Not found: the requested resource does not exist",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests: rate limit reached, retry with exponential backoff"
          },
          "500": {
            "description": "Internal server error. Write to support@routal.com if it persists"
          }
        },
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "cURL",
            "source": "curl -X POST 'https://api.routal.com/v2/task?private_key=YOUR_KEY&stop_id=...' \\\n  -H 'Content-Type: application/json' \\\n  -d '{ }'"
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript",
            "source": "const res = await fetch('https://api.routal.com/v2/task?private_key=YOUR_KEY&stop_id=...', {\n  method: 'POST',\n  headers: { 'Content-Type': 'application/json' },\n  body: JSON.stringify({ /* … */ }),\n});\nconst data = await res.json();"
          },
          {
            "lang": "Python",
            "label": "Python",
            "source": "import requests\n\nres = requests.post(\n    'https://api.routal.com/v2/task',\n    params={ 'private_key': 'YOUR_KEY', 'stop_id': '...' },\n    headers={'Content-Type': 'application/json'},\n    json={},\n)\nprint(res.json())"
          }
        ]
      }
    },
    "/v3/vehicles": {
      "post": {
        "summary": "Create multiple vehicles (v3)",
        "operationId": "postV3Vehicles",
        "description": "Bulk-creates vehicles in a project, each with its full configuration: capacity, skills (`provides`), working hours, breaks, start and end locations, driver, transport mode and traffic options.\n\n`v3` accepts the latest schema and is the endpoint for new integrations. The older `POST /v2/vehicles` is still fully supported.",
        "parameters": [
          {
            "name": "project_id",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string",
              "pattern": "^[0-9a-f]{24}"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateVehiclesValidator"
              }
            }
          }
        },
        "tags": [
          "Vehicle"
        ],
        "responses": {
          "200": {
            "description": "The newly created vehicles.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Vehicles"
                }
              }
            }
          },
          "400": {
            "description": "Bad request: validation failed or the payload is malformed",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized: the API key or token is missing or invalid"
          },
          "403": {
            "description": "Forbidden: your plan or permissions do not allow this action",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "404": {
            "description": "Not found: the requested resource does not exist",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests: rate limit reached, retry with exponential backoff"
          },
          "500": {
            "description": "Internal server error. Write to support@routal.com if it persists"
          }
        },
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "cURL",
            "source": "curl -X POST 'https://api.routal.com/v3/vehicles?private_key=YOUR_KEY&project_id=...' \\\n  -H 'Content-Type: application/json' \\\n  -d '{ }'"
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript",
            "source": "const res = await fetch('https://api.routal.com/v3/vehicles?private_key=YOUR_KEY&project_id=...', {\n  method: 'POST',\n  headers: { 'Content-Type': 'application/json' },\n  body: JSON.stringify({ /* … */ }),\n});\nconst data = await res.json();"
          },
          {
            "lang": "Python",
            "label": "Python",
            "source": "import requests\n\nres = requests.post(\n    'https://api.routal.com/v3/vehicles',\n    params={ 'private_key': 'YOUR_KEY', 'project_id': '...' },\n    headers={'Content-Type': 'application/json'},\n    json={},\n)\nprint(res.json())"
          }
        ]
      }
    },
    "/v2/connectors/imports": {
      "post": {
        "summary": "Multi-source import. Fetches and resolves addresses. Returns a transient ImportRun — nothing is stored.",
        "operationId": "postV2ConnectorsImports",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/Model7"
              }
            }
          }
        },
        "tags": [
          "Connectors"
        ],
        "responses": {
          "default": {
            "description": "Successful",
            "content": {
              "application/json": {
                "schema": {
                  "type": "string"
                }
              }
            }
          }
        },
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "cURL",
            "source": "curl -X POST 'https://api.routal.com/v2/connectors/imports?private_key=YOUR_KEY' \\\n  -H 'Content-Type: application/json' \\\n  -d '{ }'"
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript",
            "source": "const res = await fetch('https://api.routal.com/v2/connectors/imports?private_key=YOUR_KEY', {\n  method: 'POST',\n  headers: { 'Content-Type': 'application/json' },\n  body: JSON.stringify({ /* … */ }),\n});\nconst data = await res.json();"
          },
          {
            "lang": "Python",
            "label": "Python",
            "source": "import requests\n\nres = requests.post(\n    'https://api.routal.com/v2/connectors/imports',\n    params={ 'private_key': 'YOUR_KEY' },\n    headers={'Content-Type': 'application/json'},\n    json={},\n)\nprint(res.json())"
          }
        ]
      }
    },
    "/v2/stop/move": {
      "post": {
        "summary": "Move stops between plans",
        "operationId": "postV2StopMove",
        "description": "Moves one or more stops to a different plan. Omit `plan_id` to unassign them at project level, into your unassigned inbox.\n\nUse it to reschedule orders to another day, or to park a stop while you decide how to handle it.\n\n## ⚠️ Traceability warning\n\nRoutal models each stop as a single **delivery attempt**. Moving a stop after it has been executed (delivered, failed, cancelled) rewrites history and breaks the audit trail.\n\nMove only stops that have not started execution. For a retry, create a new stop in the new plan instead. If you are unsure, [contact support](mailto:support@routal.com).",
        "parameters": [
          {
            "description": "The plan where the stops will be moved. If left empty, the stop will be unassigned.",
            "name": "plan_id",
            "in": "query",
            "schema": {
              "type": "string",
              "pattern": "^[0-9a-f]{24}"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/Array_of_id"
              }
            }
          }
        },
        "tags": [
          "Stop"
        ],
        "responses": {
          "200": {
            "description": "The moved stops with their new plan assignment.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/StopData"
                }
              }
            }
          },
          "400": {
            "description": "Bad request: validation failed or the payload is malformed",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized: the API key or token is missing or invalid"
          },
          "403": {
            "description": "Forbidden: your plan or permissions do not allow this action",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "404": {
            "description": "Not found: the requested resource does not exist",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests: rate limit reached, retry with exponential backoff"
          },
          "500": {
            "description": "Internal server error. Write to support@routal.com if it persists"
          }
        },
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "cURL",
            "source": "curl -X POST 'https://api.routal.com/v2/stop/move?private_key=YOUR_KEY' \\\n  -H 'Content-Type: application/json' \\\n  -d '{ }'"
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript",
            "source": "const res = await fetch('https://api.routal.com/v2/stop/move?private_key=YOUR_KEY', {\n  method: 'POST',\n  headers: { 'Content-Type': 'application/json' },\n  body: JSON.stringify({ /* … */ }),\n});\nconst data = await res.json();"
          },
          {
            "lang": "Python",
            "label": "Python",
            "source": "import requests\n\nres = requests.post(\n    'https://api.routal.com/v2/stop/move',\n    params={ 'private_key': 'YOUR_KEY' },\n    headers={'Content-Type': 'application/json'},\n    json={},\n)\nprint(res.json())"
          }
        ]
      }
    },
    "/v2/stops/geocode": {
      "post": {
        "summary": "Create stops with automatic geocoding",
        "operationId": "postV2StopsGeocode",
        "description": "Bulk-creates stops from free-text addresses. Routal resolves each `address` to coordinates and stores the result on the stop. Do not pass `location`, it is rejected: `POST /v2/stops` is the endpoint for coordinates you already have.\n\nStops whose address cannot be geocoded are still created, without coordinates. They stay unassigned and cannot be optimized until someone fixes the address in the Routal Planner.\n\nPass either `plan_id` (add to a plan) or `project_id` (create unassigned). Payload size is capped at **5 MB**. Geocoding costs more than coordinate-based creation, so prefer `POST /v2/stops` when your side already has coordinates.",
        "parameters": [
          {
            "description": "The plan where the stops will be created",
            "name": "plan_id",
            "in": "query",
            "schema": {
              "type": "string",
              "pattern": "^[0-9a-f]{24}"
            }
          },
          {
            "description": "The project where the stops will be created in case of creating unassigned plan",
            "name": "project_id",
            "in": "query",
            "schema": {
              "type": "string",
              "pattern": "^[0-9a-f]{24}"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/stopsWithGeocode"
              }
            }
          }
        },
        "tags": [
          "Stop"
        ],
        "responses": {
          "200": {
            "description": "The newly created stops, each with its geocoded location and accuracy.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/StopsData"
                }
              }
            }
          },
          "400": {
            "description": "Bad request: validation failed or the payload is malformed",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized: the API key or token is missing or invalid"
          },
          "403": {
            "description": "Forbidden: your plan or permissions do not allow this action",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "404": {
            "description": "Not found: the requested resource does not exist",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests: rate limit reached, retry with exponential backoff"
          },
          "500": {
            "description": "Internal server error. Write to support@routal.com if it persists"
          }
        },
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "cURL",
            "source": "curl -X POST 'https://api.routal.com/v2/stops/geocode?private_key=YOUR_KEY' \\\n  -H 'Content-Type: application/json' \\\n  -d '{ }'"
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript",
            "source": "const res = await fetch('https://api.routal.com/v2/stops/geocode?private_key=YOUR_KEY', {\n  method: 'POST',\n  headers: { 'Content-Type': 'application/json' },\n  body: JSON.stringify({ /* … */ }),\n});\nconst data = await res.json();"
          },
          {
            "lang": "Python",
            "label": "Python",
            "source": "import requests\n\nres = requests.post(\n    'https://api.routal.com/v2/stops/geocode',\n    params={ 'private_key': 'YOUR_KEY' },\n    headers={'Content-Type': 'application/json'},\n    json={},\n)\nprint(res.json())"
          }
        ]
      }
    },
    "/v2/stops/search": {
      "post": {
        "summary": "Search stops",
        "operationId": "postV2StopsSearch",
        "description": "Searches stops across the plans of a project. Filters cover full text, status, date range, label, customer, supervisor and custom fields. Results are paginated and sorted by relevance or by any indexed field.\n\nIt answers the cross-plan questions: every failed delivery this week, or every stop for one customer in the last 30 days.",
        "parameters": [
          {
            "name": "project_id",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string",
              "pattern": "^[0-9a-f]{24}"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/StopsSearch"
              }
            }
          }
        },
        "tags": [
          "Stop"
        ],
        "responses": {
          "200": {
            "description": "Paginated list of stops matching the filters.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SearchStopsPagination"
                }
              }
            }
          },
          "400": {
            "description": "Bad request: validation failed or the payload is malformed",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized: the API key or token is missing or invalid"
          },
          "403": {
            "description": "Forbidden: your plan or permissions do not allow this action",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "404": {
            "description": "Not found: the requested resource does not exist",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests: rate limit reached, retry with exponential backoff"
          },
          "500": {
            "description": "Internal server error. Write to support@routal.com if it persists"
          }
        },
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "cURL",
            "source": "curl -X POST 'https://api.routal.com/v2/stops/search?private_key=YOUR_KEY&project_id=...' \\\n  -H 'Content-Type: application/json' \\\n  -d '{ }'"
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript",
            "source": "const res = await fetch('https://api.routal.com/v2/stops/search?private_key=YOUR_KEY&project_id=...', {\n  method: 'POST',\n  headers: { 'Content-Type': 'application/json' },\n  body: JSON.stringify({ /* … */ }),\n});\nconst data = await res.json();"
          },
          {
            "lang": "Python",
            "label": "Python",
            "source": "import requests\n\nres = requests.post(\n    'https://api.routal.com/v2/stops/search',\n    params={ 'private_key': 'YOUR_KEY', 'project_id': '...' },\n    headers={'Content-Type': 'application/json'},\n    json={},\n)\nprint(res.json())"
          }
        ]
      }
    },
    "/v2/vehicles/performance": {
      "post": {
        "summary": "Search fleet performance",
        "operationId": "postV2VehiclesPerformance",
        "description": "Returns a paginated, sortable list of every vehicle in the project joined with its period performance metrics (routes, stops breakdown, distance, time, price, tracing, planned baselines). Vehicles with no activity in the period are included with null metrics and sort last. Use `sort_by` (a column id, e.g. `perf_stops_per_hour`, `completion_rate`) + `sort_direction` to rank the fleet; `predicates` delimits the period (`created_at`). With `include_diagnosis`, `sort_by=diagnosis` ranks the fleet by verdict severity, so `desc` puts the vehicles that require attention first; vehicles with no verdict, or too small a sample to judge, sort last in both directions.",
        "parameters": [
          {
            "description": "Project ID",
            "name": "project_id",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string",
              "pattern": "^[0-9a-f]{24}"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/Model11"
              }
            }
          }
        },
        "tags": [
          "Vehicle"
        ],
        "responses": {
          "400": {
            "description": "Bad request: validation failed or the payload is malformed",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized: the API key or token is missing or invalid"
          },
          "403": {
            "description": "Forbidden: your plan or permissions do not allow this action",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "404": {
            "description": "Not found: the requested resource does not exist",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests: rate limit reached, retry with exponential backoff"
          },
          "500": {
            "description": "Internal server error. Write to support@routal.com if it persists"
          }
        },
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "cURL",
            "source": "curl -X POST 'https://api.routal.com/v2/vehicles/performance?private_key=YOUR_KEY&project_id=...' \\\n  -H 'Content-Type: application/json' \\\n  -d '{ }'"
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript",
            "source": "const res = await fetch('https://api.routal.com/v2/vehicles/performance?private_key=YOUR_KEY&project_id=...', {\n  method: 'POST',\n  headers: { 'Content-Type': 'application/json' },\n  body: JSON.stringify({ /* … */ }),\n});\nconst data = await res.json();"
          },
          {
            "lang": "Python",
            "label": "Python",
            "source": "import requests\n\nres = requests.post(\n    'https://api.routal.com/v2/vehicles/performance',\n    params={ 'private_key': 'YOUR_KEY', 'project_id': '...' },\n    headers={'Content-Type': 'application/json'},\n    json={},\n)\nprint(res.json())"
          }
        ]
      }
    },
    "/v2/vehicles/sessions": {
      "post": {
        "summary": "Which of these vehicles has a live driver-app session",
        "operationId": "postV2VehiclesSessions",
        "description": "Returns `{ sessions: { <vehicle_id>: boolean } }` for the vehicle ids given — meant for the rows currently on screen. Split out of `POST /v2/vehicles/performance` (where it used to live behind `include_sessions`) because answering it requires a Redis keyspace scan: `SCAN` walks every key whatever the MATCH pattern, which put a serial 300 ms-1,5 s step on the fleet list's critical path. Safe to defer precisely because this field is no part of ordering or pagination — the column is not sortable — so the table paints without it and fills it in after.",
        "parameters": [
          {
            "description": "Project ID",
            "name": "project_id",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string",
              "pattern": "^[0-9a-f]{24}"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/Model12"
              }
            }
          }
        },
        "tags": [
          "Vehicle"
        ],
        "responses": {
          "400": {
            "description": "Bad request: validation failed or the payload is malformed",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized: the API key or token is missing or invalid"
          },
          "403": {
            "description": "Forbidden: your plan or permissions do not allow this action",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "404": {
            "description": "Not found: the requested resource does not exist",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests: rate limit reached, retry with exponential backoff"
          },
          "500": {
            "description": "Internal server error. Write to support@routal.com if it persists"
          }
        },
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "cURL",
            "source": "curl -X POST 'https://api.routal.com/v2/vehicles/sessions?private_key=YOUR_KEY&project_id=...' \\\n  -H 'Content-Type: application/json' \\\n  -d '{ }'"
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript",
            "source": "const res = await fetch('https://api.routal.com/v2/vehicles/sessions?private_key=YOUR_KEY&project_id=...', {\n  method: 'POST',\n  headers: { 'Content-Type': 'application/json' },\n  body: JSON.stringify({ /* … */ }),\n});\nconst data = await res.json();"
          },
          {
            "lang": "Python",
            "label": "Python",
            "source": "import requests\n\nres = requests.post(\n    'https://api.routal.com/v2/vehicles/sessions',\n    params={ 'private_key': 'YOUR_KEY', 'project_id': '...' },\n    headers={'Content-Type': 'application/json'},\n    json={},\n)\nprint(res.json())"
          }
        ]
      }
    },
    "/v2/connectors/connections/test": {
      "post": {
        "summary": "Stateless test — authenticate + probe required capabilities. Returns a TestReport.",
        "operationId": "postV2ConnectorsConnectionsTest",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/Model13"
              }
            }
          }
        },
        "tags": [
          "Connectors"
        ],
        "responses": {
          "default": {
            "description": "Successful",
            "content": {
              "application/json": {
                "schema": {
                  "type": "string"
                }
              }
            }
          }
        },
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "cURL",
            "source": "curl -X POST 'https://api.routal.com/v2/connectors/connections/test?private_key=YOUR_KEY' \\\n  -H 'Content-Type: application/json' \\\n  -d '{ }'"
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript",
            "source": "const res = await fetch('https://api.routal.com/v2/connectors/connections/test?private_key=YOUR_KEY', {\n  method: 'POST',\n  headers: { 'Content-Type': 'application/json' },\n  body: JSON.stringify({ /* … */ }),\n});\nconst data = await res.json();"
          },
          {
            "lang": "Python",
            "label": "Python",
            "source": "import requests\n\nres = requests.post(\n    'https://api.routal.com/v2/connectors/connections/test',\n    params={ 'private_key': 'YOUR_KEY' },\n    headers={'Content-Type': 'application/json'},\n    json={},\n)\nprint(res.json())"
          }
        ]
      }
    },
    "/v2/route/{id}/optimize": {
      "post": {
        "summary": "Optimize the stop sequence of a route",
        "operationId": "postV2RouteIdOptimize",
        "description": "Runs the optimizer over the stops already assigned to this route, keeping the vehicle fixed. Stops are re-sequenced and any that no longer fit the route's constraints (time, capacity) come back unassigned instead of moving to another vehicle.\n\nPrefer it over `POST /v2/plan/{id}/optimize` to re-optimize a single driver without disturbing the rest of the plan.",
        "parameters": [
          {
            "description": "Route ID",
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "pattern": "^[0-9a-f]{24}"
            }
          }
        ],
        "tags": [
          "Route"
        ],
        "responses": {
          "200": {
            "description": "The route with the re-optimized stop sequence and recomputed ETAs.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/OptimizedRoute"
                }
              }
            }
          },
          "400": {
            "description": "Bad request: validation failed or the payload is malformed",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized: the API key or token is missing or invalid"
          },
          "403": {
            "description": "Forbidden: your plan or permissions do not allow this action",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "404": {
            "description": "Not found: the requested resource does not exist",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests: rate limit reached, retry with exponential backoff"
          },
          "500": {
            "description": "Internal server error. Write to support@routal.com if it persists"
          }
        },
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "cURL",
            "source": "curl -X POST 'https://api.routal.com/v2/route/{id}/optimize?private_key=YOUR_KEY' \\\n  -H 'Content-Type: application/json'"
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript",
            "source": "const res = await fetch('https://api.routal.com/v2/route/{id}/optimize?private_key=YOUR_KEY', {\n  method: 'POST',\n  headers: { 'Content-Type': 'application/json' },\n});\nconst data = await res.json();"
          },
          {
            "lang": "Python",
            "label": "Python",
            "source": "import requests\n\nres = requests.post(\n    'https://api.routal.com/v2/route/{id}/optimize',\n    params={ 'private_key': 'YOUR_KEY' },\n    headers={'Content-Type': 'application/json'},\n)\nprint(res.json())"
          }
        ]
      }
    },
    "/v2/route/{id}/dispatch": {
      "post": {
        "summary": "Dispatch a route to its driver",
        "operationId": "postV2RouteIdDispatch",
        "description": "Emails the route's driver a public link that opens the route in the Routal Drivers app or webview.\n\nCalling it again re-sends the email with a freshly built link, for a driver who lost it or changed device. The route status does not change: the driver starts the route from the app.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "tags": [
          "Route"
        ],
        "responses": {
          "200": {
            "description": "Confirmation that the dispatch link has been sent.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Route_Dispatch"
                }
              }
            }
          },
          "400": {
            "description": "Bad request: validation failed or the payload is malformed",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized: the API key or token is missing or invalid"
          },
          "403": {
            "description": "Forbidden: your plan or permissions do not allow this action",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "404": {
            "description": "Not found: the requested resource does not exist",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests: rate limit reached, retry with exponential backoff"
          },
          "500": {
            "description": "Internal server error. Write to support@routal.com if it persists"
          }
        },
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "cURL",
            "source": "curl -X POST 'https://api.routal.com/v2/route/{id}/dispatch?private_key=YOUR_KEY' \\\n  -H 'Content-Type: application/json'"
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript",
            "source": "const res = await fetch('https://api.routal.com/v2/route/{id}/dispatch?private_key=YOUR_KEY', {\n  method: 'POST',\n  headers: { 'Content-Type': 'application/json' },\n});\nconst data = await res.json();"
          },
          {
            "lang": "Python",
            "label": "Python",
            "source": "import requests\n\nres = requests.post(\n    'https://api.routal.com/v2/route/{id}/dispatch',\n    params={ 'private_key': 'YOUR_KEY' },\n    headers={'Content-Type': 'application/json'},\n)\nprint(res.json())"
          }
        ]
      }
    },
    "/v2/vehicles/{vehicleId}/timeseries": {
      "post": {
        "summary": "Retrieve a vehicle's metric time series",
        "operationId": "postV2VehiclesVehicleidTimeseries",
        "description": "Time series of a vehicle's metrics against its fleet, bucketed by day or week, for the Overview sparklines and trend chart. Buckets come from the execution date of the plans the routes belong to; metrics use the same definitions as the vitals endpoint. One point per bucket with fleet activity, with the vehicle's values null where it did not work. Fleet values are only returned for rates and ratios — a fleet volume total is not comparable to a single vehicle. `predicates` delimits the period (`created_at`).",
        "parameters": [
          {
            "description": "Vehicle ID",
            "name": "vehicleId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "pattern": "^[0-9a-f]{24}"
            }
          },
          {
            "description": "Project ID",
            "name": "project_id",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string",
              "pattern": "^[0-9a-f]{24}"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/Model15"
              }
            }
          }
        },
        "tags": [
          "Vehicle"
        ],
        "responses": {
          "400": {
            "description": "Bad request: validation failed or the payload is malformed",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized: the API key or token is missing or invalid"
          },
          "403": {
            "description": "Forbidden: your plan or permissions do not allow this action",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "404": {
            "description": "Not found: the requested resource does not exist",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests: rate limit reached, retry with exponential backoff"
          },
          "500": {
            "description": "Internal server error. Write to support@routal.com if it persists"
          }
        },
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "cURL",
            "source": "curl -X POST 'https://api.routal.com/v2/vehicles/{vehicleId}/timeseries?private_key=YOUR_KEY&project_id=...' \\\n  -H 'Content-Type: application/json' \\\n  -d '{ }'"
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript",
            "source": "const res = await fetch('https://api.routal.com/v2/vehicles/{vehicleId}/timeseries?private_key=YOUR_KEY&project_id=...', {\n  method: 'POST',\n  headers: { 'Content-Type': 'application/json' },\n  body: JSON.stringify({ /* … */ }),\n});\nconst data = await res.json();"
          },
          {
            "lang": "Python",
            "label": "Python",
            "source": "import requests\n\nres = requests.post(\n    'https://api.routal.com/v2/vehicles/{vehicleId}/timeseries',\n    params={ 'private_key': 'YOUR_KEY', 'project_id': '...' },\n    headers={'Content-Type': 'application/json'},\n    json={},\n)\nprint(res.json())"
          }
        ]
      }
    },
    "/v2/vehicles/{vehicleId}/vitals": {
      "post": {
        "summary": "Retrieve vehicle vital signs",
        "operationId": "postV2VehiclesVehicleidVitals",
        "description": "Returns the headline vital metrics (on-time, completion, cancellation, stops/hour, total cost, cost/stop, distance) for a vehicle: its value this period, its previous-period value (for the delta), and the fleet distribution (median/min/max/rank) that powers the \"vs fleet\" benchmark. It also returns `breakdowns`: what those numbers are made of (stops by status, delay figures, the terms of the cost, and the reasons behind the cancellations), and `diagnosis`: the verdict for this vehicle, from the same rule and the same numbers the fleet list uses, so the detail and the list cannot disagree. `predicates` and `previous_predicates` delimit the current and comparison periods (`created_at`).",
        "parameters": [
          {
            "description": "Vehicle ID",
            "name": "vehicleId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "pattern": "^[0-9a-f]{24}"
            }
          },
          {
            "description": "Project ID",
            "name": "project_id",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string",
              "pattern": "^[0-9a-f]{24}"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/Model18"
              }
            }
          }
        },
        "tags": [
          "Vehicle"
        ],
        "responses": {
          "400": {
            "description": "Bad request: validation failed or the payload is malformed",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized: the API key or token is missing or invalid"
          },
          "403": {
            "description": "Forbidden: your plan or permissions do not allow this action",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "404": {
            "description": "Not found: the requested resource does not exist",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests: rate limit reached, retry with exponential backoff"
          },
          "500": {
            "description": "Internal server error. Write to support@routal.com if it persists"
          }
        },
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "cURL",
            "source": "curl -X POST 'https://api.routal.com/v2/vehicles/{vehicleId}/vitals?private_key=YOUR_KEY&project_id=...' \\\n  -H 'Content-Type: application/json' \\\n  -d '{ }'"
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript",
            "source": "const res = await fetch('https://api.routal.com/v2/vehicles/{vehicleId}/vitals?private_key=YOUR_KEY&project_id=...', {\n  method: 'POST',\n  headers: { 'Content-Type': 'application/json' },\n  body: JSON.stringify({ /* … */ }),\n});\nconst data = await res.json();"
          },
          {
            "lang": "Python",
            "label": "Python",
            "source": "import requests\n\nres = requests.post(\n    'https://api.routal.com/v2/vehicles/{vehicleId}/vitals',\n    params={ 'private_key': 'YOUR_KEY', 'project_id': '...' },\n    headers={'Content-Type': 'application/json'},\n    json={},\n)\nprint(res.json())"
          }
        ]
      }
    },
    "/v3/stop/{stop_id}/report": {
      "post": {
        "summary": "Create a service report",
        "operationId": "postV3StopStop_idReport",
        "description": "Marks a stop as completed, canceled or attempted, optionally attaching the proof of delivery.\n\nTwo body shapes are accepted:\n\n- `application/json`: send the report fields directly and attach proof-of-delivery images in `images`,\n  each one a base64 data-URL. This is the shape to use from the API.\n- `multipart/form-data`: send the report fields JSON-encoded in `report_params` and the\n  proof-of-delivery files as `file_0`...`file_4`, described by `files_metadata`.\n  The Routal Planner web app uses this one.\n\nSend either `status` (JSON) or `report_params` (multipart), never both.\n\nRules:\n\n- `cancel_reason` is a closed set of values and is rejected on a `service_report_completed` report.\n  For a free-text motive use `comments`.\n- `custom_fields` keys are the `custom_id` of the project's custom fields, validated against the\n  `service_report_completed` model for a completed report and `service_report_canceled` for a canceled\n  or attempted one.\n- Custom fields the project marked as required for this report model must carry a value, or the request\n  is rejected with `highway.report.error.required_fields_missing` naming the missing fields.\n- Attaching images requires the `drivers.advanced_pod` subscription item.\n- A stop accepts a single terminal report: a second completed or canceled report returns 400. Discard the\n  existing one first with `DELETE /v3/stop/{stop_id}/report/{report_id}`.\n- `service_report_attempted` requires the stop's route to be in transit.",
        "parameters": [
          {
            "description": "The stop where the report will be made",
            "name": "stop_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "pattern": "^[0-9a-f]{24}"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateStopReport"
              }
            }
          }
        },
        "tags": [
          "Stop"
        ],
        "responses": {
          "default": {
            "description": "Successful",
            "content": {
              "application/json": {
                "schema": {
                  "type": "string"
                }
              }
            }
          }
        },
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "cURL",
            "source": "curl -X POST 'https://api.routal.com/v3/stop/{stop_id}/report?private_key=YOUR_KEY' \\\n  -H 'Content-Type: application/json' \\\n  -d '{ }'"
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript",
            "source": "const res = await fetch('https://api.routal.com/v3/stop/{stop_id}/report?private_key=YOUR_KEY', {\n  method: 'POST',\n  headers: { 'Content-Type': 'application/json' },\n  body: JSON.stringify({ /* … */ }),\n});\nconst data = await res.json();"
          },
          {
            "lang": "Python",
            "label": "Python",
            "source": "import requests\n\nres = requests.post(\n    'https://api.routal.com/v3/stop/{stop_id}/report',\n    params={ 'private_key': 'YOUR_KEY' },\n    headers={'Content-Type': 'application/json'},\n    json={},\n)\nprint(res.json())"
          }
        ]
      }
    },
    "/v2/plan/{id}/optimize/async": {
      "post": {
        "summary": "Optimize a plan (async)",
        "operationId": "postV2PlanIdOptimizeAsync",
        "description": "Queues an optimization and responds immediately. All routes are recomputed and stops may move between vehicles. Set a webhook to receive the result.",
        "parameters": [
          {
            "description": "Plan ID",
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "pattern": "^[0-9a-f]{24}"
            }
          },
          {
            "description": "If true, the current assignment will not be changed.",
            "name": "keep_current_assignment",
            "in": "query",
            "schema": {
              "type": "boolean",
              "default": false
            }
          }
        ],
        "tags": [
          "Plan"
        ],
        "responses": {
          "200": {
            "description": "Optimization accepted. The result is delivered by webhook.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "400": {
            "description": "Bad request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized: Missing authentication"
          }
        },
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "cURL",
            "source": "curl -X POST 'https://api.routal.com/v2/plan/{id}/optimize/async?private_key=YOUR_KEY' \\\n  -H 'Content-Type: application/json'"
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript",
            "source": "const res = await fetch('https://api.routal.com/v2/plan/{id}/optimize/async?private_key=YOUR_KEY', {\n  method: 'POST',\n  headers: { 'Content-Type': 'application/json' },\n});\nconst data = await res.json();"
          },
          {
            "lang": "Python",
            "label": "Python",
            "source": "import requests\n\nres = requests.post(\n    'https://api.routal.com/v2/plan/{id}/optimize/async',\n    params={ 'private_key': 'YOUR_KEY' },\n    headers={'Content-Type': 'application/json'},\n)\nprint(res.json())"
          }
        ]
      }
    },
    "/v2/task/{task_id}": {
      "delete": {
        "summary": "Delete a task",
        "operationId": "deleteV2TaskTask_id",
        "description": "Removes a task from a stop. The stop itself stays. Avoid deleting completed tasks: their scanned barcode and comments are part of the proof of delivery and are needed for traceability.",
        "parameters": [
          {
            "description": "Task ID",
            "name": "task_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "pattern": "^[0-9a-f]{24}"
            }
          },
          {
            "description": "Stop ID",
            "name": "stop_id",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string",
              "pattern": "^[0-9a-f]{24}"
            }
          }
        ],
        "tags": [
          "Task"
        ],
        "responses": {
          "200": {
            "description": "The deleted task.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/StopTask"
                }
              }
            }
          },
          "400": {
            "description": "Bad request: validation failed or the payload is malformed",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized: the API key or token is missing or invalid"
          },
          "403": {
            "description": "Forbidden: your plan or permissions do not allow this action",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "404": {
            "description": "Not found: the requested resource does not exist",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests: rate limit reached, retry with exponential backoff"
          },
          "500": {
            "description": "Internal server error. Write to support@routal.com if it persists"
          }
        },
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "cURL",
            "source": "curl -X DELETE 'https://api.routal.com/v2/task/{task_id}?private_key=YOUR_KEY&stop_id=...' \\\n  -H 'Content-Type: application/json'"
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript",
            "source": "const res = await fetch('https://api.routal.com/v2/task/{task_id}?private_key=YOUR_KEY&stop_id=...', {\n  method: 'DELETE',\n  headers: { 'Content-Type': 'application/json' },\n});\nconst data = await res.json();"
          },
          {
            "lang": "Python",
            "label": "Python",
            "source": "import requests\n\nres = requests.delete(\n    'https://api.routal.com/v2/task/{task_id}',\n    params={ 'private_key': 'YOUR_KEY', 'stop_id': '...' },\n    headers={'Content-Type': 'application/json'},\n)\nprint(res.json())"
          }
        ]
      },
      "put": {
        "summary": "Update a task",
        "operationId": "putV2TaskTask_id",
        "description": "Updates a task's `label`, `comments`, `barcode`, `custom_fields` or `status`. Use it to correct task data before execution, or to reset a task to `pending` when the driver ticked it by mistake.",
        "parameters": [
          {
            "description": "Task ID",
            "name": "task_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "pattern": "^[0-9a-f]{24}"
            }
          },
          {
            "description": "Stop ID",
            "name": "stop_id",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string",
              "pattern": "^[0-9a-f]{24}"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/Task2"
              }
            }
          }
        },
        "tags": [
          "Task"
        ],
        "responses": {
          "200": {
            "description": "The updated task.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/StopTask"
                }
              }
            }
          },
          "400": {
            "description": "Bad request: validation failed or the payload is malformed",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized: the API key or token is missing or invalid"
          },
          "403": {
            "description": "Forbidden: your plan or permissions do not allow this action",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "404": {
            "description": "Not found: the requested resource does not exist",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests: rate limit reached, retry with exponential backoff"
          },
          "500": {
            "description": "Internal server error. Write to support@routal.com if it persists"
          }
        },
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "cURL",
            "source": "curl -X PUT 'https://api.routal.com/v2/task/{task_id}?private_key=YOUR_KEY&stop_id=...' \\\n  -H 'Content-Type: application/json' \\\n  -d '{ }'"
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript",
            "source": "const res = await fetch('https://api.routal.com/v2/task/{task_id}?private_key=YOUR_KEY&stop_id=...', {\n  method: 'PUT',\n  headers: { 'Content-Type': 'application/json' },\n  body: JSON.stringify({ /* … */ }),\n});\nconst data = await res.json();"
          },
          {
            "lang": "Python",
            "label": "Python",
            "source": "import requests\n\nres = requests.put(\n    'https://api.routal.com/v2/task/{task_id}',\n    params={ 'private_key': 'YOUR_KEY', 'stop_id': '...' },\n    headers={'Content-Type': 'application/json'},\n    json={},\n)\nprint(res.json())"
          }
        ]
      }
    },
    "/v2/connectors/connections/{connection_id}": {
      "delete": {
        "summary": "Disconnect. Stops already created stay untouched.",
        "operationId": "deleteV2ConnectorsConnectionsConnection_id",
        "parameters": [
          {
            "name": "connection_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "tags": [
          "Connectors"
        ],
        "responses": {
          "default": {
            "description": "Successful",
            "content": {
              "application/json": {
                "schema": {
                  "type": "string"
                }
              }
            }
          }
        },
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "cURL",
            "source": "curl -X DELETE 'https://api.routal.com/v2/connectors/connections/{connection_id}?private_key=YOUR_KEY' \\\n  -H 'Content-Type: application/json'"
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript",
            "source": "const res = await fetch('https://api.routal.com/v2/connectors/connections/{connection_id}?private_key=YOUR_KEY', {\n  method: 'DELETE',\n  headers: { 'Content-Type': 'application/json' },\n});\nconst data = await res.json();"
          },
          {
            "lang": "Python",
            "label": "Python",
            "source": "import requests\n\nres = requests.delete(\n    'https://api.routal.com/v2/connectors/connections/{connection_id}',\n    params={ 'private_key': 'YOUR_KEY' },\n    headers={'Content-Type': 'application/json'},\n)\nprint(res.json())"
          }
        ]
      },
      "put": {
        "summary": "Update credentials and/or scope on an existing connection. Any change re-runs TestConnection.",
        "operationId": "putV2ConnectorsConnectionsConnection_id",
        "parameters": [
          {
            "name": "connection_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/Model21"
              }
            }
          }
        },
        "tags": [
          "Connectors"
        ],
        "responses": {
          "default": {
            "description": "Successful",
            "content": {
              "application/json": {
                "schema": {
                  "type": "string"
                }
              }
            }
          }
        },
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "cURL",
            "source": "curl -X PUT 'https://api.routal.com/v2/connectors/connections/{connection_id}?private_key=YOUR_KEY' \\\n  -H 'Content-Type: application/json' \\\n  -d '{ }'"
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript",
            "source": "const res = await fetch('https://api.routal.com/v2/connectors/connections/{connection_id}?private_key=YOUR_KEY', {\n  method: 'PUT',\n  headers: { 'Content-Type': 'application/json' },\n  body: JSON.stringify({ /* … */ }),\n});\nconst data = await res.json();"
          },
          {
            "lang": "Python",
            "label": "Python",
            "source": "import requests\n\nres = requests.put(\n    'https://api.routal.com/v2/connectors/connections/{connection_id}',\n    params={ 'private_key': 'YOUR_KEY' },\n    headers={'Content-Type': 'application/json'},\n    json={},\n)\nprint(res.json())"
          }
        ]
      }
    },
    "/v2/stops/delete": {
      "put": {
        "summary": "Delete multiple stops",
        "operationId": "putV2StopsDelete",
        "description": "Deletes the given stops in one request. Keep stops in a terminal state (delivered, failed, cancelled) for traceability and analytics instead of deleting them.\n\nUse it for bulk cleanup before optimization, for example orders cancelled in your e-commerce after the import. To remove an executed delivery for GDPR reasons, contact support so the audit log is preserved.",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/Model19"
              }
            }
          }
        },
        "tags": [
          "Stop"
        ],
        "responses": {
          "400": {
            "description": "Bad request: validation failed or the payload is malformed",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized: the API key or token is missing or invalid"
          },
          "403": {
            "description": "Forbidden: your plan or permissions do not allow this action",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "404": {
            "description": "Not found: the requested resource does not exist",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests: rate limit reached, retry with exponential backoff"
          },
          "500": {
            "description": "Internal server error. Write to support@routal.com if it persists"
          }
        },
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "cURL",
            "source": "curl -X PUT 'https://api.routal.com/v2/stops/delete?private_key=YOUR_KEY' \\\n  -H 'Content-Type: application/json' \\\n  -d '{ }'"
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript",
            "source": "const res = await fetch('https://api.routal.com/v2/stops/delete?private_key=YOUR_KEY', {\n  method: 'PUT',\n  headers: { 'Content-Type': 'application/json' },\n  body: JSON.stringify({ /* … */ }),\n});\nconst data = await res.json();"
          },
          {
            "lang": "Python",
            "label": "Python",
            "source": "import requests\n\nres = requests.put(\n    'https://api.routal.com/v2/stops/delete',\n    params={ 'private_key': 'YOUR_KEY' },\n    headers={'Content-Type': 'application/json'},\n    json={},\n)\nprint(res.json())"
          }
        ]
      }
    },
    "/v2/route/{id}/stops": {
      "put": {
        "summary": "Reorder the stops of a route",
        "operationId": "putV2RouteIdStops",
        "description": "Sets the ordered list of stop ids for this route, overriding the optimizer's sequence. Use it when a dispatcher reorders stops in your own UI or a custom heuristic runs on your side.\n\nETAs, distance and duration are recomputed. Stops left out of the payload are unassigned from the route, and stops that don't belong to this route's plan are rejected.",
        "parameters": [
          {
            "description": "Route ID",
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "pattern": "^[0-9a-f]{24}"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/RouteEditStops"
              }
            }
          }
        },
        "tags": [
          "Route"
        ],
        "responses": {
          "200": {
            "description": "The route with its new stop ordering and recomputed ETAs.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Route_With_Stops"
                }
              }
            }
          },
          "400": {
            "description": "Bad request: validation failed or the payload is malformed",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized: the API key or token is missing or invalid"
          },
          "403": {
            "description": "Forbidden: your plan or permissions do not allow this action",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "404": {
            "description": "Not found: the requested resource does not exist",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests: rate limit reached, retry with exponential backoff"
          },
          "500": {
            "description": "Internal server error. Write to support@routal.com if it persists"
          }
        },
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "cURL",
            "source": "curl -X PUT 'https://api.routal.com/v2/route/{id}/stops?private_key=YOUR_KEY' \\\n  -H 'Content-Type: application/json' \\\n  -d '{ }'"
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript",
            "source": "const res = await fetch('https://api.routal.com/v2/route/{id}/stops?private_key=YOUR_KEY', {\n  method: 'PUT',\n  headers: { 'Content-Type': 'application/json' },\n  body: JSON.stringify({ /* … */ }),\n});\nconst data = await res.json();"
          },
          {
            "lang": "Python",
            "label": "Python",
            "source": "import requests\n\nres = requests.put(\n    'https://api.routal.com/v2/route/{id}/stops',\n    params={ 'private_key': 'YOUR_KEY' },\n    headers={'Content-Type': 'application/json'},\n    json={},\n)\nprint(res.json())"
          }
        ]
      }
    }
  },
  "servers": [
    {
      "url": "https://api.routal.com"
    }
  ],
  "components": {
    "securitySchemes": {
      "api_key": {
        "type": "apiKey",
        "description": "Pass your API key as the `private_key` query string parameter. Generate and rotate keys in [Developer Settings](https://planner.routal.com/h/settings/developers/api-keys) of your Routal workspace.\n\nKeys stay server-side. Never ship them to a browser or mobile app.",
        "name": "private_key",
        "schema": {
          "type": "string"
        },
        "in": "query"
      }
    },
    "schemas": {
      "PlanResponseData": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "Plan ID",
            "example": "4f75d991ac359f8c4c79d762"
          },
          "organization_id": {
            "type": "string",
            "description": "Organization ID",
            "example": "4f75d991ac359f8c4c79d762"
          },
          "project_id": {
            "type": "string",
            "description": "Project ID",
            "example": "4f75d991ac359f8c4c79d762"
          },
          "completed_stops": {
            "type": "number",
            "description": "Number of completed stops",
            "example": 1
          },
          "canceled_stops": {
            "type": "number",
            "description": "Number of canceled stops",
            "example": 2
          },
          "pending_stops": {
            "type": "number",
            "description": "Number of pending stops",
            "example": 3
          },
          "incomplete_stops": {
            "type": "number",
            "description": "Number of incomplete stops, stops without all tasks completed",
            "example": 3
          },
          "total_stops": {
            "type": "number",
            "description": "Number of total stops",
            "example": 6
          },
          "total_routes": {
            "type": "number",
            "description": "Number of total routes",
            "example": 5
          },
          "label": {
            "type": "string",
            "description": "Plan label",
            "example": "Eixample route 2026-05-21"
          },
          "execution_date": {
            "type": "string",
            "format": "date",
            "description": "Plan execution date",
            "example": "2026-10-02T10:00:40.864Z"
          },
          "status": {
            "$ref": "#/components/schemas/PlanStatus"
          },
          "created_by": {
            "type": "string",
            "description": "Collection user creator",
            "example": "4f75d991ac359f8c4c79d762"
          },
          "created_at": {
            "type": "string",
            "format": "date",
            "description": "Collection creation date",
            "example": "2026-10-02T10:00:40.864Z"
          },
          "updated_at": {
            "type": "string",
            "format": "date",
            "description": "Collection last update date",
            "example": "2026-10-02T10:00:40.864Z"
          },
          "deleted_at": {
            "type": "string",
            "format": "date",
            "description": "Collection deletion date",
            "example": "2026-10-02T10:00:40.864Z"
          },
          "deleted": {
            "type": "boolean",
            "description": "True if plan is deleted"
          }
        },
        "required": [
          "id",
          "organization_id",
          "project_id"
        ]
      },
      "Plans": {
        "type": "array",
        "items": {
          "$ref": "#/components/schemas/PlanResponseData"
        }
      },
      "PaginationPlans": {
        "type": "object",
        "properties": {
          "total": {
            "type": "number",
            "description": "Pagination total",
            "example": 1000
          },
          "limit": {
            "type": "number",
            "description": "Pagination limit",
            "example": 50
          },
          "offset": {
            "type": "number",
            "description": "Pagination offset",
            "example": 25
          },
          "pages": {
            "type": "number",
            "description": "Pagination number of pages",
            "example": 20
          },
          "page": {
            "type": "number",
            "description": "Pagination current page",
            "example": 1
          },
          "docs": {
            "$ref": "#/components/schemas/Plans"
          }
        }
      },
      "Timewindow": {
        "type": "array",
        "description": "Start time and end time timewindow. In seconds since the start of the day and with the format [start_time, end_time]. The example has a time window to make the visit from 8am to 5pm.",
        "example": [
          28800,
          61200
        ],
        "minItems": 2,
        "maxItems": 2,
        "x-constraint": {
          "single": true
        },
        "items": {
          "type": "integer",
          "minimum": 0,
          "maximum": 172800
        }
      },
      "Model2": {
        "type": "string",
        "enum": [
          "Monday",
          "Tuesday",
          "Wednesday",
          "Thursday",
          "Friday",
          "Saturday",
          "Sunday"
        ]
      },
      "default_working_days": {
        "type": "array",
        "example": [
          "Monday",
          "Tuesday",
          "Wednesday",
          "Thursday",
          "Friday"
        ],
        "items": {
          "$ref": "#/components/schemas/Model2"
        }
      },
      "Location": {
        "type": "object",
        "properties": {
          "location_id": {
            "type": "string",
            "description": "Location ID",
            "example": "4f75d991ac359f8c4c79d762"
          },
          "label": {
            "type": "string",
            "description": "Location label",
            "example": "Location label"
          },
          "country": {
            "type": "string",
            "description": "Country",
            "example": "Spain"
          },
          "country_code": {
            "type": "string",
            "description": "Country code. It uses ISO 3166 Alpha-2 standard.",
            "example": "ES"
          },
          "state": {
            "type": "string",
            "description": "State or province",
            "example": "Catalonia"
          },
          "city": {
            "type": "string",
            "description": "City",
            "example": "Barcelona"
          },
          "street": {
            "type": "string",
            "example": "Calle de Pau Claris"
          },
          "postal_code": {
            "type": "string",
            "example": "08302"
          },
          "comments": {
            "type": "string",
            "description": "Location comments",
            "example": "Location comments"
          },
          "lat": {
            "type": "number",
            "description": "Location latitude",
            "example": 40.45
          },
          "lng": {
            "type": "number",
            "description": "Location longitude",
            "example": -3.68
          },
          "main_text": {
            "type": "string",
            "description": "Main text",
            "example": "Calle de Pau Claris 12"
          },
          "secondary_text": {
            "type": "string",
            "description": "Secondary text",
            "example": "Barcelona, Catalonia, Spain"
          }
        }
      },
      "StringArray": {
        "type": "array",
        "example": [
          "refrigerated",
          "lift_gate"
        ],
        "x-constraint": {
          "single": true
        },
        "items": {
          "type": "string",
          "minLength": 1
        }
      },
      "StringArray1": {
        "type": "array",
        "example": [
          "4f75d991ac359f8c4c79d762"
        ],
        "x-constraint": {
          "single": true
        },
        "items": {
          "type": "string",
          "minLength": 1
        }
      },
      "default_break": {
        "type": "object",
        "example": {
          "start": 43200,
          "end": 54000,
          "duration": 2700
        },
        "properties": {
          "start": {
            "type": "number",
            "description": "Earliest second-of-day at which the break can start (e.g. 12:00 = 12 * 3600).",
            "example": 43200
          },
          "end": {
            "type": "number",
            "description": "Latest second-of-day by which the break must end (e.g. 15:00 = 15 * 3600).",
            "example": 54000
          },
          "duration": {
            "type": "number",
            "description": "Total break duration in seconds (e.g. 45 min = 2700).",
            "example": 2700
          }
        },
        "required": [
          "start",
          "end",
          "duration"
        ]
      },
      "custom_fields": {
        "type": "object"
      },
      "VehicleData": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "Vehicle ID",
            "example": "4f75d991ac359f8c4c79d762"
          },
          "organization_id": {
            "type": "string",
            "description": "Organization ID",
            "example": "4f75d991ac359f8c4c79d762"
          },
          "project_id": {
            "type": "string",
            "description": "Project ID",
            "example": "4f75d991ac359f8c4c79d762"
          },
          "external_id": {
            "type": "string",
            "example": "VEH-MAD-02"
          },
          "label": {
            "type": "string",
            "example": "Refrigerated van, Madrid Downtown 02"
          },
          "comments": {
            "type": "string",
            "description": "Internal notes about the vehicle / driver.",
            "example": "Access to central depots only before 09:00."
          },
          "phone": {
            "type": "string",
            "example": "+34612345678"
          },
          "email": {
            "type": "string",
            "example": "carlos.lopez@example.com"
          },
          "plate": {
            "type": "string",
            "example": "2596 KHO"
          },
          "fuel_type": {
            "type": "string",
            "example": "diesel"
          },
          "consumption": {
            "type": "number",
            "example": 8,
            "minimum": 0
          },
          "vehicle_model": {
            "type": "string",
            "example": "Transit Custom L2H2"
          },
          "brand": {
            "type": "string",
            "example": "Ford"
          },
          "color": {
            "type": "string",
            "example": "#1E88E5"
          },
          "default_time_window": {
            "$ref": "#/components/schemas/Timewindow"
          },
          "default_working_days": {
            "$ref": "#/components/schemas/default_working_days"
          },
          "default_min_distance": {
            "type": "number",
            "example": 5000,
            "minimum": 0
          },
          "default_max_distance": {
            "type": "number",
            "example": 120000,
            "minimum": 0
          },
          "default_max_time": {
            "type": "number",
            "example": 28800,
            "minimum": 0
          },
          "default_min_time": {
            "type": "number",
            "example": 3600,
            "minimum": 0
          },
          "default_max_weight": {
            "type": "number",
            "example": 800,
            "minimum": 0
          },
          "default_max_volume": {
            "type": "number",
            "example": 8,
            "minimum": 0
          },
          "default_max_services": {
            "type": "number",
            "example": 40,
            "minimum": 0
          },
          "default_start_location": {
            "$ref": "#/components/schemas/Location"
          },
          "default_end_location": {
            "$ref": "#/components/schemas/Location"
          },
          "default_provides": {
            "$ref": "#/components/schemas/StringArray"
          },
          "default_geo_fences": {
            "$ref": "#/components/schemas/StringArray1"
          },
          "default_break": {
            "$ref": "#/components/schemas/default_break"
          },
          "planned_start_time": {
            "type": "number",
            "description": "Planned route start, second-of-day (e.g. 06:30 = 23400).",
            "example": 23400
          },
          "price_per_distance": {
            "type": "number",
            "example": 0.00045
          },
          "price_per_minute": {
            "type": "number",
            "example": 0.0035
          },
          "price_per_stop": {
            "type": "number",
            "example": 0.5
          },
          "custom_fields": {
            "$ref": "#/components/schemas/custom_fields"
          },
          "enabled": {
            "type": "boolean",
            "description": "True if vehicle is enabled",
            "example": true
          },
          "created_by": {
            "type": "string",
            "description": "Collection user creator",
            "example": "4f75d991ac359f8c4c79d762"
          },
          "created_at": {
            "type": "string",
            "format": "date",
            "description": "Collection creation date",
            "example": "2026-10-02T10:00:40.694Z"
          },
          "updated_at": {
            "type": "string",
            "format": "date",
            "description": "Collection last update date",
            "example": "2026-10-02T10:00:40.694Z"
          },
          "deleted_at": {
            "type": "string",
            "format": "date",
            "description": "Collection deletion date",
            "example": "2026-10-02T10:00:40.694Z"
          },
          "deleted": {
            "type": "boolean",
            "description": "True if vehicle is deleted",
            "example": false
          }
        },
        "required": [
          "id",
          "organization_id",
          "project_id"
        ]
      },
      "Vehicles": {
        "type": "array",
        "items": {
          "$ref": "#/components/schemas/VehicleData"
        }
      },
      "PaginatedVehicles": {
        "type": "object",
        "properties": {
          "total": {
            "type": "number",
            "description": "Pagination total",
            "example": 1000
          },
          "limit": {
            "type": "number",
            "description": "Pagination limit",
            "example": 50
          },
          "offset": {
            "type": "number",
            "description": "Pagination offset",
            "example": 25
          },
          "pages": {
            "type": "number",
            "description": "Pagination number of pages",
            "example": 20
          },
          "page": {
            "type": "number",
            "description": "Pagination current page",
            "example": 1
          },
          "docs": {
            "$ref": "#/components/schemas/Vehicles"
          }
        }
      },
      "type": {
        "type": "string",
        "description": "Stop type",
        "example": "delivery",
        "enum": [
          "pickup",
          "delivery"
        ]
      },
      "requires": {
        "type": "array",
        "description": "The stop will be performed only by vehicles providing all the features included in this field.",
        "example": [
          "refrigerated",
          "fragile"
        ],
        "x-constraint": {
          "single": true
        },
        "items": {
          "type": "string"
        }
      },
      "time_windows": {
        "type": "array",
        "description": "Time when the tasks can be executed. List of start and end times defined in seconds since the start of the day. The example has a time window to make the visit from 8am to 5pm.",
        "items": {
          "$ref": "#/components/schemas/Timewindow"
        }
      },
      "custom_fields1": {
        "type": "object",
        "description": "Stop custom fields.",
        "example": {
          "order_reference": "ORD-2026-58291",
          "parcel_count": 2,
          "cod_amount": 24.9
        }
      },
      "status2": {
        "type": "string",
        "description": "Task status",
        "example": "completed",
        "enum": [
          "completed",
          "canceled",
          "pending"
        ]
      },
      "custom_fields2": {
        "type": "object",
        "description": "Task custom fields",
        "example": {
          "sku": "SKU-4582",
          "quantity": 2,
          "lot_number": "L-2026-038"
        }
      },
      "StopTask": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "Task ID",
            "example": "4f75d991ac359f8c4c79d762"
          },
          "stop_id": {
            "type": "string",
            "description": "Stop ID where the task belongs",
            "example": "4f75d991ac359f8c4c79d762"
          },
          "label": {
            "type": "string",
            "description": "Task label: what the driver has to do at the stop.",
            "example": "Deliver refrigerated parcel 1/2"
          },
          "comments": {
            "type": "string",
            "description": "Free-text notes shown alongside the task in the driver app.",
            "example": "Keep cold chain. Weight 12.4 kg."
          },
          "barcode": {
            "type": "string",
            "description": "Barcode the driver scans to confirm the task. Typically an EAN-13, GS1-128 or your internal SSCC.",
            "example": "8410076472158"
          },
          "status": {
            "$ref": "#/components/schemas/status2"
          },
          "custom_fields": {
            "$ref": "#/components/schemas/custom_fields2"
          },
          "created_by": {
            "type": "string",
            "description": "Collection user creator",
            "example": "4f75d991ac359f8c4c79d762"
          },
          "created_at": {
            "type": "string",
            "format": "date",
            "description": "Collection creation date",
            "example": "2026-10-02T10:00:40.832Z"
          },
          "updated_at": {
            "type": "string",
            "format": "date",
            "description": "Collection last update date",
            "example": "2026-10-02T10:00:40.832Z"
          },
          "deleted_at": {
            "type": "string",
            "format": "date",
            "description": "Collection deletion date",
            "example": "2026-10-02T10:00:40.833Z"
          }
        },
        "required": [
          "id",
          "stop_id",
          "status"
        ]
      },
      "StopTasks": {
        "type": "array",
        "items": {
          "$ref": "#/components/schemas/StopTask"
        }
      },
      "type1": {
        "type": "string",
        "description": "Report type",
        "example": "service_report_completed",
        "enum": [
          "service_report_completed",
          "service_report_canceled",
          "service_report_attempted"
        ]
      },
      "ImageData": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "Image ID",
            "example": "4f75d991ac359f8c4c79d762"
          },
          "url": {
            "type": "string",
            "description": "Image url",
            "example": "https://api.routal.com"
          }
        },
        "required": [
          "id",
          "url"
        ]
      },
      "ReportImages": {
        "type": "array",
        "items": {
          "$ref": "#/components/schemas/ImageData"
        }
      },
      "status3": {
        "type": "string",
        "description": "Report Task status",
        "example": "completed",
        "enum": [
          "completed",
          "canceled",
          "pending"
        ]
      },
      "custom_fields3": {
        "type": "object",
        "description": "Report Task custom fields",
        "example": {
          "sku": "SKU-4582",
          "quantity": 2,
          "lot_number": "L-2026-038"
        }
      },
      "ReportTask": {
        "type": "object",
        "properties": {
          "task_id": {
            "type": "string",
            "description": "Report Task ID",
            "example": "4f75d991ac359f8c4c79d762"
          },
          "status": {
            "$ref": "#/components/schemas/status3"
          },
          "comments": {
            "type": "string",
            "description": "Driver notes captured at the task.",
            "example": "Parcel delivered in perfect condition."
          },
          "custom_fields": {
            "$ref": "#/components/schemas/custom_fields3"
          }
        },
        "required": [
          "task_id",
          "status"
        ]
      },
      "ReportTasks": {
        "type": "array",
        "items": {
          "$ref": "#/components/schemas/ReportTask"
        }
      },
      "cancel_reason": {
        "type": "string",
        "description": "Report cancel reason",
        "example": "nobody",
        "enum": [
          "nobody",
          "wrong_address",
          "missing_information",
          "other"
        ]
      },
      "custom_fields4": {
        "type": "object",
        "description": "Stop custom fields.",
        "example": {
          "delivered_to": "neighbor 2A",
          "cod_collected": 24.9,
          "photo_count": 2
        }
      },
      "Report": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "Report ID",
            "example": "4f75d991ac359f8c4c79d762"
          },
          "stop_id": {
            "type": "string",
            "description": "Stop ID",
            "example": "4f75d991ac359f8c4c79d762"
          },
          "route_id": {
            "type": "string",
            "description": "Route ID",
            "example": "4f75d991ac359f8c4c79d762"
          },
          "project_id": {
            "type": "string",
            "description": "Project ID",
            "example": "4f75d991ac359f8c4c79d762"
          },
          "plan_id": {
            "type": "string",
            "description": "Plan ID",
            "example": "4f75d991ac359f8c4c79d762"
          },
          "type": {
            "$ref": "#/components/schemas/type1"
          },
          "comments": {
            "type": "string",
            "description": "Driver notes captured at the stop.",
            "example": "Customer not present. Notice left in the mailbox."
          },
          "images": {
            "$ref": "#/components/schemas/ReportImages"
          },
          "signature": {
            "$ref": "#/components/schemas/ImageData"
          },
          "tasks": {
            "$ref": "#/components/schemas/ReportTasks"
          },
          "cancel_reason": {
            "$ref": "#/components/schemas/cancel_reason"
          },
          "location": {
            "$ref": "#/components/schemas/Location"
          },
          "location_distance": {
            "type": "number",
            "description": "Straight-line distance, in metres, between the stop's planned location and where the driver closed the report. Absent when either point has no coordinates.",
            "example": 120
          },
          "time_in_stop": {
            "type": "number",
            "description": "Time spent at the stop, in seconds.",
            "example": 180
          },
          "custom_fields": {
            "$ref": "#/components/schemas/custom_fields4"
          },
          "created_at": {
            "type": "string",
            "format": "date",
            "description": "Collection creation date",
            "example": "2026-10-02T10:00:40.833Z"
          }
        },
        "required": [
          "id",
          "stop_id",
          "project_id",
          "type"
        ]
      },
      "Reports": {
        "type": "array",
        "items": {
          "$ref": "#/components/schemas/Report"
        }
      },
      "rating_tag": {
        "type": "string",
        "description": "Survey additional message",
        "example": "time",
        "enum": [
          "time",
          "condition",
          "driver",
          "info"
        ]
      },
      "survey": {
        "type": "object",
        "description": "Stop survey",
        "properties": {
          "stop_id": {
            "type": "string",
            "description": "Stop ID",
            "example": "4f75d991ac359f8c4c79d762"
          },
          "route_id": {
            "type": "string",
            "description": "Route ID",
            "example": "4f75d991ac359f8c4c79d762"
          },
          "rating": {
            "type": "number",
            "minimum": 1,
            "maximum": 5
          },
          "rating_tag": {
            "$ref": "#/components/schemas/rating_tag"
          },
          "comments": {
            "type": "string",
            "example": "The delivery was very professional and punctual."
          },
          "created_at": {
            "type": "string",
            "format": "date",
            "description": "Collection creation date",
            "example": "2026-10-02T10:00:40.837Z"
          },
          "updated_at": {
            "type": "string",
            "format": "date",
            "description": "Collection last update date",
            "example": "2026-10-02T10:00:40.837Z"
          }
        },
        "required": [
          "rating"
        ]
      },
      "supervisors": {
        "type": "array",
        "description": "Stop supervisors",
        "example": [
          "4f75d991ac359f8c4c79d762"
        ],
        "items": {
          "type": "string"
        }
      },
      "Model3": {
        "type": "object",
        "properties": {
          "identifier": {
            "type": "string",
            "description": "Package ID",
            "example": "4f75d991ac359f8c4c79d762"
          },
          "status": {
            "$ref": "#/components/schemas/PackageStatus"
          },
          "picked_at": {
            "type": "string",
            "format": "date",
            "description": "Package picked date",
            "example": "2026-10-02T10:00:40.850Z"
          },
          "delivered_at": {
            "type": "string",
            "format": "date",
            "description": "Package delivered date",
            "example": "2026-10-02T10:00:40.850Z"
          },
          "updated_at": {
            "type": "string",
            "format": "date",
            "description": "Package last update date",
            "example": "2026-10-02T10:00:40.850Z"
          }
        },
        "required": [
          "identifier",
          "status"
        ]
      },
      "packages": {
        "type": "array",
        "items": {
          "$ref": "#/components/schemas/Model3"
        }
      },
      "status5": {
        "type": "string",
        "description": "Stop Status",
        "example": "pending",
        "enum": [
          "pending",
          "incomplete",
          "completed",
          "canceled"
        ]
      },
      "pickup": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "Stop ID",
            "example": "4f75d991ac359f8c4c79d762"
          },
          "fixed_id": {
            "type": "string",
            "description": "Fixed Stop ID",
            "example": "1"
          },
          "organization_id": {
            "type": "string",
            "description": "Project ID",
            "example": "4f75d991ac359f8c4c79d762"
          },
          "project_id": {
            "type": "string",
            "description": "Project ID",
            "example": "4f75d991ac359f8c4c79d762"
          },
          "plan_id": {
            "type": "string",
            "description": "Plan ID",
            "example": "4f75d991ac359f8c4c79d762"
          },
          "plan_label": {
            "type": "string",
            "description": "Denormalized label of the assigned plan; populated at read time via the search endpoint.",
            "example": "March deliveries"
          },
          "tracking_url": {
            "type": "string",
            "description": "Customer-facing tracking page for this stop. Use it as returned instead of assembling the URL: it is derived from the project's current slug on every read, so it stays correct when the slug or the URL format changes.",
            "example": "https://c.routal.com/my-project/stop/6ab1c40b9b49b284e13b0503"
          },
          "type": {
            "$ref": "#/components/schemas/type"
          },
          "route_id": {
            "type": "string",
            "description": "Route ID",
            "example": "4f75d991ac359f8c4c79d762"
          },
          "vehicle_id": {
            "type": "string",
            "description": "Vehicle ID",
            "example": "4f75d991ac359f8c4c79d762"
          },
          "client_id": {
            "type": "string",
            "description": "Client ID",
            "example": "4f75d991ac359f8c4c79d762"
          },
          "client_external_id": {
            "type": "string",
            "description": "Customer ID in your own system (CRM, ERP, e-commerce…).",
            "example": "CUST-58291"
          },
          "external_id": {
            "type": "string",
            "description": "Order / shipment ID in your own system. Useful to correlate webhooks with your DB.",
            "example": "ORD-2026-58291"
          },
          "location": {
            "$ref": "#/components/schemas/Location"
          },
          "location_details": {
            "type": "string",
            "description": "Address line 2: floor, intercom, gate, dock, building name",
            "example": "Local 2, junto a la entrada principal"
          },
          "label": {
            "type": "string",
            "description": "Stop label",
            "example": "Acme Pharmacy, Aragó branch"
          },
          "comments": {
            "type": "string",
            "description": "Driver-facing comments about the stop.",
            "example": "Ring intercom 2B. Do not leave at reception."
          },
          "phone": {
            "type": "string",
            "description": "Customer phone. In E.164 format.",
            "example": "+34612345678"
          },
          "email": {
            "type": "string",
            "description": "Customer email used for tracking notifications.",
            "example": "sarah.johnson@example.com"
          },
          "url": {
            "type": "string",
            "description": "Reference URL (your order page, customer portal…)",
            "example": "https://shop.example.com/orders/58291"
          },
          "reference_person": {
            "type": "string",
            "description": "Person to ask for on arrival.",
            "example": "Sarah Johnson"
          },
          "duration": {
            "type": "number",
            "description": "Duration of the stop in seconds (e.g. 3 min = 180).",
            "example": 180
          },
          "requires": {
            "$ref": "#/components/schemas/requires"
          },
          "cluster": {
            "type": "string",
            "description": "Stop cluster",
            "example": "eixample-norte"
          },
          "reward": {
            "type": "number",
            "description": "Reward for the stop (used by the optimizer to prioritise high-value visits).",
            "example": 5
          },
          "time_windows": {
            "$ref": "#/components/schemas/time_windows"
          },
          "volume": {
            "type": "number",
            "description": "Total volume that needs the stop (m³ by default; or \"u\" for parcel count).",
            "example": 0.12
          },
          "weight": {
            "type": "number",
            "description": "Total weight that needs the stop (kg by default).",
            "example": 8.4
          },
          "price": {
            "type": "number",
            "description": "Price of the stop.",
            "example": 8.4
          },
          "max_delivery_time": {
            "type": "number",
            "description": "Max time in seconds between the pickup and this delivery (chain).",
            "example": 7200
          },
          "status": {
            "$ref": "#/components/schemas/status5"
          },
          "plan_execution_date": {
            "type": "string",
            "format": "date",
            "description": "Plan execution date.",
            "example": "2026-10-02T10:00:40.849Z"
          },
          "order": {
            "type": "number",
            "description": "Stop order in list",
            "example": 0
          },
          "planned_arrival_time": {
            "type": "number",
            "description": "Stop planned arrival time.",
            "example": 29500
          },
          "planned_departure_time": {
            "type": "number",
            "description": "Stop planned departure time",
            "example": 30000
          },
          "estimated_arrival_time": {
            "type": "string",
            "format": "date",
            "description": "Stop estimated arrival time.",
            "example": "2026-10-02T10:00:40.849Z"
          },
          "estimated_departure_time": {
            "type": "string",
            "format": "date",
            "description": "Stop estimated departure time",
            "example": "2026-10-02T10:00:40.849Z"
          },
          "pickup_id": {
            "type": "string",
            "description": "Stop pickup ID",
            "example": "4f75d991ac359f8c4c79d762"
          },
          "distance_to_previous_stop": {
            "type": "number",
            "description": "Distance to the previous stop.",
            "example": 30000
          },
          "distance_to_next_stop": {
            "type": "number",
            "description": "Distance to the next stop.",
            "example": 20000
          },
          "optimization_warning": {
            "type": "boolean",
            "description": "True if the stop has an optimization warning"
          },
          "chain_id": {
            "type": "string",
            "description": "Chain group identifier. All stops with the same chain_id are served by the same vehicle.",
            "example": "order-58291"
          },
          "chain_position": {
            "type": "integer",
            "description": "Position of this stop within its chain (0 = first, 1 = next…).",
            "example": 1
          },
          "custom_fields": {
            "$ref": "#/components/schemas/custom_fields1"
          },
          "tasks": {
            "$ref": "#/components/schemas/StopTasks"
          },
          "reports": {
            "$ref": "#/components/schemas/Reports"
          },
          "report_attempts": {
            "type": "number",
            "example": 2
          },
          "survey": {
            "$ref": "#/components/schemas/survey"
          },
          "pin": {
            "type": "string",
            "description": "Delivery PIN the customer reads to the driver to confirm the handover.",
            "example": "4729"
          },
          "customer_text_field": {
            "type": "string",
            "description": "Free-text field captured from the customer (delivery instructions, tracking page…).",
            "example": "Leave at reception if I'm not home."
          },
          "supervisors": {
            "$ref": "#/components/schemas/supervisors"
          },
          "location_validation_sent": {
            "type": "boolean",
            "description": "True if the stop location validation has been sent to the customer",
            "example": false
          },
          "location_validation_sent_at": {
            "type": "string",
            "format": "date",
            "description": "Location validation sent at",
            "example": "2026-10-02T10:00:40.850Z"
          },
          "location_validated": {
            "type": "boolean",
            "description": "True if the stop location has been validated",
            "example": false
          },
          "location_validated_at": {
            "type": "string",
            "format": "date",
            "description": "Location validated at",
            "example": "2026-10-02T10:00:40.850Z"
          },
          "packages": {
            "$ref": "#/components/schemas/packages"
          },
          "created_by": {
            "type": "string",
            "description": "Collection user creator",
            "example": "4f75d991ac359f8c4c79d762"
          },
          "created_at": {
            "type": "string",
            "format": "date",
            "description": "Collection creation date",
            "example": "2026-10-02T10:00:40.694Z"
          },
          "updated_at": {
            "type": "string",
            "format": "date",
            "description": "Collection last update date",
            "example": "2026-10-02T10:00:40.694Z"
          },
          "deleted_at": {
            "type": "string",
            "format": "date",
            "description": "Collection deletion date",
            "example": "2026-10-02T10:00:40.694Z"
          },
          "deleted": {
            "type": "boolean",
            "description": "True if stop is deleted"
          }
        },
        "required": [
          "project_id",
          "type"
        ]
      },
      "StopData": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "Stop ID",
            "example": "4f75d991ac359f8c4c79d762"
          },
          "fixed_id": {
            "type": "string",
            "description": "Fixed Stop ID",
            "example": "1"
          },
          "organization_id": {
            "type": "string",
            "description": "Project ID",
            "example": "4f75d991ac359f8c4c79d762"
          },
          "project_id": {
            "type": "string",
            "description": "Project ID",
            "example": "4f75d991ac359f8c4c79d762"
          },
          "plan_id": {
            "type": "string",
            "description": "Plan ID",
            "example": "4f75d991ac359f8c4c79d762"
          },
          "plan_label": {
            "type": "string",
            "description": "Denormalized label of the assigned plan; populated at read time via the search endpoint.",
            "example": "March deliveries"
          },
          "tracking_url": {
            "type": "string",
            "description": "Customer-facing tracking page for this stop. Use it as returned instead of assembling the URL: it is derived from the project's current slug on every read, so it stays correct when the slug or the URL format changes.",
            "example": "https://c.routal.com/my-project/stop/6ab1c40b9b49b284e13b0503"
          },
          "type": {
            "$ref": "#/components/schemas/type"
          },
          "route_id": {
            "type": "string",
            "description": "Route ID",
            "example": "4f75d991ac359f8c4c79d762"
          },
          "vehicle_id": {
            "type": "string",
            "description": "Vehicle ID",
            "example": "4f75d991ac359f8c4c79d762"
          },
          "client_id": {
            "type": "string",
            "description": "Client ID",
            "example": "4f75d991ac359f8c4c79d762"
          },
          "client_external_id": {
            "type": "string",
            "description": "Customer ID in your own system (CRM, ERP, e-commerce…).",
            "example": "CUST-58291"
          },
          "external_id": {
            "type": "string",
            "description": "Order / shipment ID in your own system. Useful to correlate webhooks with your DB.",
            "example": "ORD-2026-58291"
          },
          "location": {
            "$ref": "#/components/schemas/Location"
          },
          "location_details": {
            "type": "string",
            "description": "Address line 2: floor, intercom, gate, dock, building name",
            "example": "Local 2, junto a la entrada principal"
          },
          "label": {
            "type": "string",
            "description": "Stop label",
            "example": "Acme Pharmacy, Aragó branch"
          },
          "comments": {
            "type": "string",
            "description": "Driver-facing comments about the stop.",
            "example": "Ring intercom 2B. Do not leave at reception."
          },
          "phone": {
            "type": "string",
            "description": "Customer phone. In E.164 format.",
            "example": "+34612345678"
          },
          "email": {
            "type": "string",
            "description": "Customer email used for tracking notifications.",
            "example": "sarah.johnson@example.com"
          },
          "url": {
            "type": "string",
            "description": "Reference URL (your order page, customer portal…)",
            "example": "https://shop.example.com/orders/58291"
          },
          "reference_person": {
            "type": "string",
            "description": "Person to ask for on arrival.",
            "example": "Sarah Johnson"
          },
          "duration": {
            "type": "number",
            "description": "Duration of the stop in seconds (e.g. 3 min = 180).",
            "example": 180
          },
          "requires": {
            "$ref": "#/components/schemas/requires"
          },
          "cluster": {
            "type": "string",
            "description": "Stop cluster",
            "example": "eixample-norte"
          },
          "reward": {
            "type": "number",
            "description": "Reward for the stop (used by the optimizer to prioritise high-value visits).",
            "example": 5
          },
          "time_windows": {
            "$ref": "#/components/schemas/time_windows"
          },
          "volume": {
            "type": "number",
            "description": "Total volume that needs the stop (m³ by default; or \"u\" for parcel count).",
            "example": 0.12
          },
          "weight": {
            "type": "number",
            "description": "Total weight that needs the stop (kg by default).",
            "example": 8.4
          },
          "price": {
            "type": "number",
            "description": "Price of the stop.",
            "example": 8.4
          },
          "max_delivery_time": {
            "type": "number",
            "description": "Max time in seconds between the pickup and this delivery (chain).",
            "example": 7200
          },
          "status": {
            "$ref": "#/components/schemas/StopStatus"
          },
          "plan_execution_date": {
            "type": "string",
            "format": "date",
            "description": "Plan execution date.",
            "example": "2026-10-02T10:00:40.849Z"
          },
          "order": {
            "type": "number",
            "description": "Stop order in list",
            "example": 0
          },
          "planned_arrival_time": {
            "type": "number",
            "description": "Stop planned arrival time.",
            "example": 29500
          },
          "planned_departure_time": {
            "type": "number",
            "description": "Stop planned departure time",
            "example": 30000
          },
          "estimated_arrival_time": {
            "type": "string",
            "format": "date",
            "description": "Stop estimated arrival time.",
            "example": "2026-10-02T10:00:40.849Z"
          },
          "estimated_departure_time": {
            "type": "string",
            "format": "date",
            "description": "Stop estimated departure time",
            "example": "2026-10-02T10:00:40.849Z"
          },
          "pickup_id": {
            "type": "string",
            "description": "Stop pickup ID",
            "example": "4f75d991ac359f8c4c79d762"
          },
          "distance_to_previous_stop": {
            "type": "number",
            "description": "Distance to the previous stop.",
            "example": 30000
          },
          "distance_to_next_stop": {
            "type": "number",
            "description": "Distance to the next stop.",
            "example": 20000
          },
          "optimization_warning": {
            "type": "boolean",
            "description": "True if the stop has an optimization warning"
          },
          "chain_id": {
            "type": "string",
            "description": "Chain group identifier. All stops with the same chain_id are served by the same vehicle.",
            "example": "order-58291"
          },
          "chain_position": {
            "type": "integer",
            "description": "Position of this stop within its chain (0 = first, 1 = next…).",
            "example": 1
          },
          "custom_fields": {
            "$ref": "#/components/schemas/custom_fields1"
          },
          "tasks": {
            "$ref": "#/components/schemas/StopTasks"
          },
          "reports": {
            "$ref": "#/components/schemas/Reports"
          },
          "report_attempts": {
            "type": "number",
            "example": 2
          },
          "survey": {
            "$ref": "#/components/schemas/survey"
          },
          "pin": {
            "type": "string",
            "description": "Delivery PIN the customer reads to the driver to confirm the handover.",
            "example": "4729"
          },
          "customer_text_field": {
            "type": "string",
            "description": "Free-text field captured from the customer (delivery instructions, tracking page…).",
            "example": "Leave at reception if I'm not home."
          },
          "supervisors": {
            "$ref": "#/components/schemas/supervisors"
          },
          "location_validation_sent": {
            "type": "boolean",
            "description": "True if the stop location validation has been sent to the customer",
            "example": false
          },
          "location_validation_sent_at": {
            "type": "string",
            "format": "date",
            "description": "Location validation sent at",
            "example": "2026-10-02T10:00:40.850Z"
          },
          "location_validated": {
            "type": "boolean",
            "description": "True if the stop location has been validated",
            "example": false
          },
          "location_validated_at": {
            "type": "string",
            "format": "date",
            "description": "Location validated at",
            "example": "2026-10-02T10:00:40.850Z"
          },
          "packages": {
            "$ref": "#/components/schemas/packages"
          },
          "created_by": {
            "type": "string",
            "description": "Collection user creator",
            "example": "4f75d991ac359f8c4c79d762"
          },
          "created_at": {
            "type": "string",
            "format": "date",
            "description": "Collection creation date",
            "example": "2026-10-02T10:00:40.694Z"
          },
          "updated_at": {
            "type": "string",
            "format": "date",
            "description": "Collection last update date",
            "example": "2026-10-02T10:00:40.694Z"
          },
          "deleted_at": {
            "type": "string",
            "format": "date",
            "description": "Collection deletion date",
            "example": "2026-10-02T10:00:40.694Z"
          },
          "deleted": {
            "type": "boolean",
            "description": "True if stop is deleted"
          },
          "pickup": {
            "$ref": "#/components/schemas/pickup"
          }
        },
        "required": [
          "project_id",
          "type"
        ]
      },
      "StopsData": {
        "type": "array",
        "items": {
          "$ref": "#/components/schemas/StopData"
        }
      },
      "break": {
        "type": "object",
        "properties": {
          "start": {
            "type": "number",
            "description": "Earliest second-of-day at which the break can start (e.g. 12:00 = 12 * 3600).",
            "example": 43200
          },
          "end": {
            "type": "number",
            "description": "Latest second-of-day by which the break must end (e.g. 15:00 = 15 * 3600).",
            "example": 54000
          },
          "duration": {
            "type": "number",
            "description": "Total break duration in seconds (e.g. 45 min = 2700).",
            "example": 2700
          }
        },
        "required": [
          "start",
          "end",
          "duration"
        ]
      },
      "vehicle": {
        "type": "object",
        "description": "Routes vehicle",
        "properties": {
          "fuel_type": {
            "type": "string",
            "description": "Vehicle fuel type",
            "example": "diesel"
          },
          "consumption": {
            "type": "number",
            "description": "Vehicle consumption",
            "example": 8
          },
          "break": {
            "$ref": "#/components/schemas/break"
          }
        }
      },
      "geo_fences": {
        "type": "array",
        "example": [
          "4f75d991ac359f8c4c79d762"
        ],
        "x-constraint": {
          "single": true
        },
        "items": {
          "type": "string"
        }
      },
      "Provides": {
        "type": "array",
        "description": "Skills or equipment the vehicle provides, matched against stop `requires`.",
        "example": [
          "refrigerated",
          "lift_gate"
        ],
        "items": {
          "type": "string"
        }
      },
      "break_stop": {
        "type": "object",
        "properties": {
          "is_completed": {
            "type": "boolean",
            "description": "Break stop is completed",
            "example": false
          },
          "order": {
            "type": "number",
            "description": "Break stop order in route stop list",
            "example": 2
          },
          "planned_start_time": {
            "type": "number",
            "description": "Break stop planned start time",
            "example": 20000
          },
          "planned_end_time": {
            "type": "number",
            "description": "Break stop planned end time",
            "example": 30000
          },
          "real_start_time": {
            "type": "number",
            "description": "Break stop real start time",
            "example": 20000
          },
          "real_end_time": {
            "type": "number",
            "description": "Break stop real end time",
            "example": 30000
          }
        }
      },
      "RoutesWithStops": {
        "type": "array",
        "description": "Plan routes",
        "items": {
          "$ref": "#/components/schemas/Route_With_Stops"
        }
      },
      "FullPlanData": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "Plan ID",
            "example": "4f75d991ac359f8c4c79d762"
          },
          "organization_id": {
            "type": "string",
            "description": "Organization ID",
            "example": "4f75d991ac359f8c4c79d762"
          },
          "project_id": {
            "type": "string",
            "description": "Project ID",
            "example": "4f75d991ac359f8c4c79d762"
          },
          "completed_stops": {
            "type": "number",
            "description": "Number of completed stops",
            "example": 1
          },
          "canceled_stops": {
            "type": "number",
            "description": "Number of canceled stops",
            "example": 2
          },
          "pending_stops": {
            "type": "number",
            "description": "Number of pending stops",
            "example": 3
          },
          "incomplete_stops": {
            "type": "number",
            "description": "Number of incomplete stops, stops without all tasks completed",
            "example": 3
          },
          "total_stops": {
            "type": "number",
            "description": "Number of total stops",
            "example": 6
          },
          "total_routes": {
            "type": "number",
            "description": "Number of total routes",
            "example": 5
          },
          "label": {
            "type": "string",
            "description": "Plan label",
            "example": "Eixample route 2026-05-21"
          },
          "execution_date": {
            "type": "string",
            "format": "date",
            "description": "Plan execution date",
            "example": "2026-10-02T10:00:40.864Z"
          },
          "status": {
            "$ref": "#/components/schemas/PlanStatus"
          },
          "created_by": {
            "type": "string",
            "description": "Collection user creator",
            "example": "4f75d991ac359f8c4c79d762"
          },
          "created_at": {
            "type": "string",
            "format": "date",
            "description": "Collection creation date",
            "example": "2026-10-02T10:00:40.864Z"
          },
          "updated_at": {
            "type": "string",
            "format": "date",
            "description": "Collection last update date",
            "example": "2026-10-02T10:00:40.864Z"
          },
          "deleted_at": {
            "type": "string",
            "format": "date",
            "description": "Collection deletion date",
            "example": "2026-10-02T10:00:40.864Z"
          },
          "deleted": {
            "type": "boolean",
            "description": "True if plan is deleted"
          },
          "stops": {
            "$ref": "#/components/schemas/StopsData"
          },
          "routes": {
            "$ref": "#/components/schemas/RoutesWithStops"
          }
        },
        "required": [
          "id",
          "organization_id",
          "project_id"
        ]
      },
      "Location1": {
        "type": "object",
        "properties": {
          "location_id": {
            "type": "string",
            "description": "Location ID",
            "example": "4f75d991ac359f8c4c79d762"
          },
          "label": {
            "type": "string",
            "description": "Location label",
            "example": "Acme Pharmacy, Aragó branch"
          },
          "country": {
            "type": "string",
            "description": "Country",
            "example": "Spain"
          },
          "country_code": {
            "type": "string",
            "description": "Country code. Must be ISO 3166 Alpha-2 standard.",
            "example": "ES"
          },
          "state": {
            "type": "string",
            "description": "State or province",
            "example": "Catalonia"
          },
          "city": {
            "type": "string",
            "description": "City",
            "example": "Barcelona"
          },
          "street": {
            "type": "string",
            "description": "Street name",
            "example": "Aragó Street"
          },
          "postal_code": {
            "type": "string",
            "description": "Postal code",
            "example": "08015"
          },
          "comments": {
            "type": "string",
            "description": "Location comments, useful for arrival hints",
            "example": "Enter through the inner courtyard, ring intercom 2B"
          },
          "partial_match": {
            "type": "boolean",
            "description": "Indicates if the location geocoding was a partial match",
            "example": false
          },
          "lat": {
            "type": "number",
            "description": "Location latitude",
            "example": 41.3818,
            "minimum": -90,
            "maximum": 90
          },
          "lng": {
            "type": "number",
            "description": "Location longitude",
            "example": 2.1547,
            "minimum": -180,
            "maximum": 180
          },
          "main_text": {
            "type": "string",
            "description": "Main text",
            "example": "Aragó Street 245"
          },
          "secondary_text": {
            "type": "string",
            "description": "Secondary text",
            "example": "Eixample, Barcelona, Spain"
          }
        }
      },
      "status7": {
        "type": "string",
        "description": "Stop Status",
        "example": "pending",
        "enum": [
          "pending",
          "incomplete",
          "completed",
          "canceled"
        ]
      },
      "Timewindows": {
        "type": "array",
        "items": {
          "$ref": "#/components/schemas/Timewindow"
        }
      },
      "StringArray2": {
        "type": "array",
        "description": "Skills / equipment required at this stop (must match a vehicle `provides`).",
        "example": [
          "refrigerated",
          "fragile"
        ],
        "x-constraint": {
          "single": true
        },
        "items": {
          "type": "string",
          "minLength": 1
        }
      },
      "status8": {
        "type": "string",
        "description": "Task status",
        "example": "pending",
        "enum": [
          "completed",
          "canceled",
          "pending"
        ]
      },
      "custom_fields5": {
        "type": "object",
        "description": "Task custom fields. Must be defined previously in planner.",
        "example": {
          "sku": "SKU-4582",
          "quantity": 2,
          "lot_number": "L-2026-038"
        }
      },
      "Task": {
        "type": "object",
        "properties": {
          "label": {
            "type": "string",
            "description": "Task label: what the driver has to do at the stop.",
            "example": "Deliver refrigerated parcel 1/2"
          },
          "comments": {
            "type": "string",
            "description": "Free-text notes shown alongside the task in the driver app.",
            "example": "Keep cold chain. Weight 12.4 kg."
          },
          "barcode": {
            "type": "string",
            "description": "Barcode the driver scans to confirm the task. Typically an EAN-13, GS1-128 or your internal SSCC.",
            "example": "8410076472158"
          },
          "status": {
            "$ref": "#/components/schemas/status8"
          },
          "custom_fields": {
            "$ref": "#/components/schemas/custom_fields5"
          }
        }
      },
      "ReportTasks1": {
        "type": "array",
        "items": {
          "$ref": "#/components/schemas/Task"
        }
      },
      "Location2": {
        "type": "object",
        "properties": {
          "location_id": {
            "type": "string",
            "description": "Location ID",
            "example": "4f75d991ac359f8c4c79d762"
          },
          "label": {
            "type": "string",
            "description": "Location label",
            "example": "Acme Pharmacy, Aragó branch"
          },
          "country": {
            "type": "string",
            "description": "Country",
            "example": "Spain"
          },
          "country_code": {
            "type": "string",
            "description": "Country code. Must be ISO 3166 Alpha-2 standard.",
            "example": "ES"
          },
          "state": {
            "type": "string",
            "description": "State or province",
            "example": "Catalonia"
          },
          "city": {
            "type": "string",
            "description": "City",
            "example": "Barcelona"
          },
          "street": {
            "type": "string",
            "description": "Street name",
            "example": "Aragó Street"
          },
          "postal_code": {
            "type": "string",
            "description": "Postal code",
            "example": "08015"
          },
          "comments": {
            "type": "string",
            "description": "Location comments, useful for arrival hints",
            "example": "Enter through the inner courtyard, ring intercom 2B"
          },
          "partial_match": {
            "type": "boolean",
            "description": "Indicates if the location geocoding was a partial match",
            "example": false
          },
          "lat": {
            "type": "number",
            "description": "Location latitude",
            "example": 41.3818,
            "minimum": -90,
            "maximum": 90
          },
          "lng": {
            "type": "number",
            "description": "Location longitude",
            "example": 2.1547,
            "minimum": -180,
            "maximum": 180
          },
          "main_text": {
            "type": "string",
            "description": "Main text",
            "example": "Aragó Street 245"
          },
          "secondary_text": {
            "type": "string",
            "description": "Secondary text",
            "example": "Eixample, Barcelona, Spain"
          }
        }
      },
      "status9": {
        "type": "string",
        "description": "Stop Status",
        "example": "pending",
        "enum": [
          "pending",
          "incomplete",
          "completed",
          "canceled"
        ]
      },
      "StringArray3": {
        "type": "array",
        "description": "Skills / equipment required at this pickup (must match a vehicle `provides`).",
        "example": [
          "refrigerated",
          "lift_gate"
        ],
        "x-constraint": {
          "single": true
        },
        "items": {
          "type": "string",
          "minLength": 1
        }
      },
      "supervisors1": {
        "type": "array",
        "description": "Pickup supervisors",
        "example": [
          "4f75d991ac359f8c4c79d762"
        ],
        "items": {
          "type": "string"
        }
      },
      "ReportTasks2": {
        "type": "array",
        "items": {
          "$ref": "#/components/schemas/Task"
        }
      },
      "custom_fields6": {
        "type": "object",
        "description": "Pickup custom fields. The custom field must be previously created in planner.",
        "example": {
          "order_reference": "ORD-2026-58291",
          "parcel_count": 3,
          "return_slip": true
        }
      },
      "PackageInput": {
        "type": "object",
        "properties": {
          "identifier": {
            "type": "string",
            "description": "Package identifier",
            "example": "PKG-001"
          }
        },
        "required": [
          "identifier"
        ]
      },
      "packages1": {
        "type": "array",
        "description": "Reserved: pickups cannot carry packages, this field is rejected by the use case.",
        "items": {
          "$ref": "#/components/schemas/PackageInput"
        }
      },
      "Pickup": {
        "type": "object",
        "properties": {
          "client_id": {
            "type": "string",
            "description": "Client ID",
            "example": "4f75d991ac359f8c4c79d762"
          },
          "client_external_id": {
            "type": "string",
            "description": "Customer ID in your own system (CRM, ERP, e-commerce…).",
            "example": "CUST-58291"
          },
          "external_id": {
            "type": "string",
            "description": "Order / shipment ID in your own system. Useful to correlate webhooks with your DB.",
            "example": "ORD-2026-58291"
          },
          "label": {
            "type": "string",
            "description": "Stop label",
            "example": "Returns pickup at Acme Warehouse Coslada"
          },
          "location": {
            "$ref": "#/components/schemas/Location2"
          },
          "location_details": {
            "type": "string",
            "description": "Address line 2: warehouse dock numbers, gates, floors",
            "example": "Dock 4, left side"
          },
          "comments": {
            "type": "string",
            "description": "Driver-facing comments about the pickup.",
            "example": "Pick up 3 return boxes, request a signed delivery note"
          },
          "status": {
            "$ref": "#/components/schemas/status9"
          },
          "phone": {
            "type": "string",
            "description": "Contact phone. In E.164 format.",
            "example": "+34612345678"
          },
          "email": {
            "type": "string",
            "description": "Contact email",
            "example": "logistics@warehouse.example.com"
          },
          "url": {
            "type": "string",
            "description": "Reference URL (your order page, return slip…)",
            "example": "https://shop.example.com/orders/58291"
          },
          "reference_person": {
            "type": "string",
            "description": "Person to ask for on arrival",
            "example": "Sarah Johnson"
          },
          "price": {
            "type": "number",
            "description": "Price of the pickup.",
            "example": 8.4
          },
          "weight": {
            "type": "number",
            "description": "Weight of the pickup (kg by default).",
            "example": 12.5
          },
          "volume": {
            "type": "number",
            "description": "Volume of the pickup (m³ by default).",
            "example": 0.18
          },
          "time_windows": {
            "$ref": "#/components/schemas/Timewindows"
          },
          "requires": {
            "$ref": "#/components/schemas/StringArray3"
          },
          "cluster": {
            "type": "string",
            "description": "Cluster to which the pickup belongs. Must be created previously in planner.",
            "example": "zona-norte"
          },
          "duration": {
            "type": "number",
            "description": "Pickup duration in seconds (e.g. 5 min = 300).",
            "example": 300,
            "minimum": 0
          },
          "reward": {
            "type": "number",
            "description": "Reward for completing the pickup (used by the optimizer).",
            "example": 8
          },
          "max_delivery_time": {
            "type": "number",
            "description": "Max time in seconds between this pickup and its delivery (chain).",
            "example": 7200
          },
          "supervisors": {
            "$ref": "#/components/schemas/supervisors1"
          },
          "tasks": {
            "$ref": "#/components/schemas/ReportTasks2"
          },
          "custom_fields": {
            "$ref": "#/components/schemas/custom_fields6"
          },
          "chain_id": {
            "type": "string",
            "description": "Chain group identifier. All stops with the same chain_id are served by the same vehicle.",
            "example": "order-58291"
          },
          "chain_position": {
            "type": "integer",
            "description": "Position of this stop within its chain (0 = first, e.g. pickup before delivery).",
            "example": 0,
            "minimum": 0
          },
          "packages": {
            "$ref": "#/components/schemas/packages1"
          }
        }
      },
      "custom_fields7": {
        "type": "object",
        "description": "Stop custom fields. The custom field must be previously created in planner.",
        "example": {
          "order_reference": "ORD-2026-58291",
          "parcel_count": 2,
          "cod_amount": 24.9
        }
      },
      "packages2": {
        "type": "array",
        "description": "Packages associated to the stop. Only the identifier is accepted as input.",
        "items": {
          "$ref": "#/components/schemas/PackageInput"
        }
      },
      "Stop": {
        "type": "object",
        "properties": {
          "fixed_id": {
            "type": "string",
            "description": "Fixed Stop ID",
            "example": "1"
          },
          "client_id": {
            "type": "string",
            "description": "Client ID",
            "example": "4f75d991ac359f8c4c79d762"
          },
          "client_external_id": {
            "type": "string",
            "description": "Customer ID in your own system (CRM, ERP, e-commerce…).",
            "example": "CUST-58291"
          },
          "external_id": {
            "type": "string",
            "description": "Order / shipment ID in your own system. Useful to correlate webhooks with your DB.",
            "example": "ORD-2026-58291"
          },
          "label": {
            "type": "string",
            "description": "Stop label",
            "example": "Acme Pharmacy, Aragó branch"
          },
          "location": {
            "$ref": "#/components/schemas/Location1"
          },
          "location_details": {
            "type": "string",
            "description": "Address line 2: floor, intercom, gate, dock, building name",
            "example": "Unit 2, next to the main entrance"
          },
          "comments": {
            "type": "string",
            "description": "Driver-facing comments about the stop.",
            "example": "Ring intercom 2B. Do not leave at reception."
          },
          "status": {
            "$ref": "#/components/schemas/status7"
          },
          "phone": {
            "type": "string",
            "description": "Customer phone. In E.164 format.",
            "example": "+34612345678"
          },
          "email": {
            "type": "string",
            "description": "Customer email used for tracking notifications.",
            "example": "sarah.johnson@example.com"
          },
          "url": {
            "type": "string",
            "description": "Reference URL (your order page, customer portal…)",
            "example": "https://shop.example.com/orders/58291"
          },
          "reference_person": {
            "type": "string",
            "description": "Person to ask for on arrival.",
            "example": "Sarah Johnson"
          },
          "price": {
            "type": "number",
            "description": "Price of the stop.",
            "example": 8.4
          },
          "weight": {
            "type": "number",
            "description": "Weight of the stop (kg by default; the project unit is honoured).",
            "example": 8.4
          },
          "volume": {
            "type": "number",
            "description": "Volume of the stop (m³ by default; or \"u\" for parcel count).",
            "example": 0.12
          },
          "time_windows": {
            "$ref": "#/components/schemas/Timewindows"
          },
          "requires": {
            "$ref": "#/components/schemas/StringArray2"
          },
          "cluster": {
            "type": "string",
            "description": "Cluster to which the stop belongs. Must be created previously in planner.",
            "example": "eixample-norte"
          },
          "duration": {
            "type": "number",
            "description": "Stop duration in seconds (e.g. 3 min = 180).",
            "example": 180,
            "minimum": 0
          },
          "reward": {
            "type": "number",
            "description": "Reward for completing the stop (used by the optimizer to prioritise high-value visits).",
            "example": 5
          },
          "supervisors": {
            "$ref": "#/components/schemas/supervisors"
          },
          "tasks": {
            "$ref": "#/components/schemas/ReportTasks1"
          },
          "pickup": {
            "$ref": "#/components/schemas/Pickup"
          },
          "custom_fields": {
            "$ref": "#/components/schemas/custom_fields7"
          },
          "chain_id": {
            "type": "string",
            "description": "Chain group identifier. All stops with the same chain_id are served by the same vehicle.",
            "example": "order-58291"
          },
          "chain_position": {
            "type": "integer",
            "description": "Position of this stop within its chain (0 = first, 1 = next…). Used to enforce ordering, e.g. pickup before delivery.",
            "example": 1,
            "minimum": 0
          },
          "packages": {
            "$ref": "#/components/schemas/packages2"
          }
        }
      },
      "stops": {
        "type": "array",
        "x-constraint": {
          "single": true
        },
        "items": {
          "$ref": "#/components/schemas/Stop"
        }
      },
      "Location3": {
        "type": "object",
        "properties": {
          "location_id": {
            "type": "string",
            "description": "Location ID",
            "example": "4f75d991ac359f8c4c79d762"
          },
          "label": {
            "type": "string",
            "description": "Location label",
            "example": "Acme Pharmacy, Aragó branch"
          },
          "country": {
            "type": "string",
            "description": "Country",
            "example": "Spain"
          },
          "country_code": {
            "type": "string",
            "description": "Country code. Must be ISO 3166 Alpha-2 standard.",
            "example": "ES"
          },
          "state": {
            "type": "string",
            "description": "State or province",
            "example": "Catalonia"
          },
          "city": {
            "type": "string",
            "description": "City",
            "example": "Barcelona"
          },
          "street": {
            "type": "string",
            "description": "Street name",
            "example": "Aragó Street"
          },
          "postal_code": {
            "type": "string",
            "description": "Postal code",
            "example": "08015"
          },
          "comments": {
            "type": "string",
            "description": "Location comments, useful for arrival hints",
            "example": "Enter through the inner courtyard, ring intercom 2B"
          },
          "partial_match": {
            "type": "boolean",
            "description": "Indicates if the location geocoding was a partial match",
            "example": false
          },
          "lat": {
            "type": "number",
            "description": "Location latitude",
            "example": 41.3818,
            "minimum": -90,
            "maximum": 90
          },
          "lng": {
            "type": "number",
            "description": "Location longitude",
            "example": 2.1547,
            "minimum": -180,
            "maximum": 180
          },
          "main_text": {
            "type": "string",
            "description": "Main text",
            "example": "Aragó Street 245"
          },
          "secondary_text": {
            "type": "string",
            "description": "Secondary text",
            "example": "Eixample, Barcelona, Spain"
          }
        }
      },
      "Location4": {
        "type": "object",
        "properties": {
          "location_id": {
            "type": "string",
            "description": "Location ID",
            "example": "4f75d991ac359f8c4c79d762"
          },
          "label": {
            "type": "string",
            "description": "Location label",
            "example": "Acme Pharmacy, Aragó branch"
          },
          "country": {
            "type": "string",
            "description": "Country",
            "example": "Spain"
          },
          "country_code": {
            "type": "string",
            "description": "Country code. Must be ISO 3166 Alpha-2 standard.",
            "example": "ES"
          },
          "state": {
            "type": "string",
            "description": "State or province",
            "example": "Catalonia"
          },
          "city": {
            "type": "string",
            "description": "City",
            "example": "Barcelona"
          },
          "street": {
            "type": "string",
            "description": "Street name",
            "example": "Aragó Street"
          },
          "postal_code": {
            "type": "string",
            "description": "Postal code",
            "example": "08015"
          },
          "comments": {
            "type": "string",
            "description": "Location comments, useful for arrival hints",
            "example": "Enter through the inner courtyard, ring intercom 2B"
          },
          "partial_match": {
            "type": "boolean",
            "description": "Indicates if the location geocoding was a partial match",
            "example": false
          },
          "lat": {
            "type": "number",
            "description": "Location latitude",
            "example": 41.3818,
            "minimum": -90,
            "maximum": 90
          },
          "lng": {
            "type": "number",
            "description": "Location longitude",
            "example": 2.1547,
            "minimum": -180,
            "maximum": 180
          },
          "main_text": {
            "type": "string",
            "description": "Main text",
            "example": "Aragó Street 245"
          },
          "secondary_text": {
            "type": "string",
            "description": "Secondary text",
            "example": "Eixample, Barcelona, Spain"
          }
        }
      },
      "geo_fences1": {
        "type": "array",
        "description": "Geo fence to which the route belongs. Must be created previously in planner.",
        "example": [
          "4f75d991ac359f8c4c79d762"
        ],
        "x-constraint": {
          "single": true
        },
        "items": {
          "type": "string"
        }
      },
      "break1": {
        "type": "object",
        "description": "Break (lunch / rest) constraint applied to this route.",
        "example": {
          "start": 43200,
          "end": 54000,
          "duration": 2700
        },
        "properties": {
          "start": {
            "type": "number",
            "description": "Earliest second-of-day at which the break can start (e.g. 12:00 = 12 * 3600).",
            "example": 43200
          },
          "end": {
            "type": "number",
            "description": "Latest second-of-day by which the break must end (e.g. 15:00 = 15 * 3600).",
            "example": 54000
          },
          "duration": {
            "type": "number",
            "description": "Total break duration in seconds (e.g. 45 min = 2700).",
            "example": 2700
          }
        },
        "required": [
          "start",
          "end",
          "duration"
        ]
      },
      "StringArray4": {
        "type": "array",
        "description": "What the vehicle provides, matched against the `requires` of each stop.",
        "example": [
          "refrigerated",
          "lift_gate"
        ],
        "x-constraint": {
          "single": true
        },
        "items": {
          "type": "string",
          "minLength": 1
        }
      },
      "custom_fields8": {
        "type": "object",
        "description": "Route custom fields. The custom field must be previously created in planner.",
        "example": {
          "depot": "MAD-COSLADA",
          "shift": "morning",
          "driver_id": "EMP-1042"
        }
      },
      "status10": {
        "type": "string",
        "description": "Driver Status",
        "example": "not_started",
        "enum": [
          "not_started",
          "in_transit",
          "finished"
        ]
      },
      "Route": {
        "type": "object",
        "properties": {
          "vehicle_id": {
            "type": "string",
            "description": "Vehicle ID",
            "example": "4f75d991ac359f8c4c79d762"
          },
          "external_id": {
            "type": "string",
            "description": "Route ID in your own system.",
            "example": "ROUTE-2026-05-21-MAD03"
          },
          "start_location": {
            "$ref": "#/components/schemas/Location3"
          },
          "end_location": {
            "$ref": "#/components/schemas/Location4"
          },
          "label": {
            "type": "string",
            "description": "Route label",
            "example": "Eixample route, refrigerated van 02"
          },
          "phone": {
            "type": "string",
            "description": "Driver phone. In E.164 format.",
            "example": "+34612345678"
          },
          "comments": {
            "type": "string",
            "description": "Internal notes about the route, shown to the dispatcher.",
            "example": "Load at 06:00 at dock 4. Depart 06:30."
          },
          "email": {
            "type": "string",
            "description": "Driver email used to dispatch the route link.",
            "example": "carlos.lopez@example.com"
          },
          "plate": {
            "type": "string",
            "description": "Vehicle plate",
            "example": "2596 KHO"
          },
          "vehicle_model": {
            "type": "string",
            "description": "Vehicle model",
            "example": "Transit Custom L2H2"
          },
          "color": {
            "type": "string",
            "description": "Vehicle color. In hex format.",
            "example": "#1E88E5"
          },
          "brand": {
            "type": "string",
            "description": "Vehicle brand",
            "example": "Ford"
          },
          "is_locked": {
            "type": "boolean",
            "description": "True if route is locked",
            "example": false
          },
          "geo_fences": {
            "$ref": "#/components/schemas/geo_fences1"
          },
          "time_window": {
            "$ref": "#/components/schemas/Timewindow"
          },
          "max_distance": {
            "type": "number",
            "description": "Max distance the route can travel. In meters (e.g. 120 km = 120000).",
            "example": 120000
          },
          "min_distance": {
            "type": "number",
            "description": "Min distance the route can travel. In meters.",
            "example": 5000
          },
          "max_time": {
            "type": "number",
            "description": "Max time the route can spend. In seconds (e.g. 8h = 28800).",
            "example": 28800
          },
          "min_time": {
            "type": "number",
            "description": "Min time the route can spend. In seconds.",
            "example": 3600
          },
          "max_weight": {
            "type": "number",
            "description": "Max weight the route can carry (kg).",
            "example": 800
          },
          "max_volume": {
            "type": "number",
            "description": "Max volume the route can carry (m³ or parcel count).",
            "example": 8
          },
          "max_services": {
            "type": "number",
            "description": "Max stops the route can perform.",
            "example": 40
          },
          "break": {
            "$ref": "#/components/schemas/break1"
          },
          "provides": {
            "$ref": "#/components/schemas/StringArray4"
          },
          "custom_fields": {
            "$ref": "#/components/schemas/custom_fields8"
          },
          "price_per_distance": {
            "type": "number",
            "description": "Cost per meter, used for route costing.",
            "example": 0.00045
          },
          "price_per_minute": {
            "type": "number",
            "description": "Cost per second, used for route costing.",
            "example": 0.0035
          },
          "price_per_stop": {
            "type": "number",
            "description": "Cost per stop, used for route costing.",
            "example": 0.5
          },
          "status": {
            "$ref": "#/components/schemas/status10"
          }
        }
      },
      "Routes": {
        "type": "array",
        "x-constraint": {
          "single": true
        },
        "items": {
          "$ref": "#/components/schemas/Route"
        }
      },
      "geo_fences2": {
        "type": "array",
        "description": "Geo fences that the plan has enabled. Must be created previously in planner. DEPRECATED: The geo fences are now managed in a project level.",
        "example": [
          "4f75d991ac359f8c4c79d762"
        ],
        "x-constraint": {
          "single": true
        },
        "items": {
          "type": "string"
        }
      },
      "PlanData": {
        "type": "object",
        "properties": {
          "external_id": {
            "type": "string",
            "description": "Plan ID in your own system. The typical pattern is a daily batch id.",
            "example": "BATCH-2026-05-21-MAD"
          },
          "label": {
            "type": "string",
            "description": "Plan label shown to the dispatcher.",
            "example": "Eixample route 2026-05-21"
          },
          "status": {
            "$ref": "#/components/schemas/PlanStatus"
          },
          "stops": {
            "$ref": "#/components/schemas/stops"
          },
          "routes": {
            "$ref": "#/components/schemas/Routes"
          },
          "geo_fences": {
            "$ref": "#/components/schemas/geo_fences2"
          },
          "execution_date": {
            "type": "string",
            "format": "date",
            "description": "Plan execution date",
            "example": "2026-05-21T05:00:00.000Z"
          }
        }
      },
      "status11": {
        "type": "string",
        "description": "Task status",
        "example": "pending",
        "enum": [
          "completed",
          "canceled",
          "pending"
        ]
      },
      "Task1": {
        "type": "object",
        "properties": {
          "label": {
            "type": "string",
            "description": "Task label: what the driver has to do at the stop.",
            "example": "Deliver refrigerated parcel 1/2"
          },
          "comments": {
            "type": "string",
            "description": "Free-text notes shown alongside the task in the driver app.",
            "example": "Keep cold chain. Weight 12.4 kg."
          },
          "barcode": {
            "type": "string",
            "description": "Barcode the driver scans to confirm the task. Typically an EAN-13, GS1-128 or your internal SSCC.",
            "example": "8410076472158"
          },
          "status": {
            "$ref": "#/components/schemas/status11"
          },
          "custom_fields": {
            "$ref": "#/components/schemas/custom_fields5"
          }
        }
      },
      "Model4": {
        "type": "string",
        "enum": [
          "Monday",
          "Tuesday",
          "Wednesday",
          "Thursday",
          "Friday",
          "Saturday",
          "Sunday"
        ]
      },
      "working_days": {
        "type": "array",
        "example": [
          "Monday",
          "Tuesday",
          "Wednesday",
          "Thursday",
          "Friday"
        ],
        "items": {
          "$ref": "#/components/schemas/Model4"
        }
      },
      "Location5": {
        "type": "object",
        "description": "Default start location of the vehicle.",
        "properties": {
          "location_id": {
            "type": "string",
            "description": "Location ID",
            "example": "4f75d991ac359f8c4c79d762"
          },
          "label": {
            "type": "string",
            "description": "Location label",
            "example": "Acme Pharmacy, Aragó branch"
          },
          "country": {
            "type": "string",
            "description": "Country",
            "example": "Spain"
          },
          "country_code": {
            "type": "string",
            "description": "Country code. Must be ISO 3166 Alpha-2 standard.",
            "example": "ES"
          },
          "state": {
            "type": "string",
            "description": "State or province",
            "example": "Catalonia"
          },
          "city": {
            "type": "string",
            "description": "City",
            "example": "Barcelona"
          },
          "street": {
            "type": "string",
            "description": "Street name",
            "example": "Aragó Street"
          },
          "postal_code": {
            "type": "string",
            "description": "Postal code",
            "example": "08015"
          },
          "comments": {
            "type": "string",
            "description": "Location comments, useful for arrival hints",
            "example": "Enter through the inner courtyard, ring intercom 2B"
          },
          "partial_match": {
            "type": "boolean",
            "description": "Indicates if the location geocoding was a partial match",
            "example": false
          },
          "lat": {
            "type": "number",
            "description": "Location latitude",
            "example": 41.3818,
            "minimum": -90,
            "maximum": 90
          },
          "lng": {
            "type": "number",
            "description": "Location longitude",
            "example": 2.1547,
            "minimum": -180,
            "maximum": 180
          },
          "main_text": {
            "type": "string",
            "description": "Main text",
            "example": "Aragó Street 245"
          },
          "secondary_text": {
            "type": "string",
            "description": "Secondary text",
            "example": "Eixample, Barcelona, Spain"
          }
        }
      },
      "Location6": {
        "type": "object",
        "description": "Default end location of the vehicle.",
        "properties": {
          "location_id": {
            "type": "string",
            "description": "Location ID",
            "example": "4f75d991ac359f8c4c79d762"
          },
          "label": {
            "type": "string",
            "description": "Location label",
            "example": "Acme Pharmacy, Aragó branch"
          },
          "country": {
            "type": "string",
            "description": "Country",
            "example": "Spain"
          },
          "country_code": {
            "type": "string",
            "description": "Country code. Must be ISO 3166 Alpha-2 standard.",
            "example": "ES"
          },
          "state": {
            "type": "string",
            "description": "State or province",
            "example": "Catalonia"
          },
          "city": {
            "type": "string",
            "description": "City",
            "example": "Barcelona"
          },
          "street": {
            "type": "string",
            "description": "Street name",
            "example": "Aragó Street"
          },
          "postal_code": {
            "type": "string",
            "description": "Postal code",
            "example": "08015"
          },
          "comments": {
            "type": "string",
            "description": "Location comments, useful for arrival hints",
            "example": "Enter through the inner courtyard, ring intercom 2B"
          },
          "partial_match": {
            "type": "boolean",
            "description": "Indicates if the location geocoding was a partial match",
            "example": false
          },
          "lat": {
            "type": "number",
            "description": "Location latitude",
            "example": 41.3818,
            "minimum": -90,
            "maximum": 90
          },
          "lng": {
            "type": "number",
            "description": "Location longitude",
            "example": 2.1547,
            "minimum": -180,
            "maximum": 180
          },
          "main_text": {
            "type": "string",
            "description": "Main text",
            "example": "Aragó Street 245"
          },
          "secondary_text": {
            "type": "string",
            "description": "Secondary text",
            "example": "Eixample, Barcelona, Spain"
          }
        }
      },
      "custom_fields9": {
        "type": "object",
        "description": "Vehicle custom fields. Must be defined previously in planner.",
        "example": {
          "depot": "MAD-COSLADA",
          "shift": "morning",
          "driver_id": "EMP-1042"
        }
      },
      "Vehicle": {
        "type": "object",
        "properties": {
          "external_id": {
            "type": "string",
            "description": "Vehicle ID in your own system (fleet ERP, payroll…).",
            "example": "VEH-MAD-02"
          },
          "label": {
            "type": "string",
            "description": "Vehicle label shown to the dispatcher.",
            "example": "Refrigerated van, Madrid Downtown 02"
          },
          "phone": {
            "type": "string",
            "description": "Driver phone. In E.164 format.",
            "example": "+34612345678"
          },
          "comments": {
            "type": "string",
            "description": "Internal notes about the vehicle / driver.",
            "example": "Access to central depots only before 09:00."
          },
          "email": {
            "type": "string",
            "description": "Driver email used to dispatch the route link.",
            "example": "carlos.lopez@example.com"
          },
          "color": {
            "type": "string",
            "description": "Vehicle color. In hex format.",
            "example": "#1E88E5"
          },
          "time_window": {
            "$ref": "#/components/schemas/Timewindow"
          },
          "working_days": {
            "$ref": "#/components/schemas/working_days"
          },
          "min_distance": {
            "type": "number",
            "description": "Default min distance the vehicle can travel. In meters.",
            "example": 5000,
            "minimum": 0
          },
          "max_distance": {
            "type": "number",
            "description": "Default max distance the vehicle can travel. In meters (e.g. 120 km = 120000).",
            "example": 120000,
            "minimum": 0
          },
          "max_time": {
            "type": "number",
            "description": "Default max time the vehicle can spend. In seconds (e.g. 8h = 28800).",
            "example": 28800,
            "minimum": 0
          },
          "min_time": {
            "type": "number",
            "description": "Default min time the vehicle can spend. In seconds.",
            "example": 3600,
            "minimum": 0
          },
          "start_location": {
            "$ref": "#/components/schemas/Location5"
          },
          "end_location": {
            "$ref": "#/components/schemas/Location6"
          },
          "custom_fields": {
            "$ref": "#/components/schemas/custom_fields9"
          },
          "price_per_distance": {
            "type": "number",
            "description": "Cost per meter, used for route costing.",
            "example": 0.00045
          },
          "price_per_minute": {
            "type": "number",
            "description": "Cost per second, used for route costing.",
            "example": 0.0035
          },
          "price_per_stop": {
            "type": "number",
            "description": "Cost per stop, used for route costing.",
            "example": 0.5
          }
        }
      },
      "CreateVehiclesValidator": {
        "type": "array",
        "x-constraint": {
          "single": true
        },
        "items": {
          "$ref": "#/components/schemas/Vehicle"
        }
      },
      "connector": {
        "type": "string",
        "enum": [
          "tipsa",
          "google_spreadsheets"
        ]
      },
      "Model5": {
        "type": "object",
        "properties": {
          "project_id": {
            "type": "string"
          },
          "connector": {
            "$ref": "#/components/schemas/connector"
          },
          "params": {
            "type": "object",
            "properties": {
              "string": {
                "type": "string",
                "x-alternatives": [
                  {
                    "type": "string"
                  },
                  {
                    "type": "number"
                  },
                  {
                    "type": "boolean"
                  }
                ]
              }
            }
          }
        },
        "required": [
          "project_id",
          "connector",
          "params"
        ]
      },
      "Model6": {
        "type": "object",
        "properties": {
          "connector": {
            "$ref": "#/components/schemas/connector"
          },
          "date": {
            "type": "string",
            "format": "date"
          }
        },
        "required": [
          "connector"
        ]
      },
      "selections": {
        "type": "array",
        "minItems": 1,
        "items": {
          "$ref": "#/components/schemas/Model6"
        }
      },
      "Model7": {
        "type": "object",
        "properties": {
          "project_id": {
            "type": "string"
          },
          "selections": {
            "$ref": "#/components/schemas/selections"
          }
        },
        "required": [
          "project_id",
          "selections"
        ]
      },
      "stop_ids": {
        "type": "array",
        "x-constraint": {
          "single": true
        },
        "items": {
          "type": "string",
          "description": "List of stops",
          "example": "4f75d991ac359f8c4c79d762",
          "pattern": "^[0-9a-f]{24}"
        }
      },
      "status12": {
        "type": "string",
        "description": "Stop Status",
        "example": "pending",
        "enum": [
          "pending",
          "incomplete",
          "completed",
          "canceled"
        ]
      },
      "StringArray5": {
        "type": "array",
        "description": "Skills / equipment required at this stop (must match a vehicle `provides`).",
        "example": [
          "refrigerated",
          "fragile"
        ],
        "x-constraint": {
          "single": true
        },
        "items": {
          "type": "string",
          "minLength": 1
        }
      },
      "ReportTasks3": {
        "type": "array",
        "items": {
          "$ref": "#/components/schemas/Task"
        }
      },
      "packages3": {
        "type": "array",
        "description": "Packages associated to the stop. Only the identifier is accepted as input.",
        "items": {
          "$ref": "#/components/schemas/PackageInput"
        }
      },
      "status13": {
        "type": "string",
        "description": "Stop Status",
        "example": "pending",
        "enum": [
          "pending",
          "incomplete",
          "completed",
          "canceled"
        ]
      },
      "StringArray6": {
        "type": "array",
        "description": "Skills / equipment required at this pickup (must match a vehicle `provides`).",
        "example": [
          "refrigerated",
          "lift_gate"
        ],
        "x-constraint": {
          "single": true
        },
        "items": {
          "type": "string",
          "minLength": 1
        }
      },
      "supervisors2": {
        "type": "array",
        "description": "Pickup supervisors",
        "example": [
          "4f75d991ac359f8c4c79d762"
        ],
        "items": {
          "type": "string"
        }
      },
      "ReportTasks4": {
        "type": "array",
        "items": {
          "$ref": "#/components/schemas/Task"
        }
      },
      "packages4": {
        "type": "array",
        "description": "Reserved: pickups cannot carry packages, this field is rejected by the use case.",
        "items": {
          "$ref": "#/components/schemas/PackageInput"
        }
      },
      "PickupWithGeocode": {
        "type": "object",
        "properties": {
          "client_id": {
            "type": "string",
            "description": "Client ID",
            "example": "4f75d991ac359f8c4c79d762"
          },
          "client_external_id": {
            "type": "string",
            "description": "Customer ID in your own system (CRM, ERP, e-commerce…).",
            "example": "CUST-58291"
          },
          "external_id": {
            "type": "string",
            "description": "Order / shipment ID in your own system. Useful to correlate webhooks with your DB.",
            "example": "ORD-2026-58291"
          },
          "label": {
            "type": "string",
            "description": "Stop label",
            "example": "Returns pickup at Acme Warehouse Coslada"
          },
          "location_details": {
            "type": "string",
            "description": "Address line 2: warehouse dock numbers, gates, floors",
            "example": "Dock 4, left side"
          },
          "comments": {
            "type": "string",
            "description": "Driver-facing comments about the pickup.",
            "example": "Pick up 3 return boxes, request a signed delivery note"
          },
          "status": {
            "$ref": "#/components/schemas/status13"
          },
          "phone": {
            "type": "string",
            "description": "Contact phone. In E.164 format.",
            "example": "+34612345678"
          },
          "email": {
            "type": "string",
            "description": "Contact email",
            "example": "logistics@warehouse.example.com"
          },
          "url": {
            "type": "string",
            "description": "Reference URL (your order page, return slip…)",
            "example": "https://shop.example.com/orders/58291"
          },
          "reference_person": {
            "type": "string",
            "description": "Person to ask for on arrival",
            "example": "Sarah Johnson"
          },
          "price": {
            "type": "number",
            "description": "Price of the pickup.",
            "example": 8.4
          },
          "weight": {
            "type": "number",
            "description": "Weight of the pickup (kg by default).",
            "example": 12.5
          },
          "volume": {
            "type": "number",
            "description": "Volume of the pickup (m³ by default).",
            "example": 0.18
          },
          "time_windows": {
            "$ref": "#/components/schemas/Timewindows"
          },
          "requires": {
            "$ref": "#/components/schemas/StringArray6"
          },
          "cluster": {
            "type": "string",
            "description": "Cluster to which the pickup belongs. Must be created previously in planner.",
            "example": "zona-norte"
          },
          "duration": {
            "type": "number",
            "description": "Pickup duration in seconds (e.g. 5 min = 300).",
            "example": 300,
            "minimum": 0
          },
          "reward": {
            "type": "number",
            "description": "Reward for completing the pickup (used by the optimizer).",
            "example": 8
          },
          "max_delivery_time": {
            "type": "number",
            "description": "Max time in seconds between this pickup and its delivery (chain).",
            "example": 7200
          },
          "supervisors": {
            "$ref": "#/components/schemas/supervisors2"
          },
          "tasks": {
            "$ref": "#/components/schemas/ReportTasks4"
          },
          "custom_fields": {
            "$ref": "#/components/schemas/custom_fields6"
          },
          "chain_id": {
            "type": "string",
            "description": "Chain group identifier. All stops with the same chain_id are served by the same vehicle.",
            "example": "order-58291"
          },
          "chain_position": {
            "type": "integer",
            "description": "Position of this stop within its chain (0 = first, e.g. pickup before delivery).",
            "example": 0,
            "minimum": 0
          },
          "packages": {
            "$ref": "#/components/schemas/packages4"
          },
          "address": {
            "type": "string",
            "description": "Free-text address to geocode. Include city and country whenever you can, because accuracy drops on ambiguous strings like \"Calle Mayor 5\" alone.",
            "example": "Aragó Street 245, 08015 Barcelona, Spain",
            "minLength": 1,
            "x-convert": {
              "trim": true
            }
          }
        },
        "required": [
          "address"
        ]
      },
      "StopWithGeocode": {
        "type": "object",
        "properties": {
          "fixed_id": {
            "type": "string",
            "description": "Fixed Stop ID",
            "example": "1"
          },
          "client_id": {
            "type": "string",
            "description": "Client ID",
            "example": "4f75d991ac359f8c4c79d762"
          },
          "client_external_id": {
            "type": "string",
            "description": "Customer ID in your own system (CRM, ERP, e-commerce…).",
            "example": "CUST-58291"
          },
          "external_id": {
            "type": "string",
            "description": "Order / shipment ID in your own system. Useful to correlate webhooks with your DB.",
            "example": "ORD-2026-58291"
          },
          "label": {
            "type": "string",
            "description": "Stop label",
            "example": "Acme Pharmacy, Aragó branch"
          },
          "location_details": {
            "type": "string",
            "description": "Address line 2: floor, intercom, gate, dock, building name",
            "example": "Unit 2, next to the main entrance"
          },
          "comments": {
            "type": "string",
            "description": "Driver-facing comments about the stop.",
            "example": "Ring intercom 2B. Do not leave at reception."
          },
          "status": {
            "$ref": "#/components/schemas/status12"
          },
          "phone": {
            "type": "string",
            "description": "Customer phone. In E.164 format.",
            "example": "+34612345678"
          },
          "email": {
            "type": "string",
            "description": "Customer email used for tracking notifications.",
            "example": "sarah.johnson@example.com"
          },
          "url": {
            "type": "string",
            "description": "Reference URL (your order page, customer portal…)",
            "example": "https://shop.example.com/orders/58291"
          },
          "reference_person": {
            "type": "string",
            "description": "Person to ask for on arrival.",
            "example": "Sarah Johnson"
          },
          "price": {
            "type": "number",
            "description": "Price of the stop.",
            "example": 8.4
          },
          "weight": {
            "type": "number",
            "description": "Weight of the stop (kg by default; the project unit is honoured).",
            "example": 8.4
          },
          "volume": {
            "type": "number",
            "description": "Volume of the stop (m³ by default; or \"u\" for parcel count).",
            "example": 0.12
          },
          "time_windows": {
            "$ref": "#/components/schemas/Timewindows"
          },
          "requires": {
            "$ref": "#/components/schemas/StringArray5"
          },
          "cluster": {
            "type": "string",
            "description": "Cluster to which the stop belongs. Must be created previously in planner.",
            "example": "eixample-norte"
          },
          "duration": {
            "type": "number",
            "description": "Stop duration in seconds (e.g. 3 min = 180).",
            "example": 180,
            "minimum": 0
          },
          "reward": {
            "type": "number",
            "description": "Reward for completing the stop (used by the optimizer to prioritise high-value visits).",
            "example": 5
          },
          "supervisors": {
            "$ref": "#/components/schemas/supervisors"
          },
          "tasks": {
            "$ref": "#/components/schemas/ReportTasks3"
          },
          "custom_fields": {
            "$ref": "#/components/schemas/custom_fields7"
          },
          "chain_id": {
            "type": "string",
            "description": "Chain group identifier. All stops with the same chain_id are served by the same vehicle.",
            "example": "order-58291"
          },
          "chain_position": {
            "type": "integer",
            "description": "Position of this stop within its chain (0 = first, 1 = next…). Used to enforce ordering, e.g. pickup before delivery.",
            "example": 1,
            "minimum": 0
          },
          "packages": {
            "$ref": "#/components/schemas/packages3"
          },
          "address": {
            "type": "string",
            "description": "Free-text address to geocode. Include city and country whenever you can, because accuracy drops on ambiguous strings like \"Calle Mayor 5\" alone.",
            "example": "Aragó Street 245, 08015 Barcelona, Spain",
            "minLength": 1,
            "x-convert": {
              "trim": true
            }
          },
          "pickup": {
            "$ref": "#/components/schemas/PickupWithGeocode"
          }
        },
        "required": [
          "address"
        ]
      },
      "stopsWithGeocode": {
        "type": "array",
        "x-constraint": {
          "single": true
        },
        "items": {
          "$ref": "#/components/schemas/StopWithGeocode"
        }
      },
      "sort_direction": {
        "type": "string",
        "description": "Sort order",
        "default": "desc",
        "enum": [
          "asc",
          "desc"
        ]
      },
      "type2": {
        "type": "string",
        "description": "Type of the value",
        "example": "string",
        "enum": [
          "string",
          "array",
          "number",
          "date",
          "boolean",
          "geojson",
          "id"
        ]
      },
      "Model8": {
        "type": "object",
        "properties": {
          "field": {
            "type": "string",
            "description": "Field to search",
            "example": "label"
          },
          "operator": {
            "type": "string",
            "description": "Operator to use",
            "example": "eq"
          },
          "value": {
            "type": "string",
            "description": "Value to search",
            "example": "Acme Pharmacy, Aragó branch"
          },
          "type": {
            "$ref": "#/components/schemas/type2"
          }
        },
        "required": [
          "field",
          "operator",
          "value",
          "type"
        ]
      },
      "predicates": {
        "type": "array",
        "description": "Predicates to search",
        "items": {
          "$ref": "#/components/schemas/Model8"
        }
      },
      "StopsSearch": {
        "type": "object",
        "properties": {
          "limit": {
            "type": "integer",
            "description": "Limit of results",
            "default": 100,
            "minimum": 1,
            "maximum": 1000
          },
          "offset": {
            "type": "integer",
            "description": "Offset of results",
            "default": 0,
            "minimum": 0
          },
          "sort_by": {
            "type": "string",
            "description": "Sort by field",
            "default": "created_at"
          },
          "sort_direction": {
            "$ref": "#/components/schemas/sort_direction"
          },
          "predicates": {
            "$ref": "#/components/schemas/predicates"
          }
        }
      },
      "SearchStopsPagination": {
        "type": "object",
        "properties": {
          "total": {
            "type": "number",
            "description": "Pagination total",
            "example": 1000
          },
          "limit": {
            "type": "number",
            "description": "Pagination limit",
            "example": 50
          },
          "offset": {
            "type": "number",
            "description": "Pagination offset",
            "example": 25
          },
          "pages": {
            "type": "number",
            "description": "Pagination number of pages",
            "example": 20
          },
          "page": {
            "type": "number",
            "description": "Pagination current page",
            "example": 1
          },
          "docs": {
            "$ref": "#/components/schemas/StopsData"
          }
        }
      },
      "Model9": {
        "type": "object"
      },
      "predicates1": {
        "type": "array",
        "items": {
          "$ref": "#/components/schemas/Model9"
        }
      },
      "Model10": {
        "type": "object"
      },
      "previous_predicates": {
        "type": "array",
        "description": "Comparison window for include_diagnosis (same shape as predicates).",
        "items": {
          "$ref": "#/components/schemas/Model10"
        }
      },
      "sort_direction1": {
        "type": "string",
        "default": "desc",
        "enum": [
          "asc",
          "desc"
        ]
      },
      "Model11": {
        "type": "object",
        "properties": {
          "predicates": {
            "$ref": "#/components/schemas/predicates1"
          },
          "previous_predicates": {
            "$ref": "#/components/schemas/previous_predicates"
          },
          "include_diagnosis": {
            "type": "boolean",
            "description": "Adds per-vehicle diagnosis: the verdict (`diagnosis`) plus the inputs it was computed from (`diagnosis_stats`: value, previous window, fleet distribution). Requires previous_predicates — without a comparison window there is nothing to judge against.",
            "default": false
          },
          "sort_by": {
            "type": "string",
            "description": "Column id to sort by (metric or identity)."
          },
          "sort_direction": {
            "$ref": "#/components/schemas/sort_direction1"
          },
          "text": {
            "type": "string",
            "description": "Free-text filter on vehicle fields."
          },
          "offset": {
            "type": "integer",
            "default": 0,
            "minimum": 0
          },
          "limit": {
            "type": "integer",
            "default": 100,
            "minimum": 1
          }
        }
      },
      "vehicle_ids": {
        "type": "array",
        "description": "The vehicles on screen. Only these are reported on; an empty array is valid.",
        "items": {
          "type": "string",
          "example": "4f75d991ac359f8c4c79d762",
          "pattern": "^[0-9a-f]{24}"
        }
      },
      "Model12": {
        "type": "object",
        "properties": {
          "vehicle_ids": {
            "$ref": "#/components/schemas/vehicle_ids"
          }
        },
        "required": [
          "vehicle_ids"
        ]
      },
      "Model13": {
        "type": "object",
        "properties": {
          "connector": {
            "$ref": "#/components/schemas/connector"
          },
          "params": {
            "type": "object",
            "properties": {
              "string": {
                "type": "string",
                "x-alternatives": [
                  {
                    "type": "string"
                  },
                  {
                    "type": "number"
                  },
                  {
                    "type": "boolean"
                  }
                ]
              }
            }
          }
        },
        "required": [
          "connector",
          "params"
        ]
      },
      "status14": {
        "type": "string",
        "description": "Route Status",
        "example": "not_started",
        "enum": [
          "not_started",
          "in_transit",
          "finished"
        ]
      },
      "route": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "Routes ID",
            "example": "4f75d991ac359f8c4c79d762"
          },
          "project_id": {
            "type": "string",
            "description": "Project ID",
            "example": "4f75d991ac359f8c4c79d762"
          },
          "plan_id": {
            "type": "string",
            "description": "Plan ID",
            "example": "4f75d991ac359f8c4c79d762"
          },
          "vehicle_id": {
            "type": "string",
            "description": "Client ID",
            "example": "4f75d991ac359f8c4c79d762"
          },
          "external_id": {
            "type": "string",
            "description": "Route ID in your own system.",
            "example": "ROUTE-2026-05-21-MAD03"
          },
          "start_location": {
            "$ref": "#/components/schemas/Location"
          },
          "end_location": {
            "$ref": "#/components/schemas/Location"
          },
          "label": {
            "type": "string",
            "description": "Routes label",
            "example": "Eixample route, refrigerated van 02"
          },
          "vehicle_label": {
            "type": "string",
            "description": "Vehicle name (search only)",
            "example": "Refrigerated van 02"
          },
          "plan_label": {
            "type": "string",
            "description": "Plan name (search only)",
            "example": "Madrid — 2026-05-21"
          },
          "plan_execution_date": {
            "type": "string",
            "format": "date",
            "description": "Execution date of the plan the route belongs to (search only)",
            "example": "2026-05-21"
          },
          "phone": {
            "type": "string",
            "description": "Driver phone. In E.164 format.",
            "example": "+34612345678"
          },
          "comments": {
            "type": "string",
            "description": "Internal notes about the route, shown to the dispatcher.",
            "example": "Load at 06:00 at dock 4. Depart 06:30."
          },
          "email": {
            "type": "string",
            "description": "Driver email used to dispatch the route link.",
            "example": "carlos.lopez@example.com"
          },
          "plate": {
            "type": "string",
            "description": "Routes vehicle plate",
            "example": "2596 KHO"
          },
          "vehicle_model": {
            "type": "string",
            "description": "Routes vehicle model",
            "example": "Transit Custom L2H2"
          },
          "color": {
            "type": "string",
            "description": "Routes vehicle color",
            "example": "#1E88E5"
          },
          "brand": {
            "type": "string",
            "description": "Routes vehicle brand",
            "example": "Ford"
          },
          "vehicle": {
            "$ref": "#/components/schemas/vehicle"
          },
          "planned_track": {
            "type": "string",
            "description": "Expected route encoded in Polyline format (https://developers.google.com/maps/documentation/utilities/polylineutility)",
            "example": "spxsBsdb|Lymo`qvAx@TKvAr@K"
          },
          "is_locked": {
            "type": "boolean",
            "description": "Routes is locked"
          },
          "geo_fences": {
            "$ref": "#/components/schemas/geo_fences"
          },
          "time_window": {
            "$ref": "#/components/schemas/Timewindow"
          },
          "max_distance": {
            "type": "number",
            "description": "Max distance the route can travel. In meters (e.g. 120 km = 120000).",
            "example": 120000
          },
          "min_distance": {
            "type": "number",
            "description": "Min distance the route can travel. In meters.",
            "example": 5000
          },
          "max_time": {
            "type": "number",
            "description": "Max time the route can spend. In seconds (e.g. 8h = 28800).",
            "example": 28800
          },
          "min_time": {
            "type": "number",
            "description": "Min time the route can spend. In seconds.",
            "example": 3600
          },
          "max_weight": {
            "type": "number",
            "description": "Max weight the route can carry (kg).",
            "example": 800
          },
          "max_volume": {
            "type": "number",
            "description": "Max volume the route can carry (m³ or parcel count).",
            "example": 8
          },
          "max_services": {
            "type": "number",
            "description": "Max stops the route can perform.",
            "example": 40
          },
          "provides": {
            "$ref": "#/components/schemas/Provides"
          },
          "order": {
            "type": "number",
            "description": "Routes order in list",
            "example": 0
          },
          "start_date": {
            "type": "string",
            "format": "date",
            "description": "Routes start date",
            "example": "2026-10-02T10:00:40.857Z"
          },
          "end_date": {
            "type": "string",
            "format": "date",
            "description": "Routes end date",
            "example": "2026-10-02T10:00:40.857Z"
          },
          "estimated_end_time": {
            "type": "string",
            "format": "date",
            "description": "Routes estimated end time",
            "example": "2026-10-02T10:00:40.857Z"
          },
          "last_position": {
            "$ref": "#/components/schemas/Route_Last_Position"
          },
          "planned_start_time": {
            "type": "number",
            "description": "Planned route start, second-of-day (e.g. 06:30 = 23400).",
            "example": 23400
          },
          "planned_end_time": {
            "type": "number",
            "description": "Planned route end, second-of-day (e.g. 14:00 = 50400).",
            "example": 50400
          },
          "custom_fields": {
            "$ref": "#/components/schemas/custom_fields"
          },
          "distance_traveled": {
            "type": "number"
          },
          "tracing_percent": {
            "type": "number"
          },
          "price_per_minute": {
            "type": "number",
            "description": "Cost per second of route time.",
            "example": 0.0035
          },
          "price_per_distance": {
            "type": "number",
            "description": "Cost per meter of route distance.",
            "example": 0.00045
          },
          "price_per_stop": {
            "type": "number",
            "description": "Cost per stop of the route.",
            "example": 0.5
          },
          "toll_price": {
            "type": "number",
            "description": "Price of the tolls in the route",
            "example": 4.8
          },
          "toll_price_currency": {
            "type": "string",
            "description": "Currency for the tolls",
            "example": "EUR"
          },
          "status": {
            "$ref": "#/components/schemas/status14"
          },
          "created_by": {
            "type": "string",
            "description": "Collection user creator",
            "example": "4f75d991ac359f8c4c79d762"
          },
          "created_at": {
            "type": "string",
            "format": "date",
            "description": "Collection creation date",
            "example": "2026-10-02T10:00:40.694Z"
          },
          "updated_at": {
            "type": "string",
            "format": "date",
            "description": "Collection last update date",
            "example": "2026-10-02T10:00:40.694Z"
          },
          "deleted_at": {
            "type": "string",
            "format": "date",
            "description": "Collection deletion date",
            "example": "2026-10-02T10:00:40.694Z"
          },
          "deleted": {
            "type": "boolean",
            "description": "True if route is deleted"
          },
          "stops": {
            "$ref": "#/components/schemas/StopsData"
          },
          "break_stop": {
            "$ref": "#/components/schemas/break_stop"
          }
        },
        "required": [
          "project_id",
          "plan_id"
        ]
      },
      "OptimizedRoute": {
        "type": "object",
        "properties": {
          "route": {
            "$ref": "#/components/schemas/route"
          },
          "unassigned_stops": {
            "$ref": "#/components/schemas/StopsData"
          }
        }
      },
      "Model14": {
        "type": "object"
      },
      "predicates2": {
        "type": "array",
        "items": {
          "$ref": "#/components/schemas/Model14"
        }
      },
      "bucket": {
        "type": "string",
        "description": "Bucket size",
        "default": "day",
        "enum": [
          "day",
          "week"
        ]
      },
      "Model15": {
        "type": "object",
        "properties": {
          "predicates": {
            "$ref": "#/components/schemas/predicates2"
          },
          "bucket": {
            "$ref": "#/components/schemas/bucket"
          }
        }
      },
      "Model16": {
        "type": "object"
      },
      "predicates3": {
        "type": "array",
        "items": {
          "$ref": "#/components/schemas/Model16"
        }
      },
      "Model17": {
        "type": "object"
      },
      "previous_predicates1": {
        "type": "array",
        "items": {
          "$ref": "#/components/schemas/Model17"
        }
      },
      "Model18": {
        "type": "object",
        "properties": {
          "predicates": {
            "$ref": "#/components/schemas/predicates3"
          },
          "previous_predicates": {
            "$ref": "#/components/schemas/previous_predicates1"
          }
        }
      },
      "status15": {
        "type": "string",
        "description": "The status of the service report",
        "enum": [
          "service_report_completed",
          "service_report_canceled",
          "service_report_attempted"
        ]
      },
      "cancel_reason1": {
        "type": "string",
        "description": "Reason of the report cancellation. Only valid on a canceled or attempted report",
        "example": "other",
        "enum": [
          "nobody",
          "wrong_address",
          "missing_information",
          "other"
        ]
      },
      "custom_fields10": {
        "type": "object",
        "description": "Custom fields, validated against the project's report field definitions",
        "example": {
          "key": "value"
        }
      },
      "location": {
        "type": "object",
        "description": "Location where the report was made",
        "properties": {
          "lat": {
            "type": "number"
          },
          "lng": {
            "type": "number"
          }
        }
      },
      "done_location": {
        "type": "object",
        "description": "Deprecated alias of `location`, kept for backwards compatibility. Send `location` instead",
        "properties": {
          "lat": {
            "type": "number"
          },
          "lng": {
            "type": "number"
          }
        }
      },
      "images": {
        "type": "array",
        "description": "Proof-of-delivery images, each one a base64 data-URL. Requires the `drivers.advanced_pod` subscription item. On a multipart/form-data body send the files as `file_0`...`file_N` instead",
        "maxItems": 5,
        "items": {
          "$ref": "#/components/schemas/Report_Image"
        }
      },
      "CreateStopReport": {
        "type": "object",
        "properties": {
          "status": {
            "$ref": "#/components/schemas/status15"
          },
          "comments": {
            "type": "string",
            "description": "Free-text comments for the report",
            "example": "Customer requested cancellation"
          },
          "cancel_reason": {
            "$ref": "#/components/schemas/cancel_reason1"
          },
          "custom_fields": {
            "$ref": "#/components/schemas/custom_fields10"
          },
          "time_in_stop": {
            "type": "number",
            "description": "Time spent in stop, in seconds",
            "example": 123
          },
          "location": {
            "$ref": "#/components/schemas/location"
          },
          "done_location": {
            "$ref": "#/components/schemas/done_location"
          },
          "images": {
            "$ref": "#/components/schemas/images"
          },
          "report_params": {
            "type": "string",
            "description": "JSON-encoded report fields. multipart/form-data only; with application/json send them directly"
          },
          "files_metadata": {
            "type": "string",
            "description": "JSON-encoded array of `{ name, mime }`, one entry per `file_N`. multipart/form-data only"
          },
          "file_0": {
            "type": "string",
            "format": "binary",
            "maxLength": 2796203
          },
          "file_1": {
            "type": "string",
            "format": "binary",
            "maxLength": 2796203
          },
          "file_2": {
            "type": "string",
            "format": "binary",
            "maxLength": 2796203
          },
          "file_3": {
            "type": "string",
            "format": "binary",
            "maxLength": 2796203
          },
          "file_4": {
            "type": "string",
            "format": "binary",
            "maxLength": 2796203
          }
        }
      },
      "geo_fences3": {
        "type": "array",
        "description": "Geo fences that the plan has enabled. Must be created previously in planner. DEPRECATED: The geo fences are now managed in a project level.",
        "x-constraint": {
          "single": true
        },
        "items": {
          "type": "string"
        }
      },
      "PlanData1": {
        "type": "object",
        "properties": {
          "label": {
            "type": "string",
            "description": "Plan label shown to the dispatcher.",
            "example": "Eixample route 2026-05-21"
          },
          "status": {
            "$ref": "#/components/schemas/PlanStatus"
          },
          "geo_fences": {
            "$ref": "#/components/schemas/geo_fences3"
          },
          "execution_date": {
            "type": "string",
            "format": "date",
            "description": "Plan execution date",
            "example": "2026-05-21T05:00:00.000Z"
          }
        }
      },
      "Location7": {
        "type": "object",
        "properties": {
          "location_id": {
            "type": "string",
            "description": "Location ID",
            "example": "4f75d991ac359f8c4c79d762"
          },
          "label": {
            "type": "string",
            "description": "Location label",
            "example": "Acme Pharmacy, Aragó branch"
          },
          "country": {
            "type": "string",
            "description": "Country",
            "example": "Spain"
          },
          "country_code": {
            "type": "string",
            "description": "Country code. Must be ISO 3166 Alpha-2 standard.",
            "example": "ES"
          },
          "state": {
            "type": "string",
            "description": "State or province",
            "example": "Catalonia"
          },
          "city": {
            "type": "string",
            "description": "City",
            "example": "Barcelona"
          },
          "street": {
            "type": "string",
            "description": "Street name",
            "example": "Aragó Street"
          },
          "postal_code": {
            "type": "string",
            "description": "Postal code",
            "example": "08015"
          },
          "comments": {
            "type": "string",
            "description": "Location comments, useful for arrival hints",
            "example": "Enter through the inner courtyard, ring intercom 2B"
          },
          "partial_match": {
            "type": "boolean",
            "description": "Indicates if the location geocoding was a partial match",
            "example": false
          },
          "lat": {
            "type": "number",
            "description": "Location latitude",
            "example": 41.3818,
            "minimum": -90,
            "maximum": 90
          },
          "lng": {
            "type": "number",
            "description": "Location longitude",
            "example": 2.1547,
            "minimum": -180,
            "maximum": 180
          },
          "main_text": {
            "type": "string",
            "description": "Main text",
            "example": "Aragó Street 245"
          },
          "secondary_text": {
            "type": "string",
            "description": "Secondary text",
            "example": "Eixample, Barcelona, Spain"
          }
        }
      },
      "Location8": {
        "type": "object",
        "properties": {
          "location_id": {
            "type": "string",
            "description": "Location ID",
            "example": "4f75d991ac359f8c4c79d762"
          },
          "label": {
            "type": "string",
            "description": "Location label",
            "example": "Acme Pharmacy, Aragó branch"
          },
          "country": {
            "type": "string",
            "description": "Country",
            "example": "Spain"
          },
          "country_code": {
            "type": "string",
            "description": "Country code. Must be ISO 3166 Alpha-2 standard.",
            "example": "ES"
          },
          "state": {
            "type": "string",
            "description": "State or province",
            "example": "Catalonia"
          },
          "city": {
            "type": "string",
            "description": "City",
            "example": "Barcelona"
          },
          "street": {
            "type": "string",
            "description": "Street name",
            "example": "Aragó Street"
          },
          "postal_code": {
            "type": "string",
            "description": "Postal code",
            "example": "08015"
          },
          "comments": {
            "type": "string",
            "description": "Location comments, useful for arrival hints",
            "example": "Enter through the inner courtyard, ring intercom 2B"
          },
          "partial_match": {
            "type": "boolean",
            "description": "Indicates if the location geocoding was a partial match",
            "example": false
          },
          "lat": {
            "type": "number",
            "description": "Location latitude",
            "example": 41.3818,
            "minimum": -90,
            "maximum": 90
          },
          "lng": {
            "type": "number",
            "description": "Location longitude",
            "example": 2.1547,
            "minimum": -180,
            "maximum": 180
          },
          "main_text": {
            "type": "string",
            "description": "Main text",
            "example": "Aragó Street 245"
          },
          "secondary_text": {
            "type": "string",
            "description": "Secondary text",
            "example": "Eixample, Barcelona, Spain"
          }
        }
      },
      "geo_fences4": {
        "type": "array",
        "description": "Geo fence to which the route belongs. Must be created previously in planner.",
        "example": [
          "4f75d991ac359f8c4c79d762"
        ],
        "x-constraint": {
          "single": true
        },
        "items": {
          "type": "string"
        }
      },
      "StringArray7": {
        "type": "array",
        "description": "What the vehicle provides, matched against the `requires` of each stop.",
        "example": [
          "refrigerated",
          "lift_gate"
        ],
        "x-constraint": {
          "single": true
        },
        "items": {
          "type": "string",
          "minLength": 1
        }
      },
      "custom_fields11": {
        "type": "object",
        "description": "Route custom fields. The custom field must be previously created in planner.",
        "example": {
          "depot": "MAD-COSLADA",
          "shift": "morning",
          "driver_id": "EMP-1042"
        }
      },
      "break2": {
        "type": "object",
        "description": "Break (lunch / rest) configuration of the vehicle.",
        "example": {
          "start": 43200,
          "end": 54000,
          "duration": 2700
        },
        "properties": {
          "start": {
            "type": "number",
            "description": "Earliest second-of-day at which the break can start (e.g. 12:00 = 12 * 3600).",
            "example": 43200
          },
          "end": {
            "type": "number",
            "description": "Latest second-of-day by which the break must end (e.g. 15:00 = 15 * 3600).",
            "example": 54000
          },
          "duration": {
            "type": "number",
            "description": "Total break duration in seconds (e.g. 45 min = 2700).",
            "example": 2700
          }
        },
        "required": [
          "start",
          "end",
          "duration"
        ]
      },
      "break_stop1": {
        "type": "object",
        "description": "Break stop values",
        "properties": {
          "is_completed": {
            "type": "boolean",
            "description": "Break stop is completed",
            "example": false
          },
          "order": {
            "type": "number",
            "description": "Break stop order in route stop list",
            "example": 7
          },
          "planned_start_time": {
            "type": "number",
            "description": "Planned break start, second-of-day (e.g. 13:00 = 46800).",
            "example": 46800
          },
          "planned_end_time": {
            "type": "number",
            "description": "Planned break end, second-of-day (e.g. 13:45 = 49500).",
            "example": 49500
          },
          "real_start_time": {
            "type": "number",
            "description": "Actual break start as reported by the driver, second-of-day.",
            "example": 47100
          },
          "real_end_time": {
            "type": "number",
            "description": "Actual break end as reported by the driver, second-of-day.",
            "example": 49800
          }
        }
      },
      "RouteEdit": {
        "type": "object",
        "properties": {
          "vehicle_id": {
            "type": "string",
            "description": "Vehicle ID",
            "example": "4f75d991ac359f8c4c79d762"
          },
          "external_id": {
            "type": "string",
            "description": "Route ID in your own system.",
            "example": "ROUTE-2026-05-21-MAD03"
          },
          "start_location": {
            "$ref": "#/components/schemas/Location7"
          },
          "end_location": {
            "$ref": "#/components/schemas/Location8"
          },
          "label": {
            "type": "string",
            "description": "Route label",
            "example": "Eixample route, refrigerated van 02"
          },
          "comments": {
            "type": "string",
            "description": "Internal notes about the route, shown to the dispatcher.",
            "example": "Load at 06:00 at dock 4. Depart 06:30."
          },
          "plate": {
            "type": "string",
            "description": "Vehicle plate",
            "example": "2596 KHO"
          },
          "vehicle_model": {
            "type": "string",
            "description": "Vehicle model",
            "example": "Transit Custom L2H2"
          },
          "color": {
            "type": "string",
            "description": "Vehicle color. In hex format.",
            "example": "#1E88E5"
          },
          "brand": {
            "type": "string",
            "description": "Vehicle brand",
            "example": "Ford"
          },
          "is_locked": {
            "type": "boolean",
            "description": "True if route is locked",
            "example": false
          },
          "geo_fences": {
            "$ref": "#/components/schemas/geo_fences4"
          },
          "time_window": {
            "$ref": "#/components/schemas/Timewindow"
          },
          "max_distance": {
            "type": "number",
            "description": "Max distance the route can travel. In meters (e.g. 120 km = 120000).",
            "example": 120000,
            "minimum": 0
          },
          "min_distance": {
            "type": "number",
            "description": "Min distance the route can travel. In meters.",
            "example": 5000,
            "minimum": 0
          },
          "max_time": {
            "type": "number",
            "description": "Max time the route can spend. In seconds (e.g. 8h = 28800).",
            "example": 28800,
            "minimum": 0
          },
          "min_time": {
            "type": "number",
            "description": "Min time the route can spend. In seconds.",
            "example": 3600,
            "minimum": 0
          },
          "max_weight": {
            "type": "number",
            "description": "Max weight the route can carry (kg).",
            "example": 800,
            "minimum": 0
          },
          "max_volume": {
            "type": "number",
            "description": "Max volume the route can carry (m³ or parcel count).",
            "example": 8,
            "minimum": 0
          },
          "max_services": {
            "type": "number",
            "description": "Max stops the route can perform.",
            "example": 40,
            "minimum": 0
          },
          "provides": {
            "$ref": "#/components/schemas/StringArray7"
          },
          "custom_fields": {
            "$ref": "#/components/schemas/custom_fields11"
          },
          "price_per_distance": {
            "type": "number",
            "description": "Cost per meter, used for route costing.",
            "example": 0.00045
          },
          "price_per_minute": {
            "type": "number",
            "description": "Cost per second, used for route costing.",
            "example": 0.0035
          },
          "price_per_stop": {
            "type": "number",
            "description": "Cost per stop, used for route costing.",
            "example": 0.5
          },
          "break": {
            "$ref": "#/components/schemas/break2"
          },
          "break_stop": {
            "$ref": "#/components/schemas/break_stop1"
          }
        }
      },
      "status16": {
        "type": "string",
        "description": "Stop Status",
        "example": "pending",
        "enum": [
          "pending",
          "incomplete",
          "completed",
          "canceled"
        ]
      },
      "StringArray8": {
        "type": "array",
        "description": "Skills / equipment required at this stop (must match a vehicle `provides`).",
        "example": [
          "refrigerated",
          "fragile"
        ],
        "x-constraint": {
          "single": true
        },
        "items": {
          "type": "string",
          "minLength": 1
        }
      },
      "ReportTasks5": {
        "type": "array",
        "items": {
          "$ref": "#/components/schemas/Task"
        }
      },
      "packages5": {
        "type": "array",
        "description": "Packages associated to the stop. Only the identifier is accepted as input.",
        "items": {
          "$ref": "#/components/schemas/PackageInput"
        }
      },
      "Stop1": {
        "type": "object",
        "properties": {
          "fixed_id": {
            "type": "string",
            "description": "Fixed Stop ID",
            "example": "1"
          },
          "client_id": {
            "type": "string",
            "description": "Client ID",
            "example": "4f75d991ac359f8c4c79d762"
          },
          "client_external_id": {
            "type": "string",
            "description": "Customer ID in your own system (CRM, ERP, e-commerce…).",
            "example": "CUST-58291"
          },
          "external_id": {
            "type": "string",
            "description": "Order / shipment ID in your own system. Useful to correlate webhooks with your DB.",
            "example": "ORD-2026-58291"
          },
          "label": {
            "type": "string",
            "description": "Stop label",
            "example": "Acme Pharmacy, Aragó branch"
          },
          "location": {
            "$ref": "#/components/schemas/Location1"
          },
          "location_details": {
            "type": "string",
            "description": "Address line 2: floor, intercom, gate, dock, building name",
            "example": "Unit 2, next to the main entrance"
          },
          "comments": {
            "type": "string",
            "description": "Driver-facing comments about the stop.",
            "example": "Ring intercom 2B. Do not leave at reception."
          },
          "status": {
            "$ref": "#/components/schemas/status16"
          },
          "phone": {
            "type": "string",
            "description": "Customer phone. In E.164 format.",
            "example": "+34612345678"
          },
          "email": {
            "type": "string",
            "description": "Customer email used for tracking notifications.",
            "example": "sarah.johnson@example.com"
          },
          "url": {
            "type": "string",
            "description": "Reference URL (your order page, customer portal…)",
            "example": "https://shop.example.com/orders/58291"
          },
          "reference_person": {
            "type": "string",
            "description": "Person to ask for on arrival.",
            "example": "Sarah Johnson"
          },
          "price": {
            "type": "number",
            "description": "Price of the stop.",
            "example": 8.4
          },
          "weight": {
            "type": "number",
            "description": "Weight of the stop (kg by default; the project unit is honoured).",
            "example": 8.4
          },
          "volume": {
            "type": "number",
            "description": "Volume of the stop (m³ by default; or \"u\" for parcel count).",
            "example": 0.12
          },
          "time_windows": {
            "$ref": "#/components/schemas/Timewindows"
          },
          "requires": {
            "$ref": "#/components/schemas/StringArray8"
          },
          "cluster": {
            "type": "string",
            "description": "Cluster to which the stop belongs. Must be created previously in planner.",
            "example": "eixample-norte"
          },
          "duration": {
            "type": "number",
            "description": "Stop duration in seconds (e.g. 3 min = 180).",
            "example": 180,
            "minimum": 0
          },
          "reward": {
            "type": "number",
            "description": "Reward for completing the stop (used by the optimizer to prioritise high-value visits).",
            "example": 5
          },
          "supervisors": {
            "$ref": "#/components/schemas/supervisors"
          },
          "tasks": {
            "$ref": "#/components/schemas/ReportTasks5"
          },
          "pickup": {
            "$ref": "#/components/schemas/Pickup"
          },
          "custom_fields": {
            "$ref": "#/components/schemas/custom_fields7"
          },
          "chain_id": {
            "type": "string",
            "description": "Chain group identifier. All stops with the same chain_id are served by the same vehicle.",
            "example": "order-58291"
          },
          "chain_position": {
            "type": "integer",
            "description": "Position of this stop within its chain (0 = first, 1 = next…). Used to enforce ordering, e.g. pickup before delivery.",
            "example": 1,
            "minimum": 0
          },
          "packages": {
            "$ref": "#/components/schemas/packages5"
          }
        }
      },
      "Model19": {
        "type": "object",
        "properties": {
          "stop_ids": {
            "$ref": "#/components/schemas/Array_of_id1"
          }
        }
      },
      "status17": {
        "type": "string",
        "description": "Task status",
        "example": "pending",
        "enum": [
          "completed",
          "canceled",
          "pending"
        ]
      },
      "Task2": {
        "type": "object",
        "properties": {
          "label": {
            "type": "string",
            "description": "Task label: what the driver has to do at the stop.",
            "example": "Deliver refrigerated parcel 1/2"
          },
          "comments": {
            "type": "string",
            "description": "Free-text notes shown alongside the task in the driver app.",
            "example": "Keep cold chain. Weight 12.4 kg."
          },
          "barcode": {
            "type": "string",
            "description": "Barcode the driver scans to confirm the task. Typically an EAN-13, GS1-128 or your internal SSCC.",
            "example": "8410076472158"
          },
          "status": {
            "$ref": "#/components/schemas/status17"
          },
          "custom_fields": {
            "$ref": "#/components/schemas/custom_fields5"
          }
        }
      },
      "Model20": {
        "type": "string",
        "enum": [
          "Monday",
          "Tuesday",
          "Wednesday",
          "Thursday",
          "Friday",
          "Saturday",
          "Sunday"
        ]
      },
      "default_working_days1": {
        "type": "array",
        "description": "Vehicle working days.",
        "example": [
          "Monday",
          "Tuesday",
          "Wednesday",
          "Thursday",
          "Friday"
        ],
        "items": {
          "$ref": "#/components/schemas/Model20"
        }
      },
      "Location9": {
        "type": "object",
        "description": "Default start location of the vehicle.",
        "properties": {
          "location_id": {
            "type": "string",
            "description": "Location ID",
            "example": "4f75d991ac359f8c4c79d762"
          },
          "label": {
            "type": "string",
            "description": "Location label",
            "example": "Acme Pharmacy, Aragó branch"
          },
          "country": {
            "type": "string",
            "description": "Country",
            "example": "Spain"
          },
          "country_code": {
            "type": "string",
            "description": "Country code. Must be ISO 3166 Alpha-2 standard.",
            "example": "ES"
          },
          "state": {
            "type": "string",
            "description": "State or province",
            "example": "Catalonia"
          },
          "city": {
            "type": "string",
            "description": "City",
            "example": "Barcelona"
          },
          "street": {
            "type": "string",
            "description": "Street name",
            "example": "Aragó Street"
          },
          "postal_code": {
            "type": "string",
            "description": "Postal code",
            "example": "08015"
          },
          "comments": {
            "type": "string",
            "description": "Location comments, useful for arrival hints",
            "example": "Enter through the inner courtyard, ring intercom 2B"
          },
          "partial_match": {
            "type": "boolean",
            "description": "Indicates if the location geocoding was a partial match",
            "example": false
          },
          "lat": {
            "type": "number",
            "description": "Location latitude",
            "example": 41.3818,
            "minimum": -90,
            "maximum": 90
          },
          "lng": {
            "type": "number",
            "description": "Location longitude",
            "example": 2.1547,
            "minimum": -180,
            "maximum": 180
          },
          "main_text": {
            "type": "string",
            "description": "Main text",
            "example": "Aragó Street 245"
          },
          "secondary_text": {
            "type": "string",
            "description": "Secondary text",
            "example": "Eixample, Barcelona, Spain"
          }
        }
      },
      "Location10": {
        "type": "object",
        "description": "Default end location of the vehicle.",
        "properties": {
          "location_id": {
            "type": "string",
            "description": "Location ID",
            "example": "4f75d991ac359f8c4c79d762"
          },
          "label": {
            "type": "string",
            "description": "Location label",
            "example": "Acme Pharmacy, Aragó branch"
          },
          "country": {
            "type": "string",
            "description": "Country",
            "example": "Spain"
          },
          "country_code": {
            "type": "string",
            "description": "Country code. Must be ISO 3166 Alpha-2 standard.",
            "example": "ES"
          },
          "state": {
            "type": "string",
            "description": "State or province",
            "example": "Catalonia"
          },
          "city": {
            "type": "string",
            "description": "City",
            "example": "Barcelona"
          },
          "street": {
            "type": "string",
            "description": "Street name",
            "example": "Aragó Street"
          },
          "postal_code": {
            "type": "string",
            "description": "Postal code",
            "example": "08015"
          },
          "comments": {
            "type": "string",
            "description": "Location comments, useful for arrival hints",
            "example": "Enter through the inner courtyard, ring intercom 2B"
          },
          "partial_match": {
            "type": "boolean",
            "description": "Indicates if the location geocoding was a partial match",
            "example": false
          },
          "lat": {
            "type": "number",
            "description": "Location latitude",
            "example": 41.3818,
            "minimum": -90,
            "maximum": 90
          },
          "lng": {
            "type": "number",
            "description": "Location longitude",
            "example": 2.1547,
            "minimum": -180,
            "maximum": 180
          },
          "main_text": {
            "type": "string",
            "description": "Main text",
            "example": "Aragó Street 245"
          },
          "secondary_text": {
            "type": "string",
            "description": "Secondary text",
            "example": "Eixample, Barcelona, Spain"
          }
        }
      },
      "custom_fields12": {
        "type": "object",
        "description": "Vehicle custom fields. Must be defined previously in planner.",
        "example": {
          "depot": "MAD-COSLADA",
          "shift": "morning",
          "driver_id": "EMP-1042"
        }
      },
      "Vehicle1": {
        "type": "object",
        "properties": {
          "external_id": {
            "type": "string",
            "description": "Vehicle ID in your own system (fleet ERP, payroll…).",
            "example": "VEH-MAD-02"
          },
          "label": {
            "type": "string",
            "description": "Vehicle label shown to the dispatcher.",
            "example": "Refrigerated van, Madrid Downtown 02"
          },
          "phone": {
            "type": "string",
            "description": "Driver phone. In E.164 format.",
            "example": "+34612345678"
          },
          "comments": {
            "type": "string",
            "description": "Internal notes about the vehicle / driver.",
            "example": "Access to central depots only before 09:00."
          },
          "email": {
            "type": "string",
            "description": "Driver email used to dispatch the route link.",
            "example": "carlos.lopez@example.com"
          },
          "color": {
            "type": "string",
            "description": "Vehicle color. In hex format.",
            "example": "#1E88E5"
          },
          "default_time_window": {
            "$ref": "#/components/schemas/Timewindow"
          },
          "default_working_days": {
            "$ref": "#/components/schemas/default_working_days1"
          },
          "default_min_distance": {
            "type": "number",
            "description": "Default min distance the vehicle can travel. In meters.",
            "example": 5000,
            "minimum": 0
          },
          "default_max_distance": {
            "type": "number",
            "description": "Default max distance the vehicle can travel. In meters (e.g. 120 km = 120000).",
            "example": 120000,
            "minimum": 0
          },
          "default_max_time": {
            "type": "number",
            "description": "Default max time the vehicle can spend. In seconds (e.g. 8h = 28800).",
            "example": 28800,
            "minimum": 0
          },
          "default_min_time": {
            "type": "number",
            "description": "Default min time the vehicle can spend. In seconds.",
            "example": 3600,
            "minimum": 0
          },
          "default_start_location": {
            "$ref": "#/components/schemas/Location9"
          },
          "default_end_location": {
            "$ref": "#/components/schemas/Location10"
          },
          "custom_fields": {
            "$ref": "#/components/schemas/custom_fields12"
          },
          "price_per_distance": {
            "type": "number",
            "description": "Cost per meter, used for route costing.",
            "example": 0.00045
          },
          "price_per_minute": {
            "type": "number",
            "description": "Cost per second, used for route costing.",
            "example": 0.0035
          },
          "price_per_stop": {
            "type": "number",
            "description": "Cost per stop, used for route costing.",
            "example": 0.5
          }
        }
      },
      "Model21": {
        "type": "object",
        "properties": {
          "params": {
            "type": "object",
            "properties": {
              "string": {
                "type": "string",
                "x-alternatives": [
                  {
                    "type": "string"
                  },
                  {
                    "type": "number"
                  },
                  {
                    "type": "boolean"
                  }
                ]
              }
            }
          }
        },
        "required": [
          "params"
        ]
      },
      "stops1": {
        "type": "array",
        "description": "Array of stop IDs that the route will visit. Must be previously created.",
        "example": [
          "4f75d991ac359f8c4c79d762"
        ],
        "x-constraint": {
          "single": true
        },
        "items": {
          "type": "string"
        }
      },
      "RouteEditStops": {
        "type": "object",
        "properties": {
          "stops": {
            "$ref": "#/components/schemas/stops1"
          }
        },
        "required": [
          "stops"
        ]
      },
      "Route_Last_Position": {
        "type": "object",
        "properties": {
          "lat": {
            "type": "number",
            "description": "Last position latitude",
            "example": 40.45
          },
          "lng": {
            "type": "number",
            "description": "Last position longitude",
            "example": -3.68
          },
          "created_at": {
            "type": "string",
            "format": "date",
            "description": "Last position tracked date",
            "example": "2026-10-02T10:00:40.856Z"
          },
          "orientation": {
            "type": "number",
            "description": "Last position orientation in degrees",
            "example": 270,
            "minimum": 0,
            "maximum": 360
          }
        }
      },
      "Route_With_Stops": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "Routes ID",
            "example": "4f75d991ac359f8c4c79d762"
          },
          "project_id": {
            "type": "string",
            "description": "Project ID",
            "example": "4f75d991ac359f8c4c79d762"
          },
          "plan_id": {
            "type": "string",
            "description": "Plan ID",
            "example": "4f75d991ac359f8c4c79d762"
          },
          "vehicle_id": {
            "type": "string",
            "description": "Client ID",
            "example": "4f75d991ac359f8c4c79d762"
          },
          "external_id": {
            "type": "string",
            "description": "Route ID in your own system.",
            "example": "ROUTE-2026-05-21-MAD03"
          },
          "start_location": {
            "$ref": "#/components/schemas/Location"
          },
          "end_location": {
            "$ref": "#/components/schemas/Location"
          },
          "label": {
            "type": "string",
            "description": "Routes label",
            "example": "Eixample route, refrigerated van 02"
          },
          "vehicle_label": {
            "type": "string",
            "description": "Vehicle name (search only)",
            "example": "Refrigerated van 02"
          },
          "plan_label": {
            "type": "string",
            "description": "Plan name (search only)",
            "example": "Madrid — 2026-05-21"
          },
          "plan_execution_date": {
            "type": "string",
            "format": "date",
            "description": "Execution date of the plan the route belongs to (search only)",
            "example": "2026-05-21"
          },
          "phone": {
            "type": "string",
            "description": "Driver phone. In E.164 format.",
            "example": "+34612345678"
          },
          "comments": {
            "type": "string",
            "description": "Internal notes about the route, shown to the dispatcher.",
            "example": "Load at 06:00 at dock 4. Depart 06:30."
          },
          "email": {
            "type": "string",
            "description": "Driver email used to dispatch the route link.",
            "example": "carlos.lopez@example.com"
          },
          "plate": {
            "type": "string",
            "description": "Routes vehicle plate",
            "example": "2596 KHO"
          },
          "vehicle_model": {
            "type": "string",
            "description": "Routes vehicle model",
            "example": "Transit Custom L2H2"
          },
          "color": {
            "type": "string",
            "description": "Routes vehicle color",
            "example": "#1E88E5"
          },
          "brand": {
            "type": "string",
            "description": "Routes vehicle brand",
            "example": "Ford"
          },
          "vehicle": {
            "$ref": "#/components/schemas/vehicle"
          },
          "planned_track": {
            "type": "string",
            "description": "Expected route encoded in Polyline format (https://developers.google.com/maps/documentation/utilities/polylineutility)",
            "example": "spxsBsdb|Lymo`qvAx@TKvAr@K"
          },
          "is_locked": {
            "type": "boolean",
            "description": "Routes is locked"
          },
          "geo_fences": {
            "$ref": "#/components/schemas/geo_fences"
          },
          "time_window": {
            "$ref": "#/components/schemas/Timewindow"
          },
          "max_distance": {
            "type": "number",
            "description": "Max distance the route can travel. In meters (e.g. 120 km = 120000).",
            "example": 120000
          },
          "min_distance": {
            "type": "number",
            "description": "Min distance the route can travel. In meters.",
            "example": 5000
          },
          "max_time": {
            "type": "number",
            "description": "Max time the route can spend. In seconds (e.g. 8h = 28800).",
            "example": 28800
          },
          "min_time": {
            "type": "number",
            "description": "Min time the route can spend. In seconds.",
            "example": 3600
          },
          "max_weight": {
            "type": "number",
            "description": "Max weight the route can carry (kg).",
            "example": 800
          },
          "max_volume": {
            "type": "number",
            "description": "Max volume the route can carry (m³ or parcel count).",
            "example": 8
          },
          "max_services": {
            "type": "number",
            "description": "Max stops the route can perform.",
            "example": 40
          },
          "provides": {
            "$ref": "#/components/schemas/Provides"
          },
          "order": {
            "type": "number",
            "description": "Routes order in list",
            "example": 0
          },
          "start_date": {
            "type": "string",
            "format": "date",
            "description": "Routes start date",
            "example": "2026-10-02T10:00:40.857Z"
          },
          "end_date": {
            "type": "string",
            "format": "date",
            "description": "Routes end date",
            "example": "2026-10-02T10:00:40.857Z"
          },
          "estimated_end_time": {
            "type": "string",
            "format": "date",
            "description": "Routes estimated end time",
            "example": "2026-10-02T10:00:40.857Z"
          },
          "last_position": {
            "$ref": "#/components/schemas/Route_Last_Position"
          },
          "planned_start_time": {
            "type": "number",
            "description": "Planned route start, second-of-day (e.g. 06:30 = 23400).",
            "example": 23400
          },
          "planned_end_time": {
            "type": "number",
            "description": "Planned route end, second-of-day (e.g. 14:00 = 50400).",
            "example": 50400
          },
          "custom_fields": {
            "$ref": "#/components/schemas/custom_fields"
          },
          "distance_traveled": {
            "type": "number"
          },
          "tracing_percent": {
            "type": "number"
          },
          "price_per_minute": {
            "type": "number",
            "description": "Cost per second of route time.",
            "example": 0.0035
          },
          "price_per_distance": {
            "type": "number",
            "description": "Cost per meter of route distance.",
            "example": 0.00045
          },
          "price_per_stop": {
            "type": "number",
            "description": "Cost per stop of the route.",
            "example": 0.5
          },
          "toll_price": {
            "type": "number",
            "description": "Price of the tolls in the route",
            "example": 4.8
          },
          "toll_price_currency": {
            "type": "string",
            "description": "Currency for the tolls",
            "example": "EUR"
          },
          "status": {
            "$ref": "#/components/schemas/RouteStatus"
          },
          "created_by": {
            "type": "string",
            "description": "Collection user creator",
            "example": "4f75d991ac359f8c4c79d762"
          },
          "created_at": {
            "type": "string",
            "format": "date",
            "description": "Collection creation date",
            "example": "2026-10-02T10:00:40.694Z"
          },
          "updated_at": {
            "type": "string",
            "format": "date",
            "description": "Collection last update date",
            "example": "2026-10-02T10:00:40.694Z"
          },
          "deleted_at": {
            "type": "string",
            "format": "date",
            "description": "Collection deletion date",
            "example": "2026-10-02T10:00:40.694Z"
          },
          "deleted": {
            "type": "boolean",
            "description": "True if route is deleted"
          },
          "stops": {
            "$ref": "#/components/schemas/StopsData"
          },
          "break_stop": {
            "$ref": "#/components/schemas/break_stop"
          }
        },
        "required": [
          "project_id",
          "plan_id"
        ]
      },
      "Array_of_id": {
        "type": "object",
        "properties": {
          "stop_ids": {
            "$ref": "#/components/schemas/stop_ids"
          }
        }
      },
      "Route_Dispatch": {
        "type": "object",
        "properties": {
          "email": {
            "type": "string",
            "description": "Project user email",
            "example": "john.doe@test.com",
            "x-format": {
              "email": true
            },
            "x-convert": {
              "case": "lower"
            }
          },
          "public_link": {
            "type": "string",
            "description": "Public link of driver platform",
            "example": "https://google.com"
          }
        },
        "required": [
          "email"
        ]
      },
      "Report_Image": {
        "type": "object",
        "properties": {
          "data": {
            "type": "string",
            "description": "The image as a base64 data-URL, e.g. `data:image/jpeg;base64,/9j/4AAQ...`",
            "maxLength": 2796203
          },
          "name": {
            "type": "string",
            "description": "File name",
            "example": "proof-of-delivery.jpg"
          },
          "mime": {
            "type": "string",
            "description": "Image MIME type. Must be an `image/*` type",
            "example": "image/jpeg",
            "pattern": "^image\\/"
          }
        },
        "required": [
          "data",
          "name",
          "mime"
        ]
      },
      "Array_of_id1": {
        "type": "array",
        "x-constraint": {
          "single": true
        },
        "items": {
          "type": "string",
          "description": "Stop id",
          "example": "4f75d991ac359f8c4c79d762",
          "pattern": "^[0-9a-f]{24}"
        }
      },
      "ApiError": {
        "type": "object",
        "properties": {
          "message": {
            "type": "string",
            "description": "Error message",
            "example": "Domain not found"
          },
          "message_id": {
            "type": "string",
            "description": "Error id",
            "example": "highway.domain.error.not_found"
          }
        },
        "required": [
          "message",
          "message_id"
        ]
      },
      "PlanStatus": {
        "type": "string",
        "description": "Plan status",
        "example": "planning",
        "enum": [
          "planning",
          "in_progress",
          "finished",
          "draft"
        ]
      },
      "StopStatus": {
        "type": "string",
        "description": "Stop Status",
        "example": "pending",
        "enum": [
          "pending",
          "incomplete",
          "completed",
          "canceled"
        ]
      },
      "RouteStatus": {
        "type": "string",
        "description": "Route Status",
        "example": "not_started",
        "enum": [
          "not_started",
          "in_transit",
          "finished"
        ]
      },
      "PackageStatus": {
        "type": "string",
        "description": "Package status",
        "example": "pending",
        "enum": [
          "pending",
          "picked",
          "delivered"
        ]
      }
    }
  }
}