{
  "swagger": "2.0",
  "info": {
    "title": "DeCA Profesional",
    "version": "1",
    "description": "Conector personalizado de la API de DeCA Profesional (Business + complemento API)."
  },
  "host": "decaprofesional.es",
  "basePath": "/",
  "schemes": [
    "https"
  ],
  "securityDefinitions": {
    "apiKey": {
      "type": "apiKey",
      "in": "header",
      "name": "Authorization",
      "description": "Escribe: Bearer <tu clave>"
    }
  },
  "security": [
    {
      "apiKey": []
    }
  ],
  "definitions": {
    "Party": {
      "type": "object",
      "additionalProperties": false,
      "required": [
        "name",
        "taxId",
        "address"
      ],
      "properties": {
        "name": {
          "type": "string",
          "maxLength": 200
        },
        "taxId": {
          "type": "string",
          "maxLength": 40,
          "description": "NIF / VAT"
        },
        "country": {
          "type": "string",
          "pattern": "^[A-Za-z]{2}$",
          "description": "País (ISO alfa-2). Por defecto, España."
        },
        "address": {
          "type": "string",
          "maxLength": 300
        },
        "postalCode": {
          "type": "string",
          "maxLength": 12
        },
        "city": {
          "type": "string",
          "maxLength": 120
        }
      }
    },
    "Location": {
      "type": "object",
      "additionalProperties": false,
      "required": [
        "name",
        "address",
        "postalCode",
        "city",
        "country"
      ],
      "properties": {
        "name": {
          "type": "string",
          "maxLength": 200
        },
        "address": {
          "type": "string",
          "maxLength": 300
        },
        "postalCode": {
          "type": "string",
          "maxLength": 12
        },
        "city": {
          "type": "string",
          "maxLength": 120
        },
        "province": {
          "type": "string",
          "maxLength": 120
        },
        "country": {
          "type": "string",
          "pattern": "^[A-Za-z]{2}$",
          "description": "Código ISO 3166-1 alfa-2 (ES, FR, PT…)."
        }
      }
    },
    "Shipment": {
      "type": "object",
      "additionalProperties": false,
      "required": [
        "loadLocation",
        "unloadLocation"
      ],
      "properties": {
        "loadLocation": {
          "$ref": "#/definitions/Location"
        },
        "unloadLocation": {
          "$ref": "#/definitions/Location"
        },
        "goods": {
          "type": "string",
          "maxLength": 300,
          "description": "Naturaleza de la mercancía."
        },
        "weight": {
          "description": "Peso o medida: un número (kg) o un texto (p. ej. \"12.000 kg\").",
          "oneOf": [
            {
              "type": "number",
              "exclusiveMinimum": 0,
              "maximum": 10000000
            },
            {
              "type": "string",
              "maxLength": 60
            }
          ]
        },
        "recipient": {
          "type": "string",
          "maxLength": 200
        },
        "loadDate": {
          "type": "string",
          "pattern": "^(\\d{2}-\\d{2}-\\d{4}|\\d{4}-\\d{2}-\\d{2})$",
          "description": "Fecha DD-MM-AAAA (también se acepta AAAA-MM-DD).",
          "examples": [
            "06-10-2026"
          ]
        },
        "unloadDate": {
          "type": "string",
          "pattern": "^(\\d{2}-\\d{2}-\\d{4}|\\d{4}-\\d{2}-\\d{2})$",
          "description": "Fecha DD-MM-AAAA (también se acepta AAAA-MM-DD).",
          "examples": [
            "06-10-2026"
          ]
        },
        "tractorPlate": {
          "type": "string",
          "maxLength": 20
        },
        "trailerPlate": {
          "type": "string",
          "maxLength": 20
        },
        "notes": {
          "type": "string",
          "maxLength": 1000,
          "description": "Información especial del envío. Admite varias líneas (saltos de línea)."
        }
      }
    },
    "CompletionRequest": {
      "type": "object",
      "additionalProperties": false,
      "required": [
        "recipientEmail",
        "fields"
      ],
      "properties": {
        "recipientEmail": {
          "type": "string",
          "format": "email",
          "maxLength": 200
        },
        "recipientName": {
          "type": "string",
          "maxLength": 200
        },
        "recipientType": {
          "type": "string",
          "enum": [
            "shipper",
            "carrier",
            "authorized_person"
          ]
        },
        "fields": {
          "type": "array",
          "minItems": 1,
          "maxItems": 20,
          "items": {
            "type": "string",
            "maxLength": 80
          },
          "description": "Campos que completa el tercero, p. ej. vehicle.tractorPlate, shipments[0].unloadLocation.city."
        },
        "expiresInHours": {
          "type": "integer",
          "minimum": 1,
          "maximum": 48
        }
      }
    },
    "DecaCreate": {
      "type": "object",
      "additionalProperties": false,
      "required": [
        "loadDate",
        "unloadDate",
        "vehicle",
        "shipper",
        "carrier",
        "shipments"
      ],
      "properties": {
        "externalId": {
          "type": "string",
          "pattern": "^[A-Za-z0-9._\\-/]{1,100}$",
          "description": "Tu identificador (pedido, expedición). Evita duplicados: un segundo DeCA activo con el mismo externalId responde 409."
        },
        "groupageRef": {
          "type": "string",
          "pattern": "^[Gg]-[A-Za-z0-9]{6,16}$",
          "description": "S-1905 — opcional: referencia operativa común a los DeCA independientes de un grupaje (un DeCA por cargador). Se puede filtrar con GET /decas?groupageRef=…"
        },
        "serviceType": {
          "type": "string",
          "enum": [
            "goods",
            "passenger"
          ],
          "default": "goods"
        },
        "reference": {
          "type": "string",
          "maxLength": 120,
          "description": "Referencia / notas del DeCA."
        },
        "notes": {
          "type": "string",
          "maxLength": 1000,
          "description": "Información especial (por defecto de todos los envíos). Admite varias líneas (saltos de línea)."
        },
        "loadDate": {
          "type": "string",
          "pattern": "^(\\d{2}-\\d{2}-\\d{4}|\\d{4}-\\d{2}-\\d{2})$",
          "description": "Fecha DD-MM-AAAA (también se acepta AAAA-MM-DD).",
          "examples": [
            "06-10-2026"
          ]
        },
        "unloadDate": {
          "type": "string",
          "pattern": "^(\\d{2}-\\d{2}-\\d{4}|\\d{4}-\\d{2}-\\d{2})$",
          "description": "Fecha DD-MM-AAAA (también se acepta AAAA-MM-DD).",
          "examples": [
            "06-10-2026"
          ]
        },
        "vehicle": {
          "type": "object",
          "additionalProperties": false,
          "required": [
            "tractorPlate"
          ],
          "properties": {
            "tractorPlate": {
              "type": "string",
              "maxLength": 20
            },
            "trailerPlate": {
              "type": "string",
              "maxLength": 20
            },
            "secondTrailerPlate": {
              "type": "string",
              "maxLength": 20,
              "description": "Opcional: segundo remolque/semirremolque (conjuntos de dos remolques). Requiere trailerPlate y debe ser distinta."
            }
          }
        },
        "shipper": {
          "description": "Cargador contractual: \"self\" (tu empresa) o sus datos.",
          "oneOf": [
            {
              "const": "self"
            },
            {
              "$ref": "#/definitions/Party"
            }
          ]
        },
        "carrier": {
          "description": "Transportista efectivo: \"self\" (tu empresa) o sus datos.",
          "oneOf": [
            {
              "const": "self"
            },
            {
              "$ref": "#/definitions/Party"
            }
          ]
        },
        "shipments": {
          "type": "array",
          "minItems": 1,
          "maxItems": 50,
          "items": {
            "$ref": "#/definitions/Shipment"
          }
        },
        "completionRequest": {
          "$ref": "#/definitions/CompletionRequest"
        },
        "draft": {
          "type": "boolean",
          "description": "true: guarda un borrador en lugar de emitir el DeCA."
        },
        "loadOrder": {
          "type": "object",
          "description": "S-1908 — emite una orden de carga CON este DeCA (mismas reglas que la web): solo si tu empresa es el cargador contractual y el plan incluye la orden de carga.",
          "properties": {
            "format": {
              "type": "string",
              "enum": [
                "joint",
                "separate"
              ]
            },
            "price": {
              "type": "string",
              "description": "Importe, p. ej. \"850,00\" (vacío = sin precio)."
            },
            "currency": {
              "type": "string",
              "enum": [
                "EUR",
                "USD",
                "GBP",
                "CHF"
              ]
            },
            "taxNote": {
              "type": "string"
            },
            "conditions": {
              "type": "string"
            },
            "paymentTerms": {
              "type": "string"
            },
            "billingInstructions": {
              "type": "string"
            }
          },
          "required": [
            "format"
          ]
        }
      }
    },
    "Correction": {
      "type": "object",
      "required": [
        "changeReason",
        "deca"
      ],
      "properties": {
        "changeReason": {
          "type": "string",
          "minLength": 3,
          "maxLength": 500
        },
        "baseVersionNo": {
          "type": "integer",
          "minimum": 1,
          "description": "Versión que estás corrigiendo (evita correcciones simultáneas: 409)."
        },
        "deca": {
          "$ref": "#/definitions/DecaCreate"
        }
      }
    },
    "Annulment": {
      "type": "object",
      "required": [
        "reason"
      ],
      "properties": {
        "reason": {
          "type": "string",
          "minLength": 3,
          "maxLength": 500
        }
      }
    },
    "VehicleChange": {
      "type": "object",
      "additionalProperties": false,
      "required": [
        "tractorPlate",
        "effectiveAt"
      ],
      "properties": {
        "tractorPlate": {
          "type": "string",
          "maxLength": 20
        },
        "trailerPlate": {
          "type": "string",
          "maxLength": 20
        },
        "effectiveAt": {
          "type": "string",
          "pattern": "^(\\d{2}-\\d{2}-\\d{4}|\\d{4}-\\d{2}-\\d{2})$",
          "description": "Fecha DD-MM-AAAA (también se acepta AAAA-MM-DD).",
          "examples": [
            "06-10-2026"
          ]
        },
        "reason": {
          "type": "string",
          "maxLength": 200
        }
      }
    },
    "CompletionResendRequest": {
      "type": "object",
      "properties": {
        "expiresInHours": {
          "type": "integer",
          "minimum": 1,
          "maximum": 48
        }
      }
    },
    "DraftCreate": {
      "type": "object",
      "description": "Cualquier subconjunto de los campos de DecaCreate (sin completionRequest ni draft).",
      "properties": {
        "externalId": {
          "type": "string",
          "pattern": "^[A-Za-z0-9._\\-/]{1,100}$"
        },
        "reference": {
          "type": "string",
          "maxLength": 120
        },
        "loadDate": {
          "type": "string",
          "pattern": "^(\\d{2}-\\d{2}-\\d{4}|\\d{4}-\\d{2}-\\d{2})$",
          "description": "Fecha DD-MM-AAAA (también se acepta AAAA-MM-DD).",
          "examples": [
            "06-10-2026"
          ]
        },
        "unloadDate": {
          "type": "string",
          "pattern": "^(\\d{2}-\\d{2}-\\d{4}|\\d{4}-\\d{2}-\\d{2})$",
          "description": "Fecha DD-MM-AAAA (también se acepta AAAA-MM-DD).",
          "examples": [
            "06-10-2026"
          ]
        },
        "vehicle": {
          "type": "object"
        },
        "shipper": {},
        "carrier": {},
        "shipments": {
          "type": "array",
          "items": {
            "type": "object"
          }
        }
      }
    },
    "DraftPatch": {
      "type": "object",
      "description": "JSON Merge Patch (RFC 7386) sobre el borrador."
    },
    "DecaStatus": {
      "type": "object",
      "required": [
        "id",
        "reference",
        "status",
        "availability",
        "version",
        "createdAt",
        "updatedAt"
      ],
      "properties": {
        "id": {
          "type": "string"
        },
        "reference": {
          "type": "string",
          "x-nullable": true,
          "examples": [
            "DECA-A4F2C9E1"
          ]
        },
        "externalId": {
          "type": "string",
          "x-nullable": true
        },
        "groupageRef": {
          "type": "string",
          "x-nullable": true
        },
        "status": {
          "type": "string",
          "enum": [
            "active",
            "annulled"
          ]
        },
        "availability": {
          "type": "string",
          "enum": [
            "public",
            "expired",
            "annulled"
          ]
        },
        "version": {
          "type": "integer",
          "x-nullable": true
        },
        "createdAt": {
          "type": "string",
          "description": "ISO 8601 con el desfase de Madrid, p. ej. 2026-10-08T10:41:00+02:00.",
          "pattern": "^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}[+-]\\d{2}:\\d{2}$"
        },
        "updatedAt": {
          "type": "string",
          "description": "ISO 8601 con el desfase de Madrid, p. ej. 2026-10-08T10:41:00+02:00.",
          "pattern": "^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}[+-]\\d{2}:\\d{2}$"
        },
        "annulledAt": {
          "type": "string",
          "x-nullable": true,
          "description": "ISO 8601 con el desfase de Madrid, p. ej. 2026-10-08T10:41:00+02:00.",
          "pattern": "^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}[+-]\\d{2}:\\d{2}$"
        },
        "service": {
          "type": "object",
          "properties": {
            "finished": {
              "type": "boolean"
            },
            "finishedAt": {
              "type": "string",
              "x-nullable": true,
              "description": "ISO 8601 con el desfase de Madrid, p. ej. 2026-10-08T10:41:00+02:00.",
              "pattern": "^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}[+-]\\d{2}:\\d{2}$"
            },
            "note": {
              "type": "string",
              "x-nullable": true
            }
          }
        },
        "verificationUrl": {
          "type": "string",
          "x-nullable": true,
          "format": "uri"
        }
      }
    },
    "ServiceCompletion": {
      "type": "object",
      "additionalProperties": false,
      "properties": {
        "note": {
          "type": "string",
          "maxLength": 500,
          "description": "Observación opcional."
        }
      }
    },
    "LoadOrder": {
      "type": "object",
      "required": [
        "id",
        "number",
        "status",
        "format"
      ],
      "properties": {
        "id": {
          "type": "string"
        },
        "number": {
          "type": "string"
        },
        "status": {
          "type": "string",
          "enum": [
            "pending_vehicle",
            "active",
            "cancelled"
          ]
        },
        "format": {
          "type": "string",
          "enum": [
            "joint",
            "separate"
          ]
        },
        "reference": {
          "type": "string",
          "x-nullable": true
        },
        "carrierName": {
          "type": "string",
          "x-nullable": true
        },
        "loadDate": {
          "type": "string",
          "x-nullable": true,
          "pattern": "^\\d{2}-\\d{2}-\\d{4}$",
          "description": "DD-MM-AAAA"
        },
        "deca": {
          "type": "object",
          "x-nullable": true,
          "properties": {
            "id": {
              "type": "string"
            },
            "status": {
              "type": "string",
              "enum": [
                "active",
                "annulled"
              ]
            }
          }
        },
        "platesPending": {
          "type": "boolean"
        },
        "version": {
          "type": "object",
          "x-nullable": true,
          "properties": {
            "number": {
              "type": "integer"
            },
            "kind": {
              "type": "string"
            },
            "createdAt": {
              "type": "string",
              "description": "ISO 8601 con el desfase de Madrid, p. ej. 2026-10-08T10:41:00+02:00.",
              "pattern": "^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}[+-]\\d{2}:\\d{2}$"
            },
            "pdfSha256": {
              "type": "string",
              "x-nullable": true
            }
          }
        },
        "acceptance": {
          "type": "object",
          "x-nullable": true,
          "properties": {
            "versionNo": {
              "type": "integer"
            },
            "acceptedAt": {
              "type": "string",
              "description": "ISO 8601 con el desfase de Madrid, p. ej. 2026-10-08T10:41:00+02:00.",
              "pattern": "^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}[+-]\\d{2}:\\d{2}$"
            }
          }
        },
        "createdAt": {
          "type": "string",
          "description": "ISO 8601 con el desfase de Madrid, p. ej. 2026-10-08T10:41:00+02:00.",
          "pattern": "^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}[+-]\\d{2}:\\d{2}$"
        },
        "links": {
          "type": "object",
          "additionalProperties": {
            "type": "string"
          }
        }
      }
    },
    "LoadOrderList": {
      "type": "object",
      "properties": {
        "data": {
          "type": "array",
          "items": {
            "$ref": "#/definitions/LoadOrder"
          }
        }
      }
    },
    "Deca": {
      "type": "object",
      "required": [
        "id",
        "status",
        "verification",
        "pdf",
        "links"
      ],
      "properties": {
        "id": {
          "type": "string"
        },
        "reference": {
          "type": "string",
          "x-nullable": true
        },
        "externalId": {
          "type": "string",
          "x-nullable": true
        },
        "groupageRef": {
          "type": "string",
          "x-nullable": true,
          "description": "S-1905 — referencia operativa del grupaje (G-…), común a los DeCA independientes de una misma operación."
        },
        "status": {
          "type": "string",
          "enum": [
            "active",
            "annulled"
          ]
        },
        "availability": {
          "type": "string",
          "enum": [
            "public",
            "expired",
            "annulled"
          ]
        },
        "version": {
          "type": "integer",
          "x-nullable": true
        },
        "createdAt": {
          "type": "string",
          "description": "ISO 8601 con el desfase de Madrid, p. ej. 2026-10-08T10:41:00+02:00.",
          "pattern": "^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}[+-]\\d{2}:\\d{2}$"
        },
        "updatedAt": {
          "type": "string",
          "description": "ISO 8601 con el desfase de Madrid, p. ej. 2026-10-08T10:41:00+02:00.",
          "pattern": "^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}[+-]\\d{2}:\\d{2}$"
        },
        "annulledAt": {
          "type": "string",
          "x-nullable": true,
          "description": "ISO 8601 con el desfase de Madrid, p. ej. 2026-10-08T10:41:00+02:00.",
          "pattern": "^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}[+-]\\d{2}:\\d{2}$"
        },
        "service": {
          "type": "object",
          "description": "S-1903 — marca OPERATIVA de servicio finalizado; nunca cambia el estado administrativo, el PDF ni la URL.",
          "properties": {
            "finished": {
              "type": "boolean"
            },
            "finishedAt": {
              "type": "string",
              "x-nullable": true,
              "description": "ISO 8601 con el desfase de Madrid, p. ej. 2026-10-08T10:41:00+02:00.",
              "pattern": "^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}[+-]\\d{2}:\\d{2}$"
            },
            "note": {
              "type": "string",
              "x-nullable": true
            }
          }
        },
        "serviceType": {
          "type": "string",
          "enum": [
            "goods",
            "passenger"
          ]
        },
        "servicePeriod": {
          "type": "object",
          "properties": {
            "start": {
              "type": "string",
              "x-nullable": true,
              "pattern": "^\\d{2}-\\d{2}-\\d{4}$",
              "description": "DD-MM-AAAA"
            },
            "end": {
              "type": "string",
              "x-nullable": true,
              "pattern": "^\\d{2}-\\d{2}-\\d{4}$",
              "description": "DD-MM-AAAA"
            },
            "publicUntil": {
              "type": "string",
              "x-nullable": true,
              "pattern": "^\\d{2}-\\d{2}-\\d{4}$",
              "description": "DD-MM-AAAA"
            }
          }
        },
        "verification": {
          "type": "object",
          "properties": {
            "url": {
              "type": "string",
              "x-nullable": true,
              "format": "uri"
            },
            "note": {
              "type": "string"
            }
          }
        },
        "pdf": {
          "type": "object",
          "properties": {
            "href": {
              "type": "string"
            },
            "sha256": {
              "type": "string",
              "x-nullable": true
            },
            "contentType": {
              "const": "application/pdf"
            }
          }
        },
        "data": {
          "type": "object",
          "x-nullable": true,
          "description": "Contenido del DeCA (partes, vehículo, envíos); fechas DD-MM-AAAA."
        },
        "warnings": {
          "type": "array",
          "items": {
            "type": "string"
          }
        },
        "links": {
          "type": "object",
          "additionalProperties": {
            "type": "string"
          }
        }
      }
    },
    "DecaList": {
      "type": "object",
      "required": [
        "data",
        "nextCursor"
      ],
      "properties": {
        "data": {
          "type": "array",
          "items": {
            "$ref": "#/definitions/DecaListItem"
          }
        },
        "nextCursor": {
          "type": "string",
          "x-nullable": true
        }
      }
    },
    "DecaListItem": {
      "type": "object",
      "description": "Un DeCA del listado: el estado ligero (como GET /decas/{id}/status), la URL de verificación, un resumen y los enlaces. El contenido completo está en GET /decas/{id}.",
      "required": [
        "id",
        "reference",
        "status",
        "availability",
        "version",
        "createdAt",
        "updatedAt",
        "verificationUrl",
        "summary",
        "links"
      ],
      "properties": {
        "id": {
          "type": "string"
        },
        "reference": {
          "type": "string",
          "x-nullable": true,
          "examples": [
            "DECA-A4F2C9E1"
          ]
        },
        "externalId": {
          "type": "string",
          "x-nullable": true
        },
        "groupageRef": {
          "type": "string",
          "x-nullable": true
        },
        "status": {
          "type": "string",
          "enum": [
            "active",
            "annulled"
          ]
        },
        "availability": {
          "type": "string",
          "enum": [
            "public",
            "expired",
            "annulled"
          ]
        },
        "version": {
          "type": "integer",
          "x-nullable": true
        },
        "createdAt": {
          "type": "string",
          "description": "ISO 8601 con el desfase de Madrid, p. ej. 2026-10-08T10:41:00+02:00.",
          "pattern": "^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}[+-]\\d{2}:\\d{2}$"
        },
        "updatedAt": {
          "type": "string",
          "description": "ISO 8601 con el desfase de Madrid, p. ej. 2026-10-08T10:41:00+02:00.",
          "pattern": "^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}[+-]\\d{2}:\\d{2}$"
        },
        "annulledAt": {
          "type": "string",
          "x-nullable": true,
          "description": "ISO 8601 con el desfase de Madrid, p. ej. 2026-10-08T10:41:00+02:00.",
          "pattern": "^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}[+-]\\d{2}:\\d{2}$"
        },
        "service": {
          "type": "object",
          "properties": {
            "finished": {
              "type": "boolean"
            },
            "finishedAt": {
              "type": "string",
              "x-nullable": true,
              "description": "ISO 8601 con el desfase de Madrid, p. ej. 2026-10-08T10:41:00+02:00.",
              "pattern": "^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}[+-]\\d{2}:\\d{2}$"
            },
            "note": {
              "type": "string",
              "x-nullable": true
            }
          }
        },
        "verificationUrl": {
          "type": "string",
          "x-nullable": true,
          "format": "uri"
        },
        "summary": {
          "type": "object",
          "x-nullable": true,
          "properties": {
            "shipper": {
              "type": "string",
              "x-nullable": true,
              "description": "Nombre del cargador."
            },
            "carrier": {
              "type": "string",
              "x-nullable": true,
              "description": "Nombre del transportista."
            },
            "tractorPlate": {
              "type": "string",
              "x-nullable": true
            },
            "loadDate": {
              "type": "string",
              "x-nullable": true,
              "pattern": "^\\d{2}-\\d{2}-\\d{4}$",
              "description": "DD-MM-AAAA"
            },
            "shipmentCount": {
              "type": "integer"
            }
          }
        },
        "links": {
          "type": "object",
          "additionalProperties": {
            "type": "string"
          }
        }
      }
    },
    "Validation": {
      "type": "object",
      "required": [
        "valid",
        "warnings"
      ],
      "properties": {
        "valid": {
          "const": true
        },
        "warnings": {
          "type": "array",
          "items": {
            "type": "string"
          }
        }
      }
    },
    "VersionList": {
      "type": "object",
      "required": [
        "data"
      ],
      "properties": {
        "data": {
          "type": "array",
          "items": {
            "type": "object",
            "required": [
              "version",
              "current",
              "createdAt",
              "actorType",
              "verificationUrl",
              "pdf"
            ],
            "properties": {
              "version": {
                "type": "integer"
              },
              "current": {
                "type": "boolean"
              },
              "createdAt": {
                "type": "string",
                "description": "ISO 8601 con el desfase de Madrid, p. ej. 2026-10-08T10:41:00+02:00.",
                "pattern": "^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}[+-]\\d{2}:\\d{2}$"
              },
              "changeReason": {
                "type": "string",
                "x-nullable": true
              },
              "actorType": {
                "type": "string",
                "enum": [
                  "api",
                  "user",
                  "anonymous"
                ]
              },
              "verificationUrl": {
                "type": "string",
                "format": "uri"
              },
              "pdf": {
                "type": "object",
                "properties": {
                  "href": {
                    "type": "string"
                  },
                  "sha256": {
                    "type": "string",
                    "x-nullable": true
                  }
                }
              }
            }
          }
        }
      }
    },
    "VehicleList": {
      "type": "object",
      "required": [
        "data"
      ],
      "properties": {
        "data": {
          "type": "array",
          "items": {
            "type": "object",
            "properties": {
              "kind": {
                "type": "string",
                "enum": [
                  "initial",
                  "change"
                ]
              },
              "tractorPlate": {
                "type": "string"
              },
              "trailerPlate": {
                "type": "string",
                "x-nullable": true
              },
              "effectiveAt": {
                "type": "string",
                "x-nullable": true,
                "pattern": "^\\d{2}-\\d{2}-\\d{4}$",
                "description": "DD-MM-AAAA"
              },
              "reason": {
                "type": "string",
                "x-nullable": true
              },
              "current": {
                "type": "boolean"
              }
            }
          }
        }
      }
    },
    "Me": {
      "type": "object",
      "required": [
        "credential",
        "company",
        "access",
        "limits",
        "usage"
      ],
      "properties": {
        "credential": {
          "type": "object",
          "properties": {
            "name": {
              "type": "string"
            },
            "key": {
              "type": "string",
              "description": "Clave enmascarada: nunca se devuelve el secreto."
            },
            "mode": {
              "type": "string",
              "enum": [
                "live",
                "test"
              ],
              "description": "test = clave de pruebas (fvd_test_)."
            },
            "scopes": {
              "type": "array",
              "items": {
                "type": "string"
              }
            },
            "createdAt": {
              "type": "string",
              "description": "ISO 8601 con el desfase de Madrid, p. ej. 2026-10-08T10:41:00+02:00.",
              "pattern": "^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}[+-]\\d{2}:\\d{2}$"
            },
            "expiresAt": {
              "type": "string",
              "x-nullable": true,
              "description": "ISO 8601 con el desfase de Madrid, p. ej. 2026-10-08T10:41:00+02:00.",
              "pattern": "^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}[+-]\\d{2}:\\d{2}$"
            }
          }
        },
        "company": {
          "type": "object",
          "properties": {
            "name": {
              "type": "string"
            },
            "taxId": {
              "type": "string",
              "x-nullable": true
            }
          }
        },
        "access": {
          "type": "object",
          "properties": {
            "plan": {
              "type": "string",
              "description": "Plan de la empresa."
            },
            "businessEligible": {
              "type": "boolean",
              "description": "El plan permite la API."
            },
            "apiEntitlement": {
              "type": "string",
              "enum": [
                "paid",
                "manual",
                "none"
              ],
              "description": "Origen del acceso a la API."
            },
            "paidAddon": {
              "type": "string",
              "enum": [
                "active",
                "cancelling",
                "inactive"
              ],
              "description": "Estado del complemento API."
            },
            "manualGrant": {
              "type": "string",
              "enum": [
                "active",
                "expired",
                "ended",
                "none"
              ]
            }
          }
        },
        "limits": {
          "type": "object",
          "properties": {
            "requestsPerMinute": {
              "type": "integer"
            },
            "createsPerDay": {
              "type": "integer"
            }
          }
        },
        "usage": {
          "type": "object",
          "properties": {
            "decasThisMonth": {
              "type": "integer",
              "description": "DeCA creados en el periodo actual del plan (API y panel comparten el cupo)."
            },
            "planMonthlyLimit": {
              "type": "integer",
              "x-nullable": true,
              "description": "Cupo mensual del plan (null = sin límite)."
            },
            "remainingThisMonth": {
              "type": "integer",
              "x-nullable": true
            },
            "decasThisPeriod": {
              "type": "integer"
            },
            "remainingThisPeriod": {
              "type": "integer",
              "x-nullable": true
            },
            "periodStart": {
              "type": "string",
              "x-nullable": true,
              "description": "ISO 8601 con el desfase de Madrid, p. ej. 2026-10-08T10:41:00+02:00.",
              "pattern": "^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}[+-]\\d{2}:\\d{2}$"
            },
            "periodEnd": {
              "type": "string",
              "x-nullable": true,
              "description": "ISO 8601 con el desfase de Madrid, p. ej. 2026-10-08T10:41:00+02:00.",
              "pattern": "^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}[+-]\\d{2}:\\d{2}$"
            }
          }
        }
      }
    },
    "Draft": {
      "type": "object",
      "required": [
        "id",
        "status"
      ],
      "properties": {
        "id": {
          "type": "string"
        },
        "draftId": {
          "type": "string"
        },
        "object": {
          "type": "string",
          "enum": [
            "api_draft",
            "draft"
          ],
          "description": "api_draft = creado por la API; draft = borrador iniciado en la web."
        },
        "origin": {
          "type": "string",
          "enum": [
            "api",
            "web"
          ]
        },
        "status": {
          "type": "string",
          "enum": [
            "open",
            "generated",
            "cancelled"
          ]
        },
        "version": {
          "type": "integer",
          "description": "Versión del borrador (ETag \"<id>.<version>\")."
        },
        "externalId": {
          "type": "string",
          "x-nullable": true
        },
        "data": {
          "type": "object"
        },
        "ready": {
          "type": "boolean",
          "description": "Solo en borradores abiertos leídos uno a uno: true si se puede generar ya."
        },
        "warnings": {
          "type": "array",
          "items": {
            "type": "string"
          }
        },
        "issues": {
          "type": "array",
          "description": "Lo que falta o no es válido (vacío si ready es true).",
          "items": {
            "type": "object",
            "properties": {
              "path": {
                "type": "string"
              },
              "code": {
                "type": "string"
              },
              "message": {
                "type": "string"
              }
            }
          }
        },
        "createdBy": {
          "type": "string",
          "x-nullable": true,
          "description": "Solo borradores web."
        },
        "updatedBy": {
          "type": "string",
          "x-nullable": true,
          "description": "Solo borradores web."
        },
        "decaId": {
          "type": "string",
          "x-nullable": true
        },
        "generatedAt": {
          "type": "string",
          "x-nullable": true,
          "description": "ISO 8601 con el desfase de Madrid, p. ej. 2026-10-08T10:41:00+02:00.",
          "pattern": "^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}[+-]\\d{2}:\\d{2}$"
        },
        "cancelledAt": {
          "type": "string",
          "x-nullable": true,
          "description": "ISO 8601 con el desfase de Madrid, p. ej. 2026-10-08T10:41:00+02:00.",
          "pattern": "^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}[+-]\\d{2}:\\d{2}$"
        },
        "createdAt": {
          "type": "string",
          "description": "ISO 8601 con el desfase de Madrid, p. ej. 2026-10-08T10:41:00+02:00.",
          "pattern": "^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}[+-]\\d{2}:\\d{2}$"
        },
        "updatedAt": {
          "type": "string",
          "description": "ISO 8601 con el desfase de Madrid, p. ej. 2026-10-08T10:41:00+02:00.",
          "pattern": "^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}[+-]\\d{2}:\\d{2}$"
        },
        "links": {
          "type": "object",
          "additionalProperties": {
            "type": "string"
          }
        }
      }
    },
    "DraftList": {
      "type": "object",
      "required": [
        "data"
      ],
      "properties": {
        "data": {
          "type": "array",
          "items": {
            "$ref": "#/definitions/Draft"
          }
        },
        "nextCursor": {
          "type": "string",
          "x-nullable": true
        }
      }
    },
    "WebhookEvent": {
      "type": "object",
      "required": [
        "id",
        "type",
        "createdAt",
        "livemode",
        "data"
      ],
      "description": "Aviso enviado a tu endpoint. Cabeceras: DeCA-Event-Id, DeCA-Event-Type, DeCA-Signature (t=<s>,v1=<HMAC-SHA256 hex de \"<t>.<cuerpo>\">).",
      "properties": {
        "id": {
          "type": "string",
          "description": "Igual en todos los reintentos: úsalo para descartar duplicados."
        },
        "type": {
          "type": "string",
          "enum": [
            "deca.created",
            "deca.corrected",
            "deca.annulled",
            "draft.generated",
            "completion.completed",
            "deca.service_finished",
            "deca.service_reopened",
            "load_order.issued",
            "load_order.accepted",
            "webhook.test"
          ]
        },
        "createdAt": {
          "type": "string",
          "description": "ISO 8601 con el desfase de Madrid, p. ej. 2026-10-08T10:41:00+02:00.",
          "pattern": "^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}[+-]\\d{2}:\\d{2}$"
        },
        "livemode": {
          "type": "boolean"
        },
        "data": {
          "type": "object"
        }
      }
    },
    "CompletionRequestResult": {
      "type": "object",
      "description": "Borrador con su solicitud a un tercero (estado, destinatario, caducidad)."
    },
    "SavedCompanyInput": {
      "type": "object",
      "additionalProperties": false,
      "required": [
        "name",
        "taxId",
        "address",
        "postalCode",
        "city"
      ],
      "properties": {
        "name": {
          "type": "string",
          "minLength": 2,
          "maxLength": 200
        },
        "taxId": {
          "type": "string",
          "minLength": 3,
          "maxLength": 20
        },
        "address": {
          "type": "string",
          "minLength": 4,
          "maxLength": 300
        },
        "postalCode": {
          "type": "string",
          "minLength": 3,
          "maxLength": 12
        },
        "city": {
          "type": "string",
          "minLength": 2,
          "maxLength": 120
        },
        "role": {
          "type": "string",
          "enum": [
            "shipper",
            "carrier",
            "both"
          ]
        }
      }
    },
    "SavedVehicleInput": {
      "type": "object",
      "additionalProperties": false,
      "required": [
        "tractorPlate"
      ],
      "properties": {
        "tractorPlate": {
          "type": "string",
          "minLength": 2,
          "maxLength": 20
        },
        "trailerPlate": {
          "type": "string",
          "maxLength": 20
        },
        "alias": {
          "type": "string",
          "maxLength": 80
        },
        "habitualDriver": {
          "type": "object",
          "additionalProperties": false,
          "properties": {
            "name": {
              "type": "string",
              "maxLength": 200
            },
            "phone": {
              "type": "string",
              "maxLength": 40
            },
            "email": {
              "type": "string",
              "maxLength": 200
            }
          }
        }
      }
    },
    "SavedPlaceInput": {
      "type": "object",
      "additionalProperties": false,
      "required": [
        "name",
        "address",
        "postalCode",
        "city"
      ],
      "properties": {
        "name": {
          "type": "string",
          "minLength": 2,
          "maxLength": 200
        },
        "address": {
          "type": "string",
          "minLength": 4,
          "maxLength": 300
        },
        "postalCode": {
          "type": "string",
          "minLength": 3,
          "maxLength": 12
        },
        "city": {
          "type": "string",
          "minLength": 2,
          "maxLength": 120
        },
        "province": {
          "type": "string",
          "maxLength": 120
        },
        "country": {
          "type": "string",
          "maxLength": 80
        },
        "type": {
          "type": "string",
          "enum": [
            "load",
            "unload",
            "both"
          ]
        }
      }
    },
    "SavedShipmentInput": {
      "type": "object",
      "additionalProperties": false,
      "required": [
        "loadPlaceExternalId",
        "unloadPlaceExternalId"
      ],
      "properties": {
        "name": {
          "type": "string",
          "maxLength": 200
        },
        "loadPlaceExternalId": {
          "type": "string",
          "pattern": "^[A-Za-z0-9._~:-]{1,100}$"
        },
        "unloadPlaceExternalId": {
          "type": "string",
          "pattern": "^[A-Za-z0-9._~:-]{1,100}$"
        },
        "goods": {
          "type": "string",
          "maxLength": 300
        },
        "weight": {
          "type": "string",
          "maxLength": 60
        },
        "recipient": {
          "type": "string",
          "maxLength": 200
        }
      }
    },
    "SavedRecord": {
      "type": "object",
      "description": "Un dato habitual: id, externalId (null si se creó en la web), createdAt y los campos del recurso (los de su *Input; los envíos devuelven loadPlace y unloadPlace con id, externalId y name).",
      "properties": {
        "id": {
          "type": "string"
        },
        "externalId": {
          "type": "string",
          "x-nullable": true
        },
        "createdAt": {
          "type": "string",
          "description": "ISO 8601 con el desfase de Madrid, p. ej. 2026-10-08T10:41:00+02:00.",
          "pattern": "^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}[+-]\\d{2}:\\d{2}$"
        }
      }
    },
    "SavedList": {
      "type": "object",
      "properties": {
        "data": {
          "type": "array",
          "items": {
            "$ref": "#/definitions/SavedRecord"
          }
        },
        "nextCursor": {
          "type": "string",
          "x-nullable": true
        }
      }
    },
    "SavedDeleted": {
      "type": "object",
      "properties": {
        "deleted": {
          "type": "boolean",
          "const": true
        },
        "externalId": {
          "type": "string"
        }
      }
    },
    "BatchCreate": {
      "type": "object",
      "required": [
        "items"
      ],
      "properties": {
        "items": {
          "type": "array",
          "minItems": 1,
          "maxItems": 50,
          "items": {
            "$ref": "#/definitions/DecaCreate"
          }
        }
      }
    },
    "Batch": {
      "type": "object",
      "properties": {
        "id": {
          "type": "string"
        },
        "status": {
          "type": "string",
          "enum": [
            "processing",
            "completed"
          ]
        },
        "total": {
          "type": "integer"
        },
        "counts": {
          "type": "object",
          "additionalProperties": {
            "type": "integer"
          }
        },
        "createdAt": {
          "type": "string",
          "description": "ISO 8601 con el desfase de Madrid, p. ej. 2026-10-08T10:41:00+02:00.",
          "pattern": "^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}[+-]\\d{2}:\\d{2}$"
        },
        "completedAt": {
          "type": "string",
          "x-nullable": true,
          "description": "ISO 8601 con el desfase de Madrid, p. ej. 2026-10-08T10:41:00+02:00.",
          "pattern": "^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}[+-]\\d{2}:\\d{2}$"
        },
        "items": {
          "type": "array",
          "items": {
            "type": "object",
            "properties": {
              "index": {
                "type": "integer"
              },
              "externalId": {
                "type": "string",
                "x-nullable": true
              },
              "status": {
                "type": "string",
                "enum": [
                  "pending",
                  "processing",
                  "created",
                  "replayed",
                  "failed"
                ]
              },
              "decaId": {
                "type": "string",
                "x-nullable": true
              },
              "error": {
                "type": "object",
                "x-nullable": true,
                "properties": {
                  "code": {
                    "type": "string"
                  },
                  "message": {
                    "type": "string"
                  },
                  "details": {}
                }
              }
            }
          }
        },
        "links": {
          "type": "object",
          "properties": {
            "self": {
              "type": "string"
            },
            "pdfs": {
              "type": "string"
            }
          }
        }
      }
    },
    "Error": {
      "type": "object",
      "required": [
        "error"
      ],
      "properties": {
        "error": {
          "type": "object",
          "required": [
            "code",
            "message",
            "requestId"
          ],
          "properties": {
            "code": {
              "type": "string",
              "description": "Código estable del error."
            },
            "message": {
              "type": "string"
            },
            "requestId": {
              "type": "string"
            },
            "details": {
              "type": "array",
              "items": {
                "type": "object",
                "properties": {
                  "path": {
                    "type": "string"
                  },
                  "code": {
                    "type": "string"
                  },
                  "message": {
                    "type": "string"
                  }
                }
              }
            }
          }
        }
      }
    }
  },
  "paths": {
    "/api/v1/me": {
      "get": {
        "operationId": "get_me",
        "summary": "Comprueba la credencial: integración, empresa, permisos, límites y uso del mes.",
        "description": "El primer endpoint que debes llamar para verificar la conexión.\n\nNo requiere permiso específico.",
        "consumes": [
          "application/json"
        ],
        "produces": [
          "application/json"
        ],
        "parameters": [],
        "responses": {
          "200": {
            "description": "Correcto",
            "schema": {
              "$ref": "#/definitions/Me"
            }
          },
          "default": {
            "description": "Error (consulta error.code)"
          }
        }
      }
    },
    "/api/v1/decas/validate": {
      "post": {
        "operationId": "post_decas_validate",
        "summary": "Valida un DeCA sin crearlo: mismas reglas que la creación.",
        "description": "No crea ningún DeCA, no genera PDF y no consume el cupo del plan.\n\nPermiso necesario: deca:create.",
        "consumes": [
          "application/json"
        ],
        "produces": [
          "application/json"
        ],
        "parameters": [
          {
            "name": "body",
            "in": "body",
            "required": true,
            "schema": {
              "$ref": "#/definitions/DecaCreate"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Correcto",
            "schema": {
              "$ref": "#/definitions/Validation"
            }
          },
          "default": {
            "description": "Error (consulta error.code)"
          }
        }
      }
    },
    "/api/v1/decas": {
      "post": {
        "operationId": "post_decas",
        "summary": "Crea un DeCA y devuelve la URL de verificación, la referencia y el PDF.",
        "description": "Responde 201 (o 200 si es un reintento con la misma Idempotency-Key). Con completionRequest (o draft: true) crea un borrador y responde 202: ver «Completar un DeCA por un tercero».\n\nPermiso necesario: deca:create.",
        "consumes": [
          "application/json"
        ],
        "produces": [
          "application/json"
        ],
        "parameters": [
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": true,
            "description": "Clave única por operación (8–255 caracteres). Un reintento con la misma clave y el mismo cuerpo devuelve el mismo resultado.",
            "type": "string"
          },
          {
            "name": "body",
            "in": "body",
            "required": true,
            "schema": {
              "$ref": "#/definitions/DecaCreate"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Correcto",
            "schema": {
              "$ref": "#/definitions/Deca"
            }
          },
          "default": {
            "description": "Error (consulta error.code)"
          }
        }
      },
      "get": {
        "operationId": "get_decas",
        "summary": "Lista los DeCA, del más reciente al más antiguo, con paginación por cursor.",
        "description": "Filtros: limit (1–100, por defecto 25), cursor, externalId, groupageRef (referencia del grupaje, G-…), status (active | annulled), createdFrom, createdTo (DD-MM-AAAA, días en hora de Madrid; createdTo incluye todo ese día). Todas las fechas de la API van en DD-MM-AAAA y las horas en hora de Madrid.\n\nPermiso necesario: deca:read.",
        "consumes": [
          "application/json"
        ],
        "produces": [
          "application/json"
        ],
        "parameters": [
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "description": "Elementos por página (1–100, por defecto 25).",
            "type": "string"
          },
          {
            "name": "cursor",
            "in": "query",
            "required": false,
            "description": "nextCursor de la página anterior.",
            "type": "string"
          },
          {
            "name": "externalId",
            "in": "query",
            "required": false,
            "description": "Búsqueda exacta por externalId.",
            "type": "string"
          },
          {
            "name": "groupageRef",
            "in": "query",
            "required": false,
            "description": "Referencia del grupaje (G-…); no distingue mayúsculas.",
            "type": "string"
          },
          {
            "name": "status",
            "in": "query",
            "required": false,
            "description": "Estado administrativo.",
            "type": "string"
          },
          {
            "name": "createdFrom",
            "in": "query",
            "required": false,
            "description": "Creados desde este día (DD-MM-AAAA, día en hora de Madrid; también una fecha y hora ISO 8601 con zona).",
            "type": "string"
          },
          {
            "name": "createdTo",
            "in": "query",
            "required": false,
            "description": "Creados hasta este día incluido (DD-MM-AAAA, día en hora de Madrid; también una fecha y hora ISO 8601 con zona).",
            "type": "string"
          }
        ],
        "responses": {
          "200": {
            "description": "Correcto",
            "schema": {
              "$ref": "#/definitions/DecaList"
            }
          },
          "default": {
            "description": "Error (consulta error.code)"
          }
        }
      }
    },
    "/api/v1/decas/{id}": {
      "get": {
        "operationId": "get_decas_id",
        "summary": "El DeCA completo: estado, periodo del servicio, URL de verificación, PDF y datos.",
        "description": "Permiso necesario: deca:read.",
        "consumes": [
          "application/json"
        ],
        "produces": [
          "application/json"
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Identificador del DeCA.",
            "type": "string"
          }
        ],
        "responses": {
          "200": {
            "description": "Correcto",
            "schema": {
              "$ref": "#/definitions/Deca"
            }
          },
          "default": {
            "description": "Error (consulta error.code)"
          }
        }
      }
    },
    "/api/v1/decas/{id}/status": {
      "get": {
        "operationId": "get_decas_id_status",
        "summary": "Estado ligero (versión, disponibilidad, URL de verificación) para consultas periódicas.",
        "description": "Permiso necesario: deca:read.",
        "consumes": [
          "application/json"
        ],
        "produces": [
          "application/json"
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Identificador del DeCA.",
            "type": "string"
          }
        ],
        "responses": {
          "200": {
            "description": "Correcto",
            "schema": {
              "$ref": "#/definitions/DecaStatus"
            }
          },
          "default": {
            "description": "Error (consulta error.code)"
          }
        }
      }
    },
    "/api/v1/decas/{id}/versions": {
      "get": {
        "operationId": "get_decas_id_versions",
        "summary": "Historial de versiones: motivo del cambio, origen (api / user) y URL y PDF de cada una.",
        "description": "Permiso necesario: deca:read.",
        "consumes": [
          "application/json"
        ],
        "produces": [
          "application/json"
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Identificador del DeCA.",
            "type": "string"
          }
        ],
        "responses": {
          "200": {
            "description": "Correcto",
            "schema": {
              "$ref": "#/definitions/VersionList"
            }
          },
          "default": {
            "description": "Error (consulta error.code)"
          }
        }
      }
    },
    "/api/v1/decas/{id}/pdf": {
      "get": {
        "operationId": "get_decas_id_pdf",
        "summary": "Descarga el PDF (versión vigente o ?version=N).",
        "description": "Cabeceras X-Pdf-Sha256 (huella del PDF) y X-Deca-Status (active | annulled).\n\nPermiso necesario: deca:read.",
        "consumes": [
          "application/json"
        ],
        "produces": [
          "application/pdf"
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Identificador del DeCA.",
            "type": "string"
          },
          {
            "name": "version",
            "in": "query",
            "required": false,
            "description": "Número de versión (por defecto, la vigente).",
            "type": "string"
          }
        ],
        "responses": {
          "200": {
            "description": "Correcto"
          },
          "default": {
            "description": "Error (consulta error.code)"
          }
        }
      }
    },
    "/api/v1/decas/{id}/corrections": {
      "post": {
        "operationId": "post_decas_id_corrections",
        "summary": "Corrige un DeCA: crea una nueva versión con nueva URL, QR y PDF.",
        "description": "Responde 201 con la nueva versión (200 si es un reintento con la misma Idempotency-Key). Solo DeCA creados por la misma integración; las versiones anteriores se conservan.\n\nPermiso necesario: deca:correct.",
        "consumes": [
          "application/json"
        ],
        "produces": [
          "application/json"
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Identificador del DeCA.",
            "type": "string"
          },
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": true,
            "description": "Clave única por operación (8–255 caracteres). Un reintento con la misma clave y el mismo cuerpo devuelve el mismo resultado.",
            "type": "string"
          },
          {
            "name": "body",
            "in": "body",
            "required": true,
            "schema": {
              "$ref": "#/definitions/Correction"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Correcto",
            "schema": {
              "$ref": "#/definitions/Deca"
            }
          },
          "default": {
            "description": "Error (consulta error.code)"
          }
        }
      }
    },
    "/api/v1/decas/{id}/vehicles": {
      "get": {
        "operationId": "get_decas_id_vehicles",
        "summary": "Vehículos del transporte: el inicial y cada cambio registrado, con el actual marcado.",
        "description": "Permiso necesario: deca:read.",
        "consumes": [
          "application/json"
        ],
        "produces": [
          "application/json"
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Identificador del DeCA.",
            "type": "string"
          }
        ],
        "responses": {
          "200": {
            "description": "Correcto",
            "schema": {
              "$ref": "#/definitions/VehicleList"
            }
          },
          "default": {
            "description": "Error (consulta error.code)"
          }
        }
      }
    },
    "/api/v1/decas/{id}/vehicle-changes": {
      "post": {
        "operationId": "post_decas_id_vehicle_changes",
        "summary": "Registra un cambio de vehículo (tractora y, si cambia, remolque) durante el transporte.",
        "description": "Es una corrección: nueva versión con nueva URL, QR y PDF, que no consume DeCA del plan. La fecha (DD-MM-AAAA) debe estar dentro del transporte y posterior al cambio anterior. Sin trailerPlate se mantiene el remolque anterior. Responde 201 (200 si es un reintento con la misma Idempotency-Key).\n\nPermiso necesario: deca:correct.",
        "consumes": [
          "application/json"
        ],
        "produces": [
          "application/json"
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Identificador del DeCA.",
            "type": "string"
          },
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": true,
            "description": "Clave única por operación (8–255 caracteres). Un reintento con la misma clave y el mismo cuerpo devuelve el mismo resultado.",
            "type": "string"
          },
          {
            "name": "body",
            "in": "body",
            "required": true,
            "schema": {
              "$ref": "#/definitions/VehicleChange"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Correcto",
            "schema": {
              "$ref": "#/definitions/Deca"
            }
          },
          "default": {
            "description": "Error (consulta error.code)"
          }
        }
      }
    },
    "/api/v1/decas/{id}/annulment": {
      "post": {
        "operationId": "post_decas_id_annulment",
        "summary": "Anula un DeCA. Es definitivo; repetir la llamada devuelve 200 sin cambios.",
        "description": "Solo DeCA creados por la misma integración.\n\nPermiso necesario: deca:annul.",
        "consumes": [
          "application/json"
        ],
        "produces": [
          "application/json"
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Identificador del DeCA.",
            "type": "string"
          },
          {
            "name": "body",
            "in": "body",
            "required": true,
            "schema": {
              "$ref": "#/definitions/Annulment"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Correcto",
            "schema": {
              "$ref": "#/definitions/Deca"
            }
          },
          "default": {
            "description": "Error (consulta error.code)"
          }
        }
      }
    },
    "/api/v1/decas/{id}/completion-request": {
      "post": {
        "operationId": "post_decas_id_completion_request",
        "summary": "Pide a un tercero, por email, los datos que faltan en un borrador.",
        "description": "Responde 201 (200 si es un reintento con la misma Idempotency-Key). Solo borradores de la misma integración.\n\nPermiso necesario: deca:create.",
        "consumes": [
          "application/json"
        ],
        "produces": [
          "application/json"
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Identificador del DeCA.",
            "type": "string"
          },
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": true,
            "description": "Clave única por operación (8–255 caracteres). Un reintento con la misma clave y el mismo cuerpo devuelve el mismo resultado.",
            "type": "string"
          },
          {
            "name": "body",
            "in": "body",
            "required": true,
            "schema": {
              "$ref": "#/definitions/CompletionRequest"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Correcto",
            "schema": {
              "$ref": "#/definitions/CompletionRequestResult"
            }
          },
          "default": {
            "description": "Error (consulta error.code)"
          }
        }
      }
    },
    "/api/v1/decas/{id}/completion-request/resend": {
      "post": {
        "operationId": "post_decas_id_completion_request_resend",
        "summary": "Reenvía la solicitud con un enlace nuevo (el anterior deja de funcionar).",
        "description": "Responde 201 (200 si es un reintento). Máximo 5 reenvíos por borrador y hora.\n\nPermiso necesario: deca:create.",
        "consumes": [
          "application/json"
        ],
        "produces": [
          "application/json"
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Identificador del DeCA.",
            "type": "string"
          },
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": true,
            "description": "Clave única por operación (8–255 caracteres). Un reintento con la misma clave y el mismo cuerpo devuelve el mismo resultado.",
            "type": "string"
          }
        ],
        "responses": {
          "200": {
            "description": "Correcto",
            "schema": {
              "$ref": "#/definitions/CompletionRequestResult"
            }
          },
          "default": {
            "description": "Error (consulta error.code)"
          }
        }
      }
    },
    "/api/v1/decas/batch": {
      "post": {
        "operationId": "post_decas_batch",
        "summary": "Crea hasta 50 DeCA en una sola llamada (lote).",
        "description": "Cuerpo { items: [ …mismo formato que POST /decas… ] }. Responde 202 con el lote ya validado: cada elemento queda pending o failed con sus detalles (un error no afecta a los demás). La creación sigue en segundo plano; consulta GET /api/v1/batches/{id}. Cada elemento consume un DeCA del plan como una creación normal. La misma Idempotency-Key con el mismo cuerpo devuelve el mismo lote (200); nunca se duplica un DeCA.\n\nPermiso necesario: deca:create.",
        "consumes": [
          "application/json"
        ],
        "produces": [
          "application/json"
        ],
        "parameters": [
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": true,
            "description": "Clave única por operación (8–255 caracteres). Un reintento con la misma clave y el mismo cuerpo devuelve el mismo resultado.",
            "type": "string"
          }
        ],
        "responses": {
          "200": {
            "description": "Correcto",
            "schema": {
              "$ref": "#/definitions/Batch"
            }
          },
          "default": {
            "description": "Error (consulta error.code)"
          }
        }
      }
    },
    "/api/v1/batches/{id}": {
      "get": {
        "operationId": "get_batches_id",
        "summary": "Estado del lote y resultado de cada elemento (created, replayed, failed, pending).",
        "description": "status pasa de processing a completed cuando todos los elementos han terminado.\n\nPermiso necesario: deca:read.",
        "consumes": [
          "application/json"
        ],
        "produces": [
          "application/json"
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Identificador del lote.",
            "type": "string"
          }
        ],
        "responses": {
          "200": {
            "description": "Correcto",
            "schema": {
              "$ref": "#/definitions/Batch"
            }
          },
          "default": {
            "description": "Error (consulta error.code)"
          }
        }
      }
    },
    "/api/v1/batches/{id}/pdfs": {
      "get": {
        "operationId": "get_batches_id_pdfs",
        "summary": "Descarga un ZIP con el PDF vigente de cada DeCA creado por el lote.",
        "description": "Responde 409 batch_without_documents si el lote aún no ha creado ninguno.\n\nPermiso necesario: deca:read.",
        "consumes": [
          "application/json"
        ],
        "produces": [
          "application/zip"
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Identificador del lote.",
            "type": "string"
          }
        ],
        "responses": {
          "200": {
            "description": "Correcto"
          },
          "default": {
            "description": "Error (consulta error.code)"
          }
        }
      }
    },
    "/api/v1/drafts": {
      "post": {
        "operationId": "post_drafts",
        "summary": "Abre un borrador nuevo e independiente (datos parciales). No consume cuota.",
        "description": "Responde 201 con draftId (200 si es un reintento con la misma Idempotency-Key).\n\nPermiso necesario: deca:create.",
        "consumes": [
          "application/json"
        ],
        "produces": [
          "application/json"
        ],
        "parameters": [
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": true,
            "description": "Clave única por operación (8–255 caracteres). Un reintento con la misma clave y el mismo cuerpo devuelve el mismo resultado.",
            "type": "string"
          },
          {
            "name": "body",
            "in": "body",
            "required": true,
            "schema": {
              "$ref": "#/definitions/DraftCreate"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Correcto",
            "schema": {
              "$ref": "#/definitions/Draft"
            }
          },
          "default": {
            "description": "Error (consulta error.code)"
          }
        }
      },
      "get": {
        "operationId": "get_drafts",
        "summary": "Lista los borradores: status=open (por defecto) | generated | cancelled | all.",
        "description": "Paginado con limit y cursor, como /decas. Los borradores son de la empresa: con deca:read_company verás también los empezados en la web (origin: \"web\") y los de otras integraciones, y podrás completarlos, generarlos o descartarlos.\n\nPermiso necesario: deca:read.",
        "consumes": [
          "application/json"
        ],
        "produces": [
          "application/json"
        ],
        "parameters": [
          {
            "name": "status",
            "in": "query",
            "required": false,
            "description": "Estado de los borradores (por defecto open).",
            "type": "string"
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "description": "Elementos por página (1–100, por defecto 25).",
            "type": "string"
          },
          {
            "name": "cursor",
            "in": "query",
            "required": false,
            "description": "nextCursor de la página anterior.",
            "type": "string"
          }
        ],
        "responses": {
          "200": {
            "description": "Correcto",
            "schema": {
              "$ref": "#/definitions/DraftList"
            }
          },
          "default": {
            "description": "Error (consulta error.code)"
          }
        }
      }
    },
    "/api/v1/drafts/{id}": {
      "get": {
        "operationId": "get_drafts_id",
        "summary": "Un borrador, con ready e issues (lo que falta para poder generarlo).",
        "description": "Permiso necesario: deca:read.",
        "consumes": [
          "application/json"
        ],
        "produces": [
          "application/json"
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Identificador del borrador.",
            "type": "string"
          }
        ],
        "responses": {
          "200": {
            "description": "Correcto",
            "schema": {
              "$ref": "#/definitions/Draft"
            }
          },
          "default": {
            "description": "Error (consulta error.code)"
          }
        }
      },
      "patch": {
        "operationId": "patch_drafts_id",
        "summary": "Modifica o completa un borrador abierto (JSON Merge Patch).",
        "description": "Opcional If-Match con el ETag: 412 si otro cambio llegó antes.\n\nPermiso necesario: deca:create.",
        "consumes": [
          "application/json"
        ],
        "produces": [
          "application/json"
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Identificador del borrador.",
            "type": "string"
          },
          {
            "name": "If-Match",
            "in": "header",
            "required": false,
            "description": "ETag leído del borrador: evita sobrescribir cambios de otro (412 precondition_failed).",
            "type": "string"
          },
          {
            "name": "body",
            "in": "body",
            "required": true,
            "schema": {
              "$ref": "#/definitions/DraftPatch"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Correcto",
            "schema": {
              "$ref": "#/definitions/Draft"
            }
          },
          "default": {
            "description": "Error (consulta error.code)"
          }
        }
      },
      "delete": {
        "operationId": "delete_drafts_id",
        "summary": "Descarta un borrador abierto (queda como cancelled).",
        "description": "Permiso necesario: deca:create.",
        "consumes": [
          "application/json"
        ],
        "produces": [
          "application/json"
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Identificador del borrador.",
            "type": "string"
          },
          {
            "name": "If-Match",
            "in": "header",
            "required": false,
            "description": "ETag leído del borrador: evita sobrescribir cambios de otro (412 precondition_failed).",
            "type": "string"
          }
        ],
        "responses": {
          "200": {
            "description": "Correcto",
            "schema": {
              "$ref": "#/definitions/Draft"
            }
          },
          "default": {
            "description": "Error (consulta error.code)"
          }
        }
      }
    },
    "/api/v1/drafts/{id}/generate": {
      "post": {
        "operationId": "post_drafts_id_generate",
        "summary": "Genera el DeCA definitivo de ese borrador; los demás no cambian.",
        "description": "201 con el DeCA; repetir devuelve el mismo DeCA (200). Aquí se consume la cuota.\n\nPermiso necesario: deca:create.",
        "consumes": [
          "application/json"
        ],
        "produces": [
          "application/json"
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Identificador del borrador.",
            "type": "string"
          },
          {
            "name": "If-Match",
            "in": "header",
            "required": false,
            "description": "ETag leído del borrador: evita sobrescribir cambios de otro (412 precondition_failed).",
            "type": "string"
          }
        ],
        "responses": {
          "201": {
            "description": "Correcto",
            "schema": {
              "$ref": "#/definitions/Deca"
            }
          },
          "default": {
            "description": "Error (consulta error.code)"
          }
        }
      }
    },
    "/api/v1/decas/{id}/service-completion": {
      "post": {
        "operationId": "post_decas_id_service_completion",
        "summary": "Marca el servicio como finalizado (dato operativo; el DeCA, su PDF y su URL no cambian).",
        "description": "Cuerpo opcional { note } (hasta 500 caracteres). Idempotente: devuelve el DeCA con service.finished. 409 deca_annulled en un DeCA anulado. Webhook deca.service_finished.\n\nPermiso necesario: deca:correct.",
        "consumes": [
          "application/json"
        ],
        "produces": [
          "application/json"
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Identificador del DeCA.",
            "type": "string"
          }
        ],
        "responses": {
          "200": {
            "description": "Correcto",
            "schema": {
              "$ref": "#/definitions/Deca"
            }
          },
          "default": {
            "description": "Error (consulta error.code)"
          }
        }
      },
      "delete": {
        "operationId": "delete_decas_id_service_completion",
        "summary": "Reabre un servicio marcado como finalizado (vuelve a pendiente).",
        "description": "Idempotente. Webhook deca.service_reopened.\n\nPermiso necesario: deca:correct.",
        "consumes": [
          "application/json"
        ],
        "produces": [
          "application/json"
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Identificador del DeCA.",
            "type": "string"
          }
        ],
        "responses": {
          "200": {
            "description": "Correcto",
            "schema": {
              "$ref": "#/definitions/Deca"
            }
          },
          "default": {
            "description": "Error (consulta error.code)"
          }
        }
      }
    },
    "/api/v1/load-orders": {
      "get": {
        "operationId": "get_load_orders",
        "summary": "Lista las órdenes de carga: número, estado, formato (junta o separada), DeCA vinculado, matrículas p",
        "description": "Filtros: decaId, status (pending_vehicle | active | cancelled), limit (1–100). Sin deca:read_company, solo las de DeCA creados por tu integración. Nunca incluye el precio ni las condiciones (están en el PDF). Para emitirla: POST /api/v1/decas con loadOrder.\n\nPermiso necesario: deca:read.",
        "consumes": [
          "application/json"
        ],
        "produces": [
          "application/json"
        ],
        "parameters": [
          {
            "name": "decaId",
            "in": "query",
            "required": false,
            "description": "Solo la orden de este DeCA.",
            "type": "string"
          },
          {
            "name": "status",
            "in": "query",
            "required": false,
            "description": "Estado de la orden de carga.",
            "type": "string"
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "description": "Número máximo de órdenes (1–100, por defecto 50).",
            "type": "string"
          }
        ],
        "responses": {
          "200": {
            "description": "Correcto",
            "schema": {
              "$ref": "#/definitions/LoadOrderList"
            }
          },
          "default": {
            "description": "Error (consulta error.code)"
          }
        }
      }
    },
    "/api/v1/load-orders/{id}": {
      "get": {
        "operationId": "get_load_orders_id",
        "summary": "Una orden de carga: estado, versión vigente, DeCA vinculado y aceptación del transportista.",
        "description": "Permiso necesario: deca:read.",
        "consumes": [
          "application/json"
        ],
        "produces": [
          "application/json"
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Identificador de la orden de carga.",
            "type": "string"
          }
        ],
        "responses": {
          "200": {
            "description": "Correcto",
            "schema": {
              "$ref": "#/definitions/LoadOrder"
            }
          },
          "default": {
            "description": "Error (consulta error.code)"
          }
        }
      }
    },
    "/api/v1/load-orders/{id}/pdf": {
      "get": {
        "operationId": "get_load_orders_id_pdf",
        "summary": "El PDF de la versión vigente de la orden de carga.",
        "description": "Permiso necesario: deca:read.",
        "consumes": [
          "application/json"
        ],
        "produces": [
          "application/pdf"
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Identificador de la orden de carga.",
            "type": "string"
          }
        ],
        "responses": {
          "200": {
            "description": "Correcto"
          },
          "default": {
            "description": "Error (consulta error.code)"
          }
        }
      }
    },
    "/api/v1/saved/companies": {
      "get": {
        "operationId": "get_saved_companies",
        "summary": "Lista las empresas habituales (cargadores y transportistas) de la empresa.",
        "description": "Orden de creación. Parámetros: limit (1–100, por defecto 50), cursor (nextCursor de la página anterior) y externalId (búsqueda exacta). Incluye también los registros creados en la web (externalId null).\n\nPermiso necesario: saved:read.",
        "consumes": [
          "application/json"
        ],
        "produces": [
          "application/json"
        ],
        "parameters": [
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "description": "Elementos por página (por defecto 50).",
            "type": "string"
          },
          {
            "name": "cursor",
            "in": "query",
            "required": false,
            "description": "nextCursor de la página anterior.",
            "type": "string"
          },
          {
            "name": "externalId",
            "in": "query",
            "required": false,
            "description": "Búsqueda exacta por externalId.",
            "type": "string"
          }
        ],
        "responses": {
          "200": {
            "description": "Correcto",
            "schema": {
              "$ref": "#/definitions/SavedList"
            }
          },
          "default": {
            "description": "Error (consulta error.code)"
          }
        }
      }
    },
    "/api/v1/saved/companies/{externalId}": {
      "get": {
        "operationId": "get_saved_companies_externalId",
        "summary": "Lee un registro por el identificador de tu ERP o TMS (externalId).",
        "description": "Permiso necesario: saved:read.",
        "consumes": [
          "application/json"
        ],
        "produces": [
          "application/json"
        ],
        "parameters": [
          {
            "name": "externalId",
            "in": "path",
            "required": true,
            "description": "El identificador del registro en tu ERP o TMS.",
            "type": "string"
          }
        ],
        "responses": {
          "200": {
            "description": "Correcto",
            "schema": {
              "$ref": "#/definitions/SavedRecord"
            }
          },
          "default": {
            "description": "Error (consulta error.code)"
          }
        }
      },
      "put": {
        "operationId": "put_saved_companies_externalId",
        "summary": "Crea o actualiza el registro con ese externalId (sincronización sin duplicados).",
        "description": "Responde 201 si lo crea y 200 si lo actualiza; repetir la misma llamada deja el mismo registro. Se valida con las mismas reglas que la web y aparece al momento en Datos habituales. Nunca cambia un DeCA ya generado.\n\nPermiso necesario: saved:write.",
        "consumes": [
          "application/json"
        ],
        "produces": [
          "application/json"
        ],
        "parameters": [
          {
            "name": "externalId",
            "in": "path",
            "required": true,
            "description": "El identificador del registro en tu ERP o TMS.",
            "type": "string"
          },
          {
            "name": "body",
            "in": "body",
            "required": true,
            "schema": {
              "$ref": "#/definitions/SavedCompanyInput"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Correcto",
            "schema": {
              "$ref": "#/definitions/SavedRecord"
            }
          },
          "default": {
            "description": "Error (consulta error.code)"
          }
        }
      },
      "delete": {
        "operationId": "delete_saved_companies_externalId",
        "summary": "Borra el registro con ese externalId.",
        "description": "Nunca cambia un DeCA ya generado.\n\nPermiso necesario: saved:write.",
        "consumes": [
          "application/json"
        ],
        "produces": [
          "application/json"
        ],
        "parameters": [
          {
            "name": "externalId",
            "in": "path",
            "required": true,
            "description": "El identificador del registro en tu ERP o TMS.",
            "type": "string"
          }
        ],
        "responses": {
          "200": {
            "description": "Correcto",
            "schema": {
              "$ref": "#/definitions/SavedDeleted"
            }
          },
          "default": {
            "description": "Error (consulta error.code)"
          }
        }
      }
    },
    "/api/v1/saved/vehicles": {
      "get": {
        "operationId": "get_saved_vehicles",
        "summary": "Lista los vehículos habituales (con su conductor habitual) de la empresa.",
        "description": "Orden de creación. Parámetros: limit (1–100, por defecto 50), cursor (nextCursor de la página anterior) y externalId (búsqueda exacta). Incluye también los registros creados en la web (externalId null).\n\nPermiso necesario: saved:read.",
        "consumes": [
          "application/json"
        ],
        "produces": [
          "application/json"
        ],
        "parameters": [
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "description": "Elementos por página (por defecto 50).",
            "type": "string"
          },
          {
            "name": "cursor",
            "in": "query",
            "required": false,
            "description": "nextCursor de la página anterior.",
            "type": "string"
          },
          {
            "name": "externalId",
            "in": "query",
            "required": false,
            "description": "Búsqueda exacta por externalId.",
            "type": "string"
          }
        ],
        "responses": {
          "200": {
            "description": "Correcto",
            "schema": {
              "$ref": "#/definitions/SavedList"
            }
          },
          "default": {
            "description": "Error (consulta error.code)"
          }
        }
      }
    },
    "/api/v1/saved/vehicles/{externalId}": {
      "get": {
        "operationId": "get_saved_vehicles_externalId",
        "summary": "Lee un registro por el identificador de tu ERP o TMS (externalId).",
        "description": "Permiso necesario: saved:read.",
        "consumes": [
          "application/json"
        ],
        "produces": [
          "application/json"
        ],
        "parameters": [
          {
            "name": "externalId",
            "in": "path",
            "required": true,
            "description": "El identificador del registro en tu ERP o TMS.",
            "type": "string"
          }
        ],
        "responses": {
          "200": {
            "description": "Correcto",
            "schema": {
              "$ref": "#/definitions/SavedRecord"
            }
          },
          "default": {
            "description": "Error (consulta error.code)"
          }
        }
      },
      "put": {
        "operationId": "put_saved_vehicles_externalId",
        "summary": "Crea o actualiza el registro con ese externalId (sincronización sin duplicados).",
        "description": "Responde 201 si lo crea y 200 si lo actualiza; repetir la misma llamada deja el mismo registro. Se valida con las mismas reglas que la web y aparece al momento en Datos habituales. Nunca cambia un DeCA ya generado.\n\nPermiso necesario: saved:write.",
        "consumes": [
          "application/json"
        ],
        "produces": [
          "application/json"
        ],
        "parameters": [
          {
            "name": "externalId",
            "in": "path",
            "required": true,
            "description": "El identificador del registro en tu ERP o TMS.",
            "type": "string"
          },
          {
            "name": "body",
            "in": "body",
            "required": true,
            "schema": {
              "$ref": "#/definitions/SavedVehicleInput"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Correcto",
            "schema": {
              "$ref": "#/definitions/SavedRecord"
            }
          },
          "default": {
            "description": "Error (consulta error.code)"
          }
        }
      },
      "delete": {
        "operationId": "delete_saved_vehicles_externalId",
        "summary": "Borra el registro con ese externalId.",
        "description": "Nunca cambia un DeCA ya generado.\n\nPermiso necesario: saved:write.",
        "consumes": [
          "application/json"
        ],
        "produces": [
          "application/json"
        ],
        "parameters": [
          {
            "name": "externalId",
            "in": "path",
            "required": true,
            "description": "El identificador del registro en tu ERP o TMS.",
            "type": "string"
          }
        ],
        "responses": {
          "200": {
            "description": "Correcto",
            "schema": {
              "$ref": "#/definitions/SavedDeleted"
            }
          },
          "default": {
            "description": "Error (consulta error.code)"
          }
        }
      }
    },
    "/api/v1/saved/places": {
      "get": {
        "operationId": "get_saved_places",
        "summary": "Lista los lugares habituales de carga y descarga de la empresa.",
        "description": "Orden de creación. Parámetros: limit (1–100, por defecto 50), cursor (nextCursor de la página anterior) y externalId (búsqueda exacta). Incluye también los registros creados en la web (externalId null).\n\nPermiso necesario: saved:read.",
        "consumes": [
          "application/json"
        ],
        "produces": [
          "application/json"
        ],
        "parameters": [
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "description": "Elementos por página (por defecto 50).",
            "type": "string"
          },
          {
            "name": "cursor",
            "in": "query",
            "required": false,
            "description": "nextCursor de la página anterior.",
            "type": "string"
          },
          {
            "name": "externalId",
            "in": "query",
            "required": false,
            "description": "Búsqueda exacta por externalId.",
            "type": "string"
          }
        ],
        "responses": {
          "200": {
            "description": "Correcto",
            "schema": {
              "$ref": "#/definitions/SavedList"
            }
          },
          "default": {
            "description": "Error (consulta error.code)"
          }
        }
      }
    },
    "/api/v1/saved/places/{externalId}": {
      "get": {
        "operationId": "get_saved_places_externalId",
        "summary": "Lee un registro por el identificador de tu ERP o TMS (externalId).",
        "description": "Permiso necesario: saved:read.",
        "consumes": [
          "application/json"
        ],
        "produces": [
          "application/json"
        ],
        "parameters": [
          {
            "name": "externalId",
            "in": "path",
            "required": true,
            "description": "El identificador del registro en tu ERP o TMS.",
            "type": "string"
          }
        ],
        "responses": {
          "200": {
            "description": "Correcto",
            "schema": {
              "$ref": "#/definitions/SavedRecord"
            }
          },
          "default": {
            "description": "Error (consulta error.code)"
          }
        }
      },
      "put": {
        "operationId": "put_saved_places_externalId",
        "summary": "Crea o actualiza el registro con ese externalId (sincronización sin duplicados).",
        "description": "Responde 201 si lo crea y 200 si lo actualiza; repetir la misma llamada deja el mismo registro. Se valida con las mismas reglas que la web y aparece al momento en Datos habituales. Nunca cambia un DeCA ya generado.\n\nPermiso necesario: saved:write.",
        "consumes": [
          "application/json"
        ],
        "produces": [
          "application/json"
        ],
        "parameters": [
          {
            "name": "externalId",
            "in": "path",
            "required": true,
            "description": "El identificador del registro en tu ERP o TMS.",
            "type": "string"
          },
          {
            "name": "body",
            "in": "body",
            "required": true,
            "schema": {
              "$ref": "#/definitions/SavedPlaceInput"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Correcto",
            "schema": {
              "$ref": "#/definitions/SavedRecord"
            }
          },
          "default": {
            "description": "Error (consulta error.code)"
          }
        }
      },
      "delete": {
        "operationId": "delete_saved_places_externalId",
        "summary": "Borra el registro con ese externalId.",
        "description": "Responde 409 saved_in_use si un envío habitual usa ese lugar.\n\nPermiso necesario: saved:write.",
        "consumes": [
          "application/json"
        ],
        "produces": [
          "application/json"
        ],
        "parameters": [
          {
            "name": "externalId",
            "in": "path",
            "required": true,
            "description": "El identificador del registro en tu ERP o TMS.",
            "type": "string"
          }
        ],
        "responses": {
          "200": {
            "description": "Correcto",
            "schema": {
              "$ref": "#/definitions/SavedDeleted"
            }
          },
          "default": {
            "description": "Error (consulta error.code)"
          }
        }
      }
    },
    "/api/v1/saved/shipments": {
      "get": {
        "operationId": "get_saved_shipments",
        "summary": "Lista los envíos habituales (lugar de carga + lugar de descarga + mercancía) de la empresa.",
        "description": "Orden de creación. Parámetros: limit (1–100, por defecto 50), cursor (nextCursor de la página anterior) y externalId (búsqueda exacta). Incluye también los registros creados en la web (externalId null).\n\nPermiso necesario: saved:read.",
        "consumes": [
          "application/json"
        ],
        "produces": [
          "application/json"
        ],
        "parameters": [
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "description": "Elementos por página (por defecto 50).",
            "type": "string"
          },
          {
            "name": "cursor",
            "in": "query",
            "required": false,
            "description": "nextCursor de la página anterior.",
            "type": "string"
          },
          {
            "name": "externalId",
            "in": "query",
            "required": false,
            "description": "Búsqueda exacta por externalId.",
            "type": "string"
          }
        ],
        "responses": {
          "200": {
            "description": "Correcto",
            "schema": {
              "$ref": "#/definitions/SavedList"
            }
          },
          "default": {
            "description": "Error (consulta error.code)"
          }
        }
      }
    },
    "/api/v1/saved/shipments/{externalId}": {
      "get": {
        "operationId": "get_saved_shipments_externalId",
        "summary": "Lee un registro por el identificador de tu ERP o TMS (externalId).",
        "description": "Permiso necesario: saved:read.",
        "consumes": [
          "application/json"
        ],
        "produces": [
          "application/json"
        ],
        "parameters": [
          {
            "name": "externalId",
            "in": "path",
            "required": true,
            "description": "El identificador del registro en tu ERP o TMS.",
            "type": "string"
          }
        ],
        "responses": {
          "200": {
            "description": "Correcto",
            "schema": {
              "$ref": "#/definitions/SavedRecord"
            }
          },
          "default": {
            "description": "Error (consulta error.code)"
          }
        }
      },
      "put": {
        "operationId": "put_saved_shipments_externalId",
        "summary": "Crea o actualiza el registro con ese externalId (sincronización sin duplicados).",
        "description": "Responde 201 si lo crea y 200 si lo actualiza; repetir la misma llamada deja el mismo registro. Se valida con las mismas reglas que la web y aparece al momento en Datos habituales. Los lugares se indican con su externalId (loadPlaceExternalId, unloadPlaceExternalId): sincronízalos antes. Nunca cambia un DeCA ya generado.\n\nPermiso necesario: saved:write.",
        "consumes": [
          "application/json"
        ],
        "produces": [
          "application/json"
        ],
        "parameters": [
          {
            "name": "externalId",
            "in": "path",
            "required": true,
            "description": "El identificador del registro en tu ERP o TMS.",
            "type": "string"
          },
          {
            "name": "body",
            "in": "body",
            "required": true,
            "schema": {
              "$ref": "#/definitions/SavedShipmentInput"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Correcto",
            "schema": {
              "$ref": "#/definitions/SavedRecord"
            }
          },
          "default": {
            "description": "Error (consulta error.code)"
          }
        }
      },
      "delete": {
        "operationId": "delete_saved_shipments_externalId",
        "summary": "Borra el registro con ese externalId.",
        "description": "Nunca cambia un DeCA ya generado.\n\nPermiso necesario: saved:write.",
        "consumes": [
          "application/json"
        ],
        "produces": [
          "application/json"
        ],
        "parameters": [
          {
            "name": "externalId",
            "in": "path",
            "required": true,
            "description": "El identificador del registro en tu ERP o TMS.",
            "type": "string"
          }
        ],
        "responses": {
          "200": {
            "description": "Correcto",
            "schema": {
              "$ref": "#/definitions/SavedDeleted"
            }
          },
          "default": {
            "description": "Error (consulta error.code)"
          }
        }
      }
    }
  }
}