{
  "openapi": "3.0.0",
  "info": {
    "title": "Routal API",
    "version": "7.12.55",
    "description": "# Routal API — Route optimization & last-mile delivery platform\n\nRoutal helps logistics and field-service teams **plan**, **dispatch** and **track** their daily operations.\nThis API is the same one that powers the [Routal Planner](https://planner.routal.com) and our driver and customer apps,\nso anything you can do in the product, you can automate from here.\n\n## Typical 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 end customers with live tracking.\n- **Field service / installation / maintenance** — schedule technician visits with time windows,\n  required skills and service durations.\n- **Distribution & B2B routes** — plan multi-stop routes with vehicle capacity, working hours,\n  break times and depot constraints.\n- **System integration** — keep your TMS, ERP or marketplace in sync with Routal through\n  CRUD operations on plans, routes, 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**    | A daily (or per-shift) container with the routes to execute on a given date.       |\n| **Route**   | An ordered sequence of stops assigned to a driver / vehicle.                       |\n| **Stop**    | A delivery, pickup or visit — the unit of work performed by a driver.              |\n| **Task**    | A sub-operation inside a stop (e.g. deliver parcel A, collect signature, scan SKU).|\n| **Vehicle** | A driver + vehicle profile with capacity, skills, working hours and start point.   |\n\n## Authentication\n\nThe Routal API uses **API keys** sent as a `private_key` query parameter on every request.\nGenerate and rotate keys from the [Developer Settings](https://planner.routal.com/h/settings/developers/api-keys) section of your workspace.\n\nKeep your API keys **server-side**. Do not embed them in mobile, web or single-page apps.\n\n## Versioning\n\nThe current production version is `v2` (endpoints prefixed with `/v2/...`).\nBreaking changes are introduced on a new major version; non-breaking additions can land on the existing version.\n\n## Rate limiting\n\nRequests are rate-limited per authenticated credential (API key or user session) — currently\n**2,000 requests per minute** per credential. If you hit the limit you'll receive a\n`429 Too Many Requests` response — back off and retry with exponential backoff.\n\n## Support\n\nNeed help integrating? Reach us at **support@routal.com** or through 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": "Daily / per-shift containers. A plan groups the routes, stops and vehicles for an execution date. Start integrations here: create a plan, push stops, optimize, retrieve routes."
    },
    {
      "name": "Route",
      "description": "Ordered sequences of stops assigned to a driver and vehicle. Use these endpoints to inspect, modify or dispatch a single route once the plan has been optimized."
    },
    {
      "name": "Stop",
      "description": "The unit of work: a delivery, pickup or visit. Stops can be created in bulk, geocoded automatically and moved between plans / routes before execution starts."
    },
    {
      "name": "Task",
      "description": "Sub-operations performed at a stop (deliver, pick up, scan, sign, photo). Use tasks when a single visit involves multiple discrete actions or SKUs."
    },
    {
      "name": "Vehicle",
      "description": "Driver + vehicle profiles. Define capacity, skills, working hours and start / end locations so the optimizer can build feasible routes."
    }
  ],
  "security": [
    {
      "api_key": []
    }
  ],
  "paths": {
    "/v2/plans": {
      "get": {
        "summary": "List plans",
        "operationId": "getV2Plans",
        "description": "Returns a paginated list of plans for a project, optionally filtered by date range, status or label.\n\nTypical uses:\n- Build a daily / weekly calendar view of operations.\n- Reconcile execution data with your TMS or BI tool.\n- Power a dashboard that shows \"today's plans\" or \"plans in progress\".",
        "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 malformed payload",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized — missing or invalid API key / token"
          },
          "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 — please contact support@routal.com if this 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 available in a project. Use the `sort` query parameter with the format `<field>:<asc|desc>` (e.g. `status:asc`, `label:desc`) to order the result.\n\nUse this to power dropdowns, dispatch boards or fleet-utilization dashboards.",
        "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 malformed payload",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized — missing or invalid API key / token"
          },
          "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 — please contact support@routal.com if this 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/plan/{id}": {
      "get": {
        "summary": "Retrieve a plan",
        "operationId": "getV2PlanId",
        "description": "Returns the full plan, including its routes, stops, vehicles and metadata.\n\nUse this endpoint to **hydrate your UI** after an optimization, to **audit** what was executed, or to **sync the plan state** back into your TMS / ERP. For lightweight polling, prefer `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 malformed payload",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized — missing or invalid API key / token"
          },
          "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 — please contact support@routal.com if this 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())"
          }
        ]
      },
      "put": {
        "summary": "Update a plan",
        "operationId": "putV2PlanId",
        "description": "Updates plan-level attributes such as label, execution date, status or optimizer configuration. Stops and vehicles are **not** modified by this endpoint — use the dedicated `/v2/stops` and `/v2/vehicles` endpoints to manage them.\n\n> Plan-level GeoFences are deprecated and inherited from the project. They 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 malformed payload",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized — missing or invalid API key / token"
          },
          "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 — please contact support@routal.com if this 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())"
          }
        ]
      },
      "delete": {
        "summary": "Delete a plan",
        "operationId": "deleteV2PlanId",
        "description": "Deletes the plan together with **all its routes and stops**. This is destructive and cannot be undone.\n\nFor audit-friendly integrations we recommend keeping completed plans and instead archive them on your side — historical plans are the source of truth for proof of delivery, KPIs and analytics in Routal.",
        "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 malformed payload",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized — missing or invalid API key / token"
          },
          "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 — please contact support@routal.com if this 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())"
          }
        ]
      }
    },
    "/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\nUse this endpoint to **inspect a single route** without pulling the full plan — for example to render a driver dashboard, build a public route page, or sync an individual route into your TMS.",
        "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 malformed payload",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized — missing or invalid API key / token"
          },
          "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 — please contact support@routal.com if this 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())"
          }
        ]
      },
      "put": {
        "summary": "Update a route",
        "operationId": "putV2RouteId",
        "description": "Updates route-level attributes such as status, assigned vehicle / driver, label and constraints. The stop sequence is **not** modified here — use `PUT /v2/route/{id}/stops` to reorder stops.\n\nChanging the assigned vehicle once the route is in progress will rebroadcast it to the new driver if the route had been previously dispatched.",
        "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 malformed payload",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized — missing or invalid API key / token"
          },
          "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 — please contact support@routal.com if this 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())"
          }
        ]
      },
      "delete": {
        "summary": "Delete a route",
        "operationId": "deleteV2RouteId",
        "description": "Deletes the route from its plan. **Stops are not deleted** — they are released back to the plan as unassigned, ready to be moved to another route or re-optimized.\n\nUse this to remove an empty driver shift from a plan (e.g. when a driver calls in sick) without losing the orders that were assigned to them.",
        "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 malformed payload",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized — missing or invalid API key / token"
          },
          "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 — please contact support@routal.com if this 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())"
          }
        ]
      }
    },
    "/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.\n\nUse this to **drill into a single delivery** from your back-office, build a customer-facing tracking page, or correlate a webhook event with the underlying stop data.",
        "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 malformed payload",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized — missing or invalid API key / token"
          },
          "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 — please contact support@routal.com if this 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. Use it to fix customer data, adjust time windows, update the service duration, change required skills or replace the tasks attached to the stop.\n\n**Warning** — updating a stop that has already been executed (delivered, failed, cancelled) changes its historical record. Only do this for corrections; for retries, create a new stop instead.",
        "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 malformed payload",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized — missing or invalid API key / token"
          },
          "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 — please contact support@routal.com if this 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 malformed payload",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized — missing or invalid API key / token"
          },
          "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 — please contact support@routal.com if this 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())"
          }
        ]
      },
      "put": {
        "summary": "Update a vehicle",
        "operationId": "putV2VehicleVehicle_id",
        "description": "Updates the vehicle profile — capacity, skills, working hours, breaks, start / end locations, assigned driver and traffic options.\n\nChanges apply to **future optimizations**. Routes that have already been optimized or dispatched keep their current shape until you re-run the optimizer for the affected 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 malformed payload",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized — missing or invalid API key / token"
          },
          "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 — please contact support@routal.com if this 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 that belongs to the plan along with its execution payload — each stop includes its tasks, custom fields, timestamps and any `reports` captured at the visit (signature, photos, comments and per-task results such as scanned barcodes).\n\nThis is the right endpoint to **export delivery data** back to your TMS, generate invoices, or feed your customer notifications. The response can be large — paginate on your side if you need to display it.",
        "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 malformed payload",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized — missing or invalid API key / token"
          },
          "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 — please contact support@routal.com if this 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, each containing its ordered list of stops, the assigned vehicle, total distance, total duration and estimated arrival times.\n\nUse this endpoint after `POST /v2/plan/{id}/optimize` to read the optimized assignment, or any time during execution to build a live dispatch / monitoring 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 malformed payload",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized — missing or invalid API key / token"
          },
          "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 — please contact support@routal.com if this 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/plan": {
      "post": {
        "summary": "Create a plan",
        "operationId": "postV2Plan",
        "description": "Creates a new plan inside the given project. A plan is the container for the routes and stops that will be executed on a specific date.\n\n**Recommended integration flow**\n1. `POST /v2/plan?project_id=...` — create the plan for the execution date.\n2. `POST /v2/stops?plan_id=...` (or `POST /v2/stops/geocode`) — push the day's orders.\n3. `POST /v2/plan/{id}/optimize` — let the Routal optimizer build the routes.\n4. `POST /v2/route/{id}/dispatch` — send each route to its driver.\n\nPayload size is limited to **3 MB**. If you need to send more stops at once, split them into multiple `POST /v2/stops` calls after the plan is created.\n\n> Plan-level GeoFences are deprecated — the plan inherits the GeoFences defined at the project level and they cannot be overridden per plan.",
        "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 malformed payload",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized — missing or invalid API key / token"
          },
          "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 — please contact support@routal.com if this 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 multiple routes in a plan in a single request. Each route can include its assigned vehicle, constraints and (optionally) the stop sequence.\n\nUse this when you have **pre-computed assignments** elsewhere (your own optimizer, a manual dispatch, or a recurring template) and just want to materialize the routes in Routal — skipping the `POST /v2/plan/{id}/optimize` step.",
        "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 malformed payload",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized — missing or invalid API key / token"
          },
          "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 — please contact support@routal.com if this 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 **already known coordinates**. No geocoding is performed — pass the `location` (`lat` / `lng`) directly on each stop. Stops without a `location` are accepted but the optimizer cannot place them until coordinates are added. Use this when your source system already stores validated coordinates (warehouse pick lists, sorted parcels, IoT devices).\n\nPass either:\n- `plan_id` — the stops are added to that plan, ready to be optimized; or\n- `project_id` — the stops are created as **unassigned** at the project level, useful for an inbox of pending orders that you'll assign to a plan later.\n\nPayload size is limited to **5 MB** (~5,000 stops as a rough rule of thumb). For larger imports chunk your calls. If your data has free-text addresses instead of coordinates, use `POST /v2/stops/geocode` instead.",
        "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 malformed payload",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized — missing or invalid API key / token"
          },
          "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 — please contact support@routal.com if this 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 new task to an existing stop. A task is a **labeled checklist item** the driver ticks during the visit: it carries a `label`, optional `comments`, optional `barcode` (to be scanned), `custom_fields` and a `status` (`pending` / `completed` / `canceled`).\n\nUse tasks when a single stop contains multiple SKUs or sub-operations the driver has to tick / scan one by one. For richer proof-of-delivery payloads (signatures, photos, free-form comments) the driver app produces a `report` on the stop — that's separate from tasks.",
        "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 malformed payload",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized — missing or invalid API key / token"
          },
          "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 — please contact support@routal.com if this 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 vehicle carries its full configuration (capacity, provides / skills, working hours, breaks, start / end locations, driver, transport mode, traffic options).\n\n`v3` is the **recommended** endpoint for new integrations as it accepts the latest schema. 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 malformed payload",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized — missing or invalid API key / token"
          },
          "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 — please contact support@routal.com if this 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/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** the stops at the project level (they go to your unassigned inbox).\n\nTypical use cases:\n- Reschedule orders from today's plan to tomorrow's.\n- Pull a stop out of a plan when the customer requests a different day.\n- Park stops in the unassigned inbox while you decide how to handle them.\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 audit trails.\n\nRecommended pattern:\n1. Do **not** move stops in terminal states.\n2. **Create a new stop** in the new plan instead — that's the right way to model a retry.\n3. Only move stops that have not yet started execution.\n\nIf you are unsure, [contact support](mailto:support@routal.com) — we'll help you model it correctly.",
        "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 malformed payload",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized — missing or invalid API key / token"
          },
          "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 — please contact support@routal.com if this 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 using its geocoding stack and stores the result on the stop. Pass an `address` per stop; do **not** pass `location` (it's rejected — use `POST /v2/stops` if you already have coordinates).\n\nThis is the right endpoint when integrating with an e-commerce, ERP, marketplace or spreadsheet where customer addresses are not normalized. Stops whose addresses cannot be geocoded are still created, but without coordinates — they appear unassigned and cannot be optimized until a planner fixes the address in the Routal Planner.\n\nPass either `plan_id` (add to a plan) or `project_id` (create unassigned). Payload size is limited to **5 MB**; geocoding is more expensive than coordinate-based creation, so prefer `POST /v2/stops` when coordinates are already known on your side.",
        "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 malformed payload",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized — missing or invalid API key / token"
          },
          "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 — please contact support@routal.com if this 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 with rich filters",
        "operationId": "postV2StopsSearch",
        "description": "Searches stops across plans within a project, with **full-text, status, date-range, label, customer, supervisor and custom-field filters**. Results are paginated and sorted by relevance or by any indexed field.\n\nUse this endpoint to power dashboards, reports and integrations that need cross-plan visibility — for example, \"all stops for customer X in the last 30 days\", \"all failed deliveries this week\", or \"all stops with SKU `ABC-123` pending today\".",
        "parameters": [
          {
            "name": "project_id",
            "in": "query",
            "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 malformed payload",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized — missing or invalid API key / token"
          },
          "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 — please contact support@routal.com if this persists"
          }
        },
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "cURL",
            "source": "curl -X POST 'https://api.routal.com/v2/stops/search?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/search?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/search',\n    params={ 'private_key': 'YOUR_KEY' },\n    headers={'Content-Type': 'application/json'},\n    json={},\n)\nprint(res.json())"
          }
        ]
      }
    },
    "/v2/plan/{id}/optimize": {
      "post": {
        "summary": "Optimize a plan",
        "operationId": "postV2PlanIdOptimize",
        "description": "Triggers the Routal optimizer for this plan. Stops are assigned to vehicles, ordered, and the ETA, distance and load of every route are recomputed.\n\nBy default, **all routes are rebuilt** — stops may move between vehicles. Routes flagged as `is_locked` are always preserved and excluded from re-optimization.\n\nPass `keep_current_assignment=true` to keep each stop on its currently assigned route and only re-sequence stops within that route (useful when a driver has already started or when you want to lock the assignment).\n\nMost calls are **synchronous** — the response contains the optimized plan. Very large plans fall back to an asynchronous run; progress can be polled at `GET /v2/plan/{id}/running_optimization`, or subscribe to webhooks for push notifications.\n\n**Constraints respected by the optimizer:** time windows, service duration per stop, vehicle capacities (weight / volume / units), required vs. provided skills (stop `requires` matched against vehicle `provides`), route time windows, break times, route start / end locations and geo-fenced zones.",
        "parameters": [
          {
            "description": "Plan ID",
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "pattern": "^[0-9a-f]{24}"
            }
          },
          {
            "description": "When true, stops stay on their currently assigned route and only the in-route sequence is re-optimized. Use this once dispatch has started.",
            "name": "keep_current_assignment",
            "in": "query",
            "schema": {
              "type": "boolean",
              "default": false
            }
          }
        ],
        "tags": [
          "Plan"
        ],
        "responses": {
          "200": {
            "description": "The plan with its re-optimized routes when the run is synchronous, or the original plan if the run was kicked off asynchronously (poll `GET /v2/plan/{id}/running_optimization`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FullPlanData"
                }
              }
            }
          },
          "400": {
            "description": "Bad request — validation failed or malformed payload",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized — missing or invalid API key / token"
          },
          "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 — please contact support@routal.com if this persists"
          }
        },
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "cURL",
            "source": "curl -X POST 'https://api.routal.com/v2/plan/{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/plan/{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/plan/{id}/optimize',\n    params={ 'private_key': 'YOUR_KEY' },\n    headers={'Content-Type': 'application/json'},\n)\nprint(res.json())"
          }
        ]
      }
    },
    "/v2/route/{id}/optimize": {
      "post": {
        "summary": "Optimize the stop sequence of a single route",
        "operationId": "postV2RouteIdOptimize",
        "description": "Runs the Routal optimizer over the stops **already assigned to this route**, keeping the vehicle assignment fixed. Stops are re-sequenced and any stops that no longer fit the route's constraints (time, capacity…) are returned as **unassigned** — they will not be moved to a different vehicle.\n\nPrefer this endpoint over `POST /v2/plan/{id}/optimize` when you want a quick re-optimization for a single driver (e.g. after manually adding or removing a stop) 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 malformed payload",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized — missing or invalid API key / token"
          },
          "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 — please contact support@routal.com if this 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": "Sends an email to the route's assigned driver containing a **public driver link** that opens the route in the Routal Drivers webview or mobile app.\n\nCalling this endpoint again **re-sends the email** with a freshly built link — handy when the driver lost the email or changed device. The route status is not changed by this call; the driver starts the route from the app itself.",
        "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 malformed payload",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized — missing or invalid API key / token"
          },
          "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 — please contact support@routal.com if this 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/stops/delete": {
      "put": {
        "summary": "Delete multiple stops",
        "operationId": "putV2StopsDelete",
        "description": "Deletes the given stops in a single request. Stops in a terminal state (delivered, failed, cancelled) should not be deleted — keep them for traceability and analytics.\n\nUse this for **bulk cleanup before optimization** (e.g. orders cancelled on your e-commerce after import). To remove a single executed delivery for GDPR / privacy reasons, contact support so we can do it in a way that preserves audit logs.",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/Model5"
              }
            }
          }
        },
        "tags": [
          "Stop"
        ],
        "responses": {
          "400": {
            "description": "Bad request — validation failed or malformed payload",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized — missing or invalid API key / token"
          },
          "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 — please contact support@routal.com if this 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/task/{task_id}": {
      "put": {
        "summary": "Update a task",
        "operationId": "putV2TaskTask_id",
        "description": "Updates a task on a stop — its `label`, `comments`, `barcode`, `custom_fields` or `status`. Useful for correcting task data before execution, or for resetting a task back to `pending` if the driver checked it off 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 malformed payload",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized — missing or invalid API key / token"
          },
          "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 — please contact support@routal.com if this 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())"
          }
        ]
      },
      "delete": {
        "summary": "Delete a task",
        "operationId": "deleteV2TaskTask_id",
        "description": "Removes a task from a stop. The stop itself is not deleted. Avoid deleting tasks that have already been completed (status `completed` with a scanned barcode or comments) — that data is part of the proof of delivery and is required 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 malformed payload",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized — missing or invalid API key / token"
          },
          "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 — please contact support@routal.com if this 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())"
          }
        ]
      }
    },
    "/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. Use this when you want to manually override the optimizer's sequence (e.g. a dispatcher dragging and dropping stops in your own UI, or a custom heuristic implemented on your side).\n\nETAs, distance and duration are recomputed automatically. Stops not included in the payload are unassigned from the route. Stops that don't currently 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 malformed payload",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized — missing or invalid API key / token"
          },
          "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 — please contact support@routal.com if this 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": "The Routal API authenticates requests with an <b>API key</b> passed as the <code>private_key</code> query string parameter. Generate and rotate your keys from the <a href=\"https://planner.routal.com/h/settings/developers/api-keys\" target=\"_blank\">Developer Settings</a> section of your Routal workspace.<br/><br/><b>Never</b> ship API keys to a browser or mobile app — they must stay server-side.",
        "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-05-29T14:55:24.518Z"
          },
          "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-05-29T14:55:24.518Z"
          },
          "updated_at": {
            "type": "string",
            "format": "date",
            "description": "Collection last update date",
            "example": "2026-05-29T14:55:24.518Z"
          },
          "deleted_at": {
            "type": "string",
            "format": "date",
            "description": "Collection deletion date",
            "example": "2026-05-29T14:55:24.518Z"
          },
          "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
          },
          "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-05-29T14:55:24.339Z"
          },
          "updated_at": {
            "type": "string",
            "format": "date",
            "description": "Collection last update date",
            "example": "2026-05-29T14:55:24.339Z"
          },
          "deleted_at": {
            "type": "string",
            "format": "date",
            "description": "Collection deletion date",
            "example": "2026-05-29T14:55:24.339Z"
          },
          "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"
        }
      },
      "status1": {
        "type": "string",
        "description": "Stop Status",
        "example": "pending",
        "enum": [
          "pending",
          "incomplete",
          "completed",
          "canceled"
        ]
      },
      "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-05-29T14:55:24.485Z"
          },
          "updated_at": {
            "type": "string",
            "format": "date",
            "description": "Collection last update date",
            "example": "2026-05-29T14:55:24.485Z"
          },
          "deleted_at": {
            "type": "string",
            "format": "date",
            "description": "Collection deletion date",
            "example": "2026-05-29T14:55:24.485Z"
          }
        },
        "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"
          },
          "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-05-29T14:55:24.486Z"
          }
        },
        "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-05-29T14:55:24.489Z"
          },
          "updated_at": {
            "type": "string",
            "format": "date",
            "description": "Collection last update date",
            "example": "2026-05-29T14:55:24.489Z"
          }
        },
        "required": [
          "rating"
        ]
      },
      "supervisors": {
        "type": "array",
        "description": "Stop supervisors",
        "example": [
          "4f75d991ac359f8c4c79d762"
        ],
        "items": {
          "type": "string"
        }
      },
      "status4": {
        "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"
          },
          "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
          },
          "max_delivery_time": {
            "type": "number",
            "description": "Max time in seconds between the pickup and this delivery (chain).",
            "example": 7200
          },
          "status": {
            "$ref": "#/components/schemas/status4"
          },
          "plan_execution_date": {
            "type": "string",
            "format": "date",
            "description": "Plan execution date.",
            "example": "2026-05-29T14:55:24.491Z"
          },
          "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-05-29T14:55:24.491Z"
          },
          "estimated_departure_time": {
            "type": "string",
            "format": "date",
            "description": "Stop estimated departure time",
            "example": "2026-05-29T14:55:24.491Z"
          },
          "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-05-29T14:55:24.499Z"
          },
          "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-05-29T14:55:24.500Z"
          },
          "created_by": {
            "type": "string",
            "description": "Collection user creator",
            "example": "4f75d991ac359f8c4c79d762"
          },
          "created_at": {
            "type": "string",
            "format": "date",
            "description": "Collection creation date",
            "example": "2026-05-29T14:55:24.339Z"
          },
          "updated_at": {
            "type": "string",
            "format": "date",
            "description": "Collection last update date",
            "example": "2026-05-29T14:55:24.339Z"
          },
          "deleted_at": {
            "type": "string",
            "format": "date",
            "description": "Collection deletion date",
            "example": "2026-05-29T14:55:24.339Z"
          },
          "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"
          },
          "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
          },
          "max_delivery_time": {
            "type": "number",
            "description": "Max time in seconds between the pickup and this delivery (chain).",
            "example": 7200
          },
          "status": {
            "$ref": "#/components/schemas/status1"
          },
          "plan_execution_date": {
            "type": "string",
            "format": "date",
            "description": "Plan execution date.",
            "example": "2026-05-29T14:55:24.491Z"
          },
          "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-05-29T14:55:24.491Z"
          },
          "estimated_departure_time": {
            "type": "string",
            "format": "date",
            "description": "Stop estimated departure time",
            "example": "2026-05-29T14:55:24.491Z"
          },
          "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-05-29T14:55:24.499Z"
          },
          "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-05-29T14:55:24.500Z"
          },
          "created_by": {
            "type": "string",
            "description": "Collection user creator",
            "example": "4f75d991ac359f8c4c79d762"
          },
          "created_at": {
            "type": "string",
            "format": "date",
            "description": "Collection creation date",
            "example": "2026-05-29T14:55:24.339Z"
          },
          "updated_at": {
            "type": "string",
            "format": "date",
            "description": "Collection last update date",
            "example": "2026-05-29T14:55:24.339Z"
          },
          "deleted_at": {
            "type": "string",
            "format": "date",
            "description": "Collection deletion date",
            "example": "2026-05-29T14:55:24.339Z"
          },
          "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 / equipment the vehicle provides — matched against stop `requires`.",
        "example": [
          "refrigerated",
          "lift_gate"
        ],
        "items": {
          "type": "string"
        }
      },
      "status5": {
        "type": "string",
        "description": "Route Status",
        "example": "not_started",
        "enum": [
          "not_started",
          "in_transit",
          "finished"
        ]
      },
      "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-05-29T14:55:24.518Z"
          },
          "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-05-29T14:55:24.518Z"
          },
          "updated_at": {
            "type": "string",
            "format": "date",
            "description": "Collection last update date",
            "example": "2026-05-29T14:55:24.518Z"
          },
          "deleted_at": {
            "type": "string",
            "format": "date",
            "description": "Collection deletion date",
            "example": "2026-05-29T14:55:24.518Z"
          },
          "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"
          }
        }
      },
      "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
        }
      },
      "status7": {
        "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/status7"
          },
          "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"
          }
        }
      },
      "status8": {
        "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
        }
      },
      "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 — Acme Warehouse Coslada"
          },
          "location": {
            "$ref": "#/components/schemas/Location2"
          },
          "location_details": {
            "type": "string",
            "description": "Address line 2 — useful for 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/status8"
          },
          "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"
          },
          "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
          }
        }
      },
      "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
        }
      },
      "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/StopStatus"
          },
          "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"
          },
          "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
          }
        }
      },
      "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"
        }
      },
      "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
          },
          "status": {
            "$ref": "#/components/schemas/RouteStatus"
          }
        }
      },
      "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 — 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"
          }
        }
      },
      "status10": {
        "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/status10"
          },
          "custom_fields": {
            "$ref": "#/components/schemas/custom_fields5"
          }
        }
      },
      "Model3": {
        "type": "string",
        "enum": [
          "Monday",
          "Tuesday",
          "Wednesday",
          "Thursday",
          "Friday",
          "Saturday",
          "Sunday"
        ]
      },
      "working_days": {
        "type": "array",
        "example": [
          "Monday",
          "Tuesday",
          "Wednesday",
          "Thursday",
          "Friday"
        ],
        "items": {
          "$ref": "#/components/schemas/Model3"
        }
      },
      "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
          }
        }
      },
      "CreateVehiclesValidator": {
        "type": "array",
        "x-constraint": {
          "single": true
        },
        "items": {
          "$ref": "#/components/schemas/Vehicle"
        }
      },
      "stop_ids": {
        "type": "array",
        "x-constraint": {
          "single": true
        },
        "items": {
          "type": "string",
          "description": "List of stops",
          "example": "4f75d991ac359f8c4c79d762",
          "pattern": "^[0-9a-f]{24}"
        }
      },
      "status11": {
        "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"
        }
      },
      "status12": {
        "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"
        }
      },
      "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 — Acme Warehouse Coslada"
          },
          "location_details": {
            "type": "string",
            "description": "Address line 2 — useful for 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/status12"
          },
          "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"
          },
          "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
          },
          "address": {
            "type": "string",
            "description": "Free-text address to geocode. Include city and country whenever you can — 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/status11"
          },
          "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"
          },
          "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
          },
          "address": {
            "type": "string",
            "description": "Free-text address to geocode. Include city and country whenever you can — 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"
        ]
      },
      "Model4": {
        "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/Model4"
        }
      },
      "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"
          }
        }
      },
      "status13": {
        "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"
          },
          "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-05-29T14:55:24.509Z"
          },
          "end_date": {
            "type": "string",
            "format": "date",
            "description": "Routes end date",
            "example": "2026-05-29T14:55:24.509Z"
          },
          "estimated_end_time": {
            "type": "string",
            "format": "date",
            "description": "Routes estimated end time",
            "example": "2026-05-29T14:55:24.509Z"
          },
          "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
          },
          "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/status13"
          },
          "created_by": {
            "type": "string",
            "description": "Collection user creator",
            "example": "4f75d991ac359f8c4c79d762"
          },
          "created_at": {
            "type": "string",
            "format": "date",
            "description": "Collection creation date",
            "example": "2026-05-29T14:55:24.339Z"
          },
          "updated_at": {
            "type": "string",
            "format": "date",
            "description": "Collection last update date",
            "example": "2026-05-29T14:55:24.339Z"
          },
          "deleted_at": {
            "type": "string",
            "format": "date",
            "description": "Collection deletion date",
            "example": "2026-05-29T14:55:24.339Z"
          },
          "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"
          }
        }
      },
      "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_fields10": {
        "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_fields10"
          },
          "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
          },
          "break": {
            "$ref": "#/components/schemas/break2"
          },
          "break_stop": {
            "$ref": "#/components/schemas/break_stop1"
          }
        }
      },
      "status14": {
        "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"
        }
      },
      "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/status14"
          },
          "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"
          },
          "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
          }
        }
      },
      "Model5": {
        "type": "object",
        "properties": {
          "stop_ids": {
            "$ref": "#/components/schemas/Array_of_id1"
          }
        }
      },
      "status15": {
        "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/status15"
          },
          "custom_fields": {
            "$ref": "#/components/schemas/custom_fields5"
          }
        }
      },
      "Model6": {
        "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/Model6"
        }
      },
      "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_fields11": {
        "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_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
          }
        }
      },
      "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-05-29T14:55:24.508Z"
          },
          "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"
          },
          "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-05-29T14:55:24.509Z"
          },
          "end_date": {
            "type": "string",
            "format": "date",
            "description": "Routes end date",
            "example": "2026-05-29T14:55:24.509Z"
          },
          "estimated_end_time": {
            "type": "string",
            "format": "date",
            "description": "Routes estimated end time",
            "example": "2026-05-29T14:55:24.509Z"
          },
          "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
          },
          "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/status5"
          },
          "created_by": {
            "type": "string",
            "description": "Collection user creator",
            "example": "4f75d991ac359f8c4c79d762"
          },
          "created_at": {
            "type": "string",
            "format": "date",
            "description": "Collection creation date",
            "example": "2026-05-29T14:55:24.339Z"
          },
          "updated_at": {
            "type": "string",
            "format": "date",
            "description": "Collection last update date",
            "example": "2026-05-29T14:55:24.339Z"
          },
          "deleted_at": {
            "type": "string",
            "format": "date",
            "description": "Collection deletion date",
            "example": "2026-05-29T14:55:24.339Z"
          },
          "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"
        ]
      },
      "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": "Driver Status",
        "example": "not_started",
        "enum": [
          "not_started",
          "in_transit",
          "finished"
        ]
      }
    }
  }
}