{
  "openapi": "3.1.1",
  "info": {
    "title": "API DeCA",
    "version": "1.0.0",
    "description": "API para emitir, rectificar, consultar y descargar documentos españoles de control administrativo de transporte de mercancías. Para obtener un token, contacta con soporte de SERTEC PESAGE. Los tokens sk_test_ utilizan desarrollo y los tokens sk_live_ producción, con datos separados. La restricción por IP es opcional.\n\nCada credencial permite consultar y descargar sus documentos durante las 24 horas siguientes a su creación. La descarga por QR tiene una vigencia independiente. Las rectificaciones conservan el documento original.\n\nEl borrado lógico solo se permite durante las primeras 2 horas para errores de generación. La integración debe confirmar que el documento no se ha entregado al transportista ni utilizado en el transporte. Conserva el archivo y la trazabilidad, pero bloquea su acceso.\n\nPuedes solicitar un token `sk_demo_` sin una cuenta ni un alta de organización. Dura **24 horas desde su emisión** y solo funciona desde la **IP pública que lo solicitó**. Cambiar de red o de IP requiere solicitar otro token. Utiliza únicamente **datos ficticios**. Cada página del PDF muestra **SIN VALIDEZ** junto al logo y una marca diagonal **DEMO**. Estos documentos no sirven para transportar mercancías ni como documentos administrativos válidos. La demo añade demo: true, expires_at, qr_url (alias de url) y pdf_url (alias de pdf_download_url). document_locale es el idioma elegido al solicitar el token (es por defecto). Los datos y PDF temporales quedan separados del archivo real y no se cifran como los documentos reales. La demo admite cuerpos de hasta 32 KiB y diez documentos por sesión. La emisión de tokens admite tres por minuto y diez por hora desde una misma IP; la creación, cinco por minuto y veinte por hora desde una misma IP. Los límites compartidos por red, región y carga global pueden aplicarse antes. Estas cifras corresponden a la configuración predeterminada."
  },
  "servers": [
    {
      "url": "https://deca.sertecpesage.com",
      "description": "Producción"
    }
  ],
  "security": [
    {
      "bearerAuth": []
    }
  ],
  "tags": [
    {
      "name": "Documentos",
      "description": "Emisión, rectificación y acceso a documentos propios recientes."
    },
    {
      "name": "Descarga por QR",
      "description": "Descarga directa del PDF mediante el secreto de un documento."
    },
    {
      "name": "Demostración de la API",
      "description": "Consigue un token público temporal y genera documentos de demostración con datos ficticios."
    }
  ],
  "paths": {
    "/api/v1/documents": {
      "post": {
        "operationId": "createDocument",
        "tags": [
          "Documentos"
        ],
        "summary": "Crear documento",
        "description": "Envía los datos completos del transporte. La respuesta se entrega después de generar y almacenar el PDF cifrado.\n\nUna referencia no nula es única por organización y entorno. Repetir la referencia con los mismos datos normalizados y metadata devuelve el resultado original dentro de 24 horas (200), sin emitir otro PDF. Una creación nueva devuelve 201. Datos distintos o una referencia de otra credencial producen 409 sin divulgar documentos. Sin referencia no se garantiza deduplicación.\n\nUna referencia cuyo resultado ha vencido devuelve 409 DOCUMENT_WINDOW_EXPIRED. Si el documento está eliminado, no se devuelve su contenido ni se reutiliza su referencia; el reintento devuelve 404. Para rectificar utiliza los endpoints PUT.\n\n\nPuedes solicitar un token `sk_demo_` sin una cuenta ni un alta de organización. Dura **24 horas desde su emisión** y solo funciona desde la **IP pública que lo solicitó**. Cambiar de red o de IP requiere solicitar otro token. La demo añade demo: true, expires_at, qr_url (alias de url) y pdf_url (alias de pdf_download_url). document_locale es el idioma elegido al solicitar el token (es por defecto). Los datos y PDF temporales quedan separados del archivo real y no se cifran como los documentos reales.",
        "requestBody": {
          "required": true,
          "description": "Datos completos del documento que se desea crear.",
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateDocumentRequest"
              },
              "examples": {
                "create": {
                  "summary": "Emisión con peso y remolque",
                  "value": {
                    "reference": "EXP-2026-004587",
                    "transport_date": "2026-10-05",
                    "shipper": {
                      "name": "Distribuciones Demo, S. L.",
                      "tax_country": "ES",
                      "tax_id": "B12345674",
                      "address": {
                        "street": "Carrer de la Logística, 18",
                        "postal_code": "08040",
                        "city": "Barcelona",
                        "country": "ES"
                      }
                    },
                    "carrier": {
                      "name": "Transportes Demo, S. L.",
                      "tax_country": "ES",
                      "tax_id": "B87654323"
                    },
                    "origin": {
                      "place": "Almacén Barcelona",
                      "city": "Barcelona",
                      "country": "ES"
                    },
                    "destination": {
                      "place": "Almacén Zaragoza",
                      "city": "Zaragoza",
                      "country": "ES"
                    },
                    "goods": {
                      "description": "Vino embotellado",
                      "weight_kg": "1850.000"
                    },
                    "vehicle": {
                      "country": "ES",
                      "registration": "4827 MNL",
                      "trailer_country": "ES",
                      "trailer_registration": "R 5832 BCD"
                    },
                    "special_authorization": null,
                    "observations": null,
                    "metadata": {
                      "order_id": "4587",
                      "terminal": "POS-02"
                    }
                  }
                },
                "alternativeMeasure": {
                  "summary": "Magnitud alternativa para peso de difícil determinación",
                  "value": {
                    "transport_date": "2026-10-05",
                    "shipper": {
                      "name": "Distribuciones Demo, S. L.",
                      "tax_country": "ES",
                      "tax_id": "B12345674",
                      "address": {
                        "street": "Carrer de la Logística, 18",
                        "postal_code": "08040",
                        "city": "Barcelona",
                        "country": "ES"
                      }
                    },
                    "carrier": {
                      "name": "Transportes Demo, S. L.",
                      "tax_country": "ES",
                      "tax_id": "B87654323"
                    },
                    "origin": {
                      "place": "Almacén Barcelona",
                      "city": "Barcelona",
                      "country": "ES"
                    },
                    "destination": {
                      "place": "Almacén Zaragoza",
                      "city": "Zaragoza",
                      "country": "ES"
                    },
                    "goods": {
                      "description": "Carga cuyo peso es de difícil determinación.",
                      "alternative_measure": {
                        "value": "12",
                        "unit": "palés"
                      }
                    },
                    "vehicle": {
                      "country": "ES",
                      "registration": "4827 MNL"
                    }
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Reintento idempotente; devuelve el resultado original sin generar otro PDF.",
            "headers": {
              "Cache-Control": {
                "$ref": "#/components/headers/NoStore"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "oneOf": [
                    {
                      "$ref": "#/components/schemas/DocumentResult"
                    },
                    {
                      "$ref": "#/components/schemas/DemoDocumentResult"
                    }
                  ]
                },
                "examples": {
                  "original": {
                    "$ref": "#/components/examples/DocumentResult"
                  }
                }
              }
            }
          },
          "201": {
            "description": "Documento nuevo generado y almacenado.",
            "headers": {
              "Cache-Control": {
                "$ref": "#/components/headers/NoStore"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "oneOf": [
                    {
                      "$ref": "#/components/schemas/DocumentResult"
                    },
                    {
                      "$ref": "#/components/schemas/DemoDocumentResult"
                    }
                  ]
                },
                "examples": {
                  "created": {
                    "$ref": "#/components/examples/DocumentResult"
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "description": "Referencia en conflicto o resultado del reintento vencido.",
            "headers": {
              "Cache-Control": {
                "$ref": "#/components/headers/NoStore"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "examples": {
                  "referenceConflict": {
                    "value": {
                      "errors": [
                        {
                          "field": "reference",
                          "code": "REFERENCE_CONFLICT",
                          "message": "Reference conflicts with an existing request."
                        }
                      ]
                    }
                  },
                  "expiredReplay": {
                    "value": {
                      "errors": [
                        {
                          "field": "reference",
                          "code": "DOCUMENT_WINDOW_EXPIRED",
                          "message": "Original result is no longer available through the API."
                        }
                      ]
                    }
                  }
                }
              }
            }
          },
          "413": {
            "$ref": "#/components/responses/RequestTooLarge"
          },
          "415": {
            "$ref": "#/components/responses/UnsupportedMediaType"
          },
          "422": {
            "$ref": "#/components/responses/ValidationError"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          },
          "503": {
            "$ref": "#/components/responses/ServiceUnavailable"
          }
        },
        "x-deca-order": 10
      },
      "get": {
        "operationId": "listDocuments",
        "tags": [
          "Documentos"
        ],
        "summary": "Listar últimos documentos",
        "description": "Devuelve resúmenes, sin PDF, secreto QR ni metadata. El servidor aplica siempre la ventana de 24 horas y el aislamiento de la credencial. Orden descendente por fecha de creación y UUID como desempate, paginación por página y filtros inclusivos de fecha de transporte. La ventana se evalúa en cada petición; no constituye una instantánea estable entre páginas. No permite consultar el histórico.\n\nLos documentos eliminados no aparecen en el listado.",
        "parameters": [
          {
            "name": "page",
            "in": "query",
            "description": "Página solicitada, a partir de 1.",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "default": 1
            }
          },
          {
            "name": "limit",
            "in": "query",
            "description": "Documentos por página, entre 1 y 100.",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 25
            }
          },
          {
            "name": "from",
            "in": "query",
            "description": "Fecha de transporte mínima, inclusive. No amplía la ventana de lectura.",
            "schema": {
              "type": "string",
              "format": "date"
            },
            "examples": {
              "default": {
                "value": "2026-10-05"
              }
            }
          },
          {
            "name": "to",
            "in": "query",
            "description": "Fecha de transporte máxima, inclusive; igual o posterior a from.",
            "schema": {
              "type": "string",
              "format": "date"
            },
            "examples": {
              "default": {
                "value": "2026-10-05"
              }
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Página de documentos recientes. Una página sin resultados tiene data vacío.",
            "headers": {
              "Cache-Control": {
                "$ref": "#/components/headers/NoStore"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DocumentList"
                },
                "examples": {
                  "default": {
                    "value": {
                      "data": [
                        {
                          "uuid": "75b10a04-1a2d-4f9e-9277-5f3c270f476e",
                          "reference": "EXP-2026-004587",
                          "created_at": "2026-10-05T14:43:00Z",
                          "transport_date": "2026-10-05",
                          "rectifies_uuid": null
                        }
                      ],
                      "meta": {
                        "current_page": 1,
                        "limit": 25,
                        "total": 1,
                        "last_page": 1
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "422": {
            "$ref": "#/components/responses/ValidationError"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        },
        "x-deca-order": 50
      }
    },
    "/api/v1/documents/{uuid}": {
      "parameters": [
        {
          "$ref": "#/components/parameters/DocumentUuidWithReferenceAlternative"
        }
      ],
      "put": {
        "operationId": "rectifyDocument",
        "tags": [
          "Documentos"
        ],
        "summary": "Rectificar documento",
        "x-deca-order": 20,
        "description": "Identifica el documento anterior mediante su UUID y envía todos los datos corregidos, sin parches. Solo puede rectificarse la última versión de la misma credencial. Se permite rectificar un documento de más de 24 horas sin devolver su contenido anterior. Un documento eliminado devuelve 404 y no puede rectificarse.\n\nSe genera un documento rectificativo con UUID, PDF y QR nuevos; el original y su QR se conservan. Entrega el nuevo PDF al conductor. El campo reference del cuerpo corresponde a la nueva versión, no al documento de la URL.\n\nRepetir el mismo PUT con los mismos datos normalizados y metadata devuelve la rectificación ya emitida durante las 24 horas siguientes a su creación, sin generar otra versión. Esta comprobación se realiza antes de rechazar un documento ya rectificado y funciona también si el reintento identifica al mismo documento por referencia. Un cambio de datos sobre un documento ya rectificado devuelve 409. Un reintento cuyo resultado ha vencido devuelve 409 DOCUMENT_WINDOW_EXPIRED. Si la rectificación devuelta está eliminada, el reintento devuelve 404. Para otra rectificación, utiliza el UUID o la referencia de la última versión.\n",
        "requestBody": {
          "required": true,
          "description": "Datos completos corregidos del transporte.",
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/RectifyDocumentRequest"
              },
              "examples": {
                "rectify": {
                  "summary": "Rectificación completa con referencia nueva",
                  "value": {
                    "reference": "EXP-2026-004587-R1",
                    "transport_date": "2026-10-05",
                    "shipper": {
                      "name": "Distribuciones Demo, S. L.",
                      "tax_country": "ES",
                      "tax_id": "B12345674",
                      "address": {
                        "street": "Carrer de la Logística, 18",
                        "postal_code": "08040",
                        "city": "Barcelona",
                        "country": "ES"
                      }
                    },
                    "carrier": {
                      "name": "Transportes Demo, S. L.",
                      "tax_country": "ES",
                      "tax_id": "B87654323"
                    },
                    "origin": {
                      "place": "Almacén Barcelona",
                      "city": "Barcelona",
                      "country": "ES"
                    },
                    "destination": {
                      "place": "Almacén Zaragoza",
                      "city": "Zaragoza",
                      "country": "ES"
                    },
                    "goods": {
                      "description": "Vino embotellado",
                      "weight_kg": "1900.000"
                    },
                    "vehicle": {
                      "country": "ES",
                      "registration": "4827 MNL",
                      "trailer_country": "ES",
                      "trailer_registration": "R 5832 BCD"
                    },
                    "special_authorization": null,
                    "observations": "Corrección del peso de la mercancía.",
                    "metadata": {
                      "order_id": "4587",
                      "terminal": "POS-02"
                    }
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Documento rectificativo emitido, o resultado de un reintento idéntico sin crear otra versión.",
            "headers": {
              "Cache-Control": {
                "$ref": "#/components/headers/NoStore"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "oneOf": [
                    {
                      "$ref": "#/components/schemas/DocumentResult"
                    },
                    {
                      "$ref": "#/components/schemas/DemoDocumentResult"
                    }
                  ]
                },
                "examples": {
                  "rectified": {
                    "$ref": "#/components/examples/RectifiedDocumentResult"
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "description": "Documento ya rectificado, referencia nueva en conflicto o resultado del reintento vencido.",
            "headers": {
              "Cache-Control": {
                "$ref": "#/components/headers/NoStore"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "examples": {
                  "alreadyRectified": {
                    "value": {
                      "errors": [
                        {
                          "field": null,
                          "code": "DOCUMENT_ALREADY_RECTIFIED",
                          "message": "Document has already been rectified."
                        }
                      ]
                    }
                  },
                  "referenceConflict": {
                    "value": {
                      "errors": [
                        {
                          "field": "reference",
                          "code": "REFERENCE_CONFLICT",
                          "message": "Reference conflicts with an existing request."
                        }
                      ]
                    }
                  },
                  "expiredReplay": {
                    "value": {
                      "errors": [
                        {
                          "field": null,
                          "code": "DOCUMENT_WINDOW_EXPIRED",
                          "message": "Original result is no longer available through the API."
                        }
                      ]
                    }
                  }
                }
              }
            }
          },
          "413": {
            "$ref": "#/components/responses/RequestTooLarge"
          },
          "415": {
            "$ref": "#/components/responses/UnsupportedMediaType"
          },
          "422": {
            "$ref": "#/components/responses/ValidationError"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          },
          "503": {
            "$ref": "#/components/responses/ServiceUnavailable"
          }
        },
        "x-deca-alternative-path": "/api/v1/documents/ref/{reference}"
      },
      "delete": {
        "operationId": "deleteDocument",
        "tags": [
          "Documentos"
        ],
        "summary": "Eliminar documento",
        "x-deca-order": 40,
        "description": "Borrado lógico para corregir errores detectados al generar un documento, exclusivamente **antes de entregarlo al transportista o al conductor y antes de utilizarlo en un transporte**. La integración debe confirmar ambas condiciones; no basta con estar dentro del plazo. Si el documento ya se ha entregado o utilizado, debe rectificarse mediante PUT.\n\nSe permite únicamente mientras la hora del servidor sea anterior a created_at + **2 horas**; a las 2 horas exactas ya no se permite. Ni una consulta ni un reintento reinician el plazo. Cada versión utiliza su propia fecha de creación. Solo puede eliminarse la última versión de la credencial autenticada.\n\nSe conservan el PDF, los datos y la trazabilidad, pero el documento deja de aparecer en el listado. Las consultas por UUID o referencia, la descarga autenticada, el QR y los reintentos que devolverían su contenido responden 404. La consulta de estado responde únicamente con status: deleted durante la ventana de consulta de 24 horas. La referencia sigue reservada. No hay endpoint de reactivación; el borrado no destruye el archivo para permitir una recuperación futura.\n\nRepetir el DELETE dentro de las mismas 2 horas devuelve 204 sin cambiar la fecha de eliminación. Pasado ese plazo devuelve 409.\n\n\nEl primer acceso correcto al enlace QR bloquea también el borrado con 409 DOCUMENT_ALREADY_ACCESSED. Ese acceso no prueba por sí solo la realización del transporte.",
        "requestBody": {
          "required": true,
          "description": "Motivo y confirmación de que el documento todavía no se ha entregado ni utilizado.",
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/DeleteDocumentRequest"
              },
              "examples": {
                "generationError": {
                  "value": {
                    "reason": "Peso incorrecto detectado antes de enviar el documento al transportista.",
                    "confirm_not_delivered": true,
                    "confirm_not_used": true
                  }
                }
              }
            }
          }
        },
        "responses": {
          "204": {
            "description": "Documento marcado como eliminado. Sin cuerpo de respuesta.",
            "headers": {
              "Cache-Control": {
                "$ref": "#/components/headers/NoStore"
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "description": "Plazo de 2 horas vencido, documento entregado o utilizado, o versión ya rectificada. El primer acceso correcto al enlace QR bloquea también el borrado con 409 DOCUMENT_ALREADY_ACCESSED. Ese acceso no prueba por sí solo la realización del transporte.",
            "headers": {
              "Cache-Control": {
                "$ref": "#/components/headers/NoStore"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "examples": {
                  "expired": {
                    "value": {
                      "errors": [
                        {
                          "field": null,
                          "code": "DELETE_WINDOW_EXPIRED",
                          "message": "The two-hour deletion window has expired."
                        }
                      ]
                    }
                  },
                  "delivered": {
                    "value": {
                      "errors": [
                        {
                          "field": null,
                          "code": "DOCUMENT_ALREADY_DELIVERED",
                          "message": "A delivered document cannot be deleted."
                        }
                      ]
                    }
                  },
                  "used": {
                    "value": {
                      "errors": [
                        {
                          "field": null,
                          "code": "DOCUMENT_ALREADY_USED",
                          "message": "A document used in transport cannot be deleted."
                        }
                      ]
                    }
                  },
                  "rectified": {
                    "value": {
                      "errors": [
                        {
                          "field": null,
                          "code": "DOCUMENT_ALREADY_RECTIFIED",
                          "message": "Document has already been rectified."
                        }
                      ]
                    }
                  },
                  "accessed": {
                    "value": {
                      "errors": [
                        {
                          "field": null,
                          "code": "DOCUMENT_ALREADY_ACCESSED",
                          "message": "A document accessed through its QR link cannot be deleted."
                        }
                      ]
                    }
                  }
                }
              }
            }
          },
          "413": {
            "$ref": "#/components/responses/RequestTooLarge"
          },
          "415": {
            "$ref": "#/components/responses/UnsupportedMediaType"
          },
          "422": {
            "$ref": "#/components/responses/ValidationError"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        },
        "x-deca-alternative-path": "/api/v1/documents/ref/{reference}"
      },
      "get": {
        "operationId": "getDocument",
        "tags": [
          "Documentos"
        ],
        "summary": "Consultar documento",
        "description": "Recupera el resultado original dentro de 24 horas, solo para la credencial que emitió el documento. Incluye todos los campos aceptados y normalizados y la metadata sin modificación semántica.\n\nUn UUID o una referencia inexistente, de otra credencial o fuera de la ventana devuelve la misma respuesta `404`, sin confirmar existencia ni propietario. Cada referencia identifica su propia versión y no redirige a una rectificación posterior.\n\nUn documento eliminado devuelve 404. Para saber si está eliminado, utiliza la consulta de estado.",
        "responses": {
          "200": {
            "description": "Resultado original del documento.",
            "headers": {
              "Cache-Control": {
                "$ref": "#/components/headers/NoStore"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "oneOf": [
                    {
                      "$ref": "#/components/schemas/DocumentResult"
                    },
                    {
                      "$ref": "#/components/schemas/DemoDocumentResult"
                    }
                  ]
                },
                "examples": {
                  "document": {
                    "$ref": "#/components/examples/DocumentResult"
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          },
          "503": {
            "$ref": "#/components/responses/ServiceUnavailable"
          }
        },
        "x-deca-order": 60,
        "x-deca-alternative-path": "/api/v1/documents/ref/{reference}"
      }
    },
    "/api/v1/documents/{uuid}/status": {
      "parameters": [
        {
          "$ref": "#/components/parameters/DocumentStatusUuidWithReferenceAlternative"
        }
      ],
      "get": {
        "operationId": "getDocumentStatus",
        "tags": [
          "Documentos"
        ],
        "summary": "Consultar estado",
        "x-deca-order": 80,
        "description": "Devuelve únicamente el estado de un documento de la credencial autenticada, durante las 24 horas siguientes a su creación. Para un documento eliminado responde 200 con {\"status\":\"deleted\"}; no devuelve datos del transporte, PDF, enlace QR ni metadata. Un documento activo responde con {\"status\":\"active\"}.\n\nUn UUID inexistente, de otra credencial o fuera de las 24 horas devuelve el mismo 404. La eliminación no amplía esa ventana.\n",
        "responses": {
          "200": {
            "description": "Estado del documento sin datos de su contenido.",
            "headers": {
              "Cache-Control": {
                "$ref": "#/components/headers/NoStore"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DocumentStatus"
                },
                "examples": {
                  "deleted": {
                    "summary": "Documento eliminado",
                    "value": {
                      "status": "deleted"
                    }
                  },
                  "active": {
                    "summary": "Documento activo",
                    "value": {
                      "status": "active"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        },
        "x-deca-alternative-path": "/api/v1/documents/ref/{reference}/status"
      }
    },
    "/api/v1/documents/{uuid}/pdf": {
      "parameters": [
        {
          "name": "uuid",
          "in": "path",
          "required": true,
          "description": "UUID público del documento. Alternativamente, utiliza `GET /api/v1/documents/ref/{reference}/pdf`. Ejemplo: `/api/v1/documents/ref/EXP-2026-004587/pdf`.",
          "schema": {
            "type": "string",
            "format": "uuid"
          },
          "examples": {
            "default": {
              "value": "75b10a04-1a2d-4f9e-9277-5f3c270f476e"
            }
          }
        }
      ],
      "get": {
        "operationId": "downloadDocumentPdf",
        "tags": [
          "Documentos"
        ],
        "summary": "Descargar PDF",
        "description": "Devuelve exactamente los bytes del PDF almacenado, sin regenerarlo.\nExige credencial propietaria y una antigüedad inferior a 24 horas.\nEsta limitación no altera la conservación legal ni la vigencia del QR.\n\nUn documento eliminado devuelve 404 sin entregar el PDF.\n",
        "responses": {
          "200": {
            "$ref": "#/components/responses/PdfDownload"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        },
        "x-deca-order": 90,
        "x-deca-alternative-path": "/api/v1/documents/ref/{reference}/pdf"
      }
    },
    "/d/{uuid}": {
      "parameters": [
        {
          "$ref": "#/components/parameters/DocumentUuid"
        }
      ],
      "get": {
        "operationId": "downloadDocumentByQr",
        "tags": [
          "Descarga por QR"
        ],
        "summary": "Descargar PDF por QR",
        "description": "Enlace incluido en el QR. El secreto k autoriza únicamente este documento. No requiere Bearer, sesión, CAPTCHA ni página intermedia. Quien conoce el enlace puede descargarlo. No registrar la URL completa en logs, analítica ni trazas.\n\nLa descarga no vence a las 24 horas ni se revoca automáticamente al rotar o revocar el Bearer. El QR anterior conserva su PDF después de una rectificación; no redirige a la nueva versión.\n\nEl PDF se entrega con `Content-Disposition: inline` para abrirlo en el visor del navegador.\n\nUn documento eliminado devuelve 404, aunque el secreto sea correcto.",
        "security": [
          {
            "qrSecret": []
          }
        ],
        "responses": {
          "200": {
            "$ref": "#/components/responses/PdfPreview"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        },
        "x-deca-order": 100
      }
    },
    "/api/v1/demo/tokens": {
      "post": {
        "operationId": "createDemoToken",
        "tags": [
          "Demostración de la API"
        ],
        "summary": "Solicitar token demo",
        "description": "Envía un objeto JSON vacío a este endpoint público. No necesita una cabecera Bearer. La respuesta `201` contiene `token` y `expires_at`; la fecha de caducidad se expresa en UTC.\n\nPuedes solicitar un token `sk_demo_` sin una cuenta ni un alta de organización. Dura **24 horas desde su emisión** y solo funciona desde la **IP pública que lo solicitó**. Cambiar de red o de IP requiere solicitar otro token.\n\nUtiliza únicamente **datos ficticios**. Cada página del PDF muestra **SIN VALIDEZ** junto al logo y una marca diagonal **DEMO**. Estos documentos no sirven para transportar mercancías ni como documentos administrativos válidos.\n\nLa demo admite cuerpos de hasta 32 KiB y diez documentos por sesión. La emisión de tokens admite tres por minuto y diez por hora desde una misma IP; la creación, cinco por minuto y veinte por hora desde una misma IP. Los límites compartidos por red, región y carga global pueden aplicarse antes. Estas cifras corresponden a la configuración predeterminada. Puedes enviar document_locale para elegir el idioma de todos los PDF del token: es, ca, gl, ca-valencia, eu, en, pt-pt o fr. Si se omite, se utiliza es. La respuesta también incluye document_locale; cambiarlo requiere un nuevo token.",
        "security": [],
        "requestBody": {
          "required": true,
          "description": "Envía un objeto JSON vacío a este endpoint público. No necesita una cabecera Bearer. La respuesta `201` contiene `token` y `expires_at`; la fecha de caducidad se expresa en UTC. Puedes enviar document_locale para elegir el idioma de todos los PDF del token: es, ca, gl, ca-valencia, eu, en, pt-pt o fr. Si se omite, se utiliza es. La respuesta también incluye document_locale; cambiarlo requiere un nuevo token.",
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "additionalProperties": false,
                "properties": {
                  "document_locale": {
                    "type": "string",
                    "enum": [
                      "es",
                      "ca",
                      "gl",
                      "ca-valencia",
                      "eu",
                      "en",
                      "pt-pt",
                      "fr"
                    ],
                    "default": "es",
                    "description": "Idioma de los PDF de este token. Español por defecto."
                  }
                }
              },
              "examples": {
                "default": {
                  "value": {}
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Token demo emitido; válido durante 24 horas y vinculado a la IP solicitante.",
            "headers": {
              "Cache-Control": {
                "$ref": "#/components/headers/NoStore"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DemoTokenResult"
                },
                "examples": {
                  "default": {
                    "value": {
                      "token": "sk_demo_0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef",
                      "expires_at": "2026-10-07T11:00:00Z",
                      "document_locale": "es"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "413": {
            "$ref": "#/components/responses/RequestTooLarge"
          },
          "415": {
            "$ref": "#/components/responses/UnsupportedMediaType"
          },
          "422": {
            "$ref": "#/components/responses/ValidationError"
          },
          "429": {
            "$ref": "#/components/responses/DemoRateLimited"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          },
          "503": {
            "$ref": "#/components/responses/DemoUnavailable"
          }
        },
        "x-deca-order": 5
      }
    },
    "/demo/d/{uuid}": {
      "parameters": [
        {
          "$ref": "#/components/parameters/DocumentUuid"
        }
      ],
      "get": {
        "operationId": "downloadDemoDocumentByQr",
        "tags": [
          "Demostración de la API"
        ],
        "summary": "Abrir PDF demo por QR",
        "description": "El QR abre el PDF directamente en el navegador mediante `/demo/d/{uuid}?k=...`, sin Bearer. Tanto el documento como sus enlaces caducan con la sesión demo; crear o rectificar un documento no amplía la duración del token.\n\nUtiliza únicamente **datos ficticios**. Cada página del PDF muestra **SIN VALIDEZ** junto al logo y una marca diagonal **DEMO**. Estos documentos no sirven para transportar mercancías ni como documentos administrativos válidos.",
        "security": [
          {
            "qrSecret": []
          }
        ],
        "responses": {
          "200": {
            "$ref": "#/components/responses/PdfPreview"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/DemoRateLimited"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          },
          "503": {
            "$ref": "#/components/responses/DemoUnavailable"
          }
        },
        "x-deca-order": 110
      }
    }
  },
  "components": {
    "securitySchemes": {
      "bearerAuth": {
        "type": "http",
        "scheme": "bearer",
        "description": "Token de integración facilitado por soporte de SERTEC PESAGE. Enviar en Authorization: Bearer, exclusivamente mediante HTTPS. sk_test_ selecciona desarrollo y sk_live_ producción. Si el token tiene una lista de IP autorizadas, la petición debe proceder de una de ellas; una lista vacía permite cualquier IP. No enviar el token en la URL ni exponerlo en aplicaciones cliente.\n\nPuedes solicitar un token `sk_demo_` sin una cuenta ni un alta de organización. Dura **24 horas desde su emisión** y solo funciona desde la **IP pública que lo solicitó**. Cambiar de red o de IP requiere solicitar otro token. Utiliza únicamente **datos ficticios**. Cada página del PDF muestra **SIN VALIDEZ** junto al logo y una marca diagonal **DEMO**. Estos documentos no sirven para transportar mercancías ni como documentos administrativos válidos."
      },
      "qrSecret": {
        "type": "apiKey",
        "in": "query",
        "name": "k",
        "description": "Secreto opaco del enlace QR; copiar exactamente el valor devuelto en url."
      }
    },
    "parameters": {
      "DocumentUuid": {
        "name": "uuid",
        "in": "path",
        "required": true,
        "description": "UUID público del documento; conocerlo no concede acceso.",
        "schema": {
          "type": "string",
          "format": "uuid"
        },
        "examples": {
          "default": {
            "value": "75b10a04-1a2d-4f9e-9277-5f3c270f476e"
          }
        }
      },
      "DocumentUuidWithReferenceAlternative": {
        "name": "uuid",
        "in": "path",
        "required": true,
        "description": "UUID público del documento. Alternativamente, utiliza `/api/v1/documents/ref/{reference}` con el mismo método. Ejemplo: `/api/v1/documents/ref/EXP-2026-004587`.",
        "schema": {
          "type": "string",
          "format": "uuid"
        },
        "examples": {
          "default": {
            "value": "75b10a04-1a2d-4f9e-9277-5f3c270f476e"
          }
        }
      },
      "DocumentStatusUuidWithReferenceAlternative": {
        "name": "uuid",
        "in": "path",
        "required": true,
        "description": "UUID público del documento. Alternativamente, utiliza `GET /api/v1/documents/ref/{reference}/status`. Ejemplo: `/api/v1/documents/ref/EXP-2026-004587/status`.",
        "schema": {
          "type": "string",
          "format": "uuid"
        },
        "examples": {
          "default": {
            "value": "75b10a04-1a2d-4f9e-9277-5f3c270f476e"
          }
        }
      }
    },
    "headers": {
      "NoStore": {
        "description": "La respuesta contiene información sensible y no debe almacenarse en caché.",
        "schema": {
          "type": "string",
          "examples": [
            "no-store, private"
          ]
        }
      }
    },
    "schemas": {
      "CountryCode": {
        "type": "string",
        "pattern": "^[A-Z]{2}$",
        "description": "Código del país en dos letras; se normaliza a mayúsculas. El formato no comprueba la pertenencia al catálogo ISO 3166-1.",
        "examples": [
          "ES"
        ]
      },
      "PositiveDecimal": {
        "type": "string",
        "pattern": "^(?!0+(?:\\.0+)?$)(?:0|[1-9][0-9]{0,14})(?:\\.[0-9]{1,6})?$",
        "description": "Decimal positivo como string, con punto, sin coma ni notación científica. Hasta 15 cifras enteras y 6 decimales. La respuesta elimina los ceros decimales finales.",
        "examples": [
          "1850.000"
        ]
      },
      "WeightKilograms": {
        "oneOf": [
          {
            "$ref": "#/components/schemas/PositiveDecimal"
          },
          {
            "type": "number",
            "exclusiveMinimum": 0,
            "exclusiveMaximum": 1000000000000000,
            "multipleOf": 0.000001
          }
        ],
        "description": "Peso positivo en kg, como número JSON o texto decimal. Hasta 15 cifras enteras y 6 decimales. La respuesta normaliza a texto y conserva la precisión; el PDF muestra hasta 3 decimales, con coma decimal y puntos de miles. Usa texto decimal para evitar pérdida de precisión en valores grandes.",
        "examples": [
          1850.5
        ]
      },
      "Address": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "street",
          "postal_code",
          "city",
          "country"
        ],
        "properties": {
          "street": {
            "type": "string",
            "minLength": 1,
            "description": "Vía y número del domicilio.",
            "maxLength": 255
          },
          "postal_code": {
            "type": "string",
            "minLength": 1,
            "description": "Código postal como texto para conservar ceros iniciales.",
            "maxLength": 255
          },
          "city": {
            "type": "string",
            "minLength": 1,
            "description": "Localidad del domicilio.",
            "maxLength": 255
          },
          "country": {
            "$ref": "#/components/schemas/CountryCode"
          }
        }
      },
      "Shipper": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "name",
          "tax_country",
          "tax_id",
          "address"
        ],
        "description": "Cargador contractual; puede ser distinto del almacén de origen y del cliente de DeCA.",
        "properties": {
          "name": {
            "type": "string",
            "minLength": 1,
            "description": "Nombre o razón social.",
            "maxLength": 255
          },
          "tax_country": {
            "$ref": "#/components/schemas/CountryCode"
          },
          "tax_id": {
            "type": "string",
            "minLength": 1,
            "description": "Identificador fiscal normalizado a mayúsculas y sin espacios ni guiones; la longitud se comprueba tras normalizar. En España se validan formato y carácter de control de DNI, NIE y NIF de entidades (CIF). Los NIF especiales K, L y M se comprueban solo por formato: prefijo, siete caracteres alfanuméricos y letra final. Para otros países se exige formato alfanumérico sin verificar su control fiscal. Esta validación no acredita existencia ni alta censal.",
            "maxLength": 32
          },
          "address": {
            "$ref": "#/components/schemas/Address"
          }
        }
      },
      "Carrier": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "name",
          "tax_country",
          "tax_id"
        ],
        "description": "Transportista que realiza efectivamente el transporte, incluido el subcontratado.",
        "properties": {
          "name": {
            "type": "string",
            "minLength": 1,
            "description": "Nombre o razón social.",
            "maxLength": 255
          },
          "tax_country": {
            "$ref": "#/components/schemas/CountryCode"
          },
          "tax_id": {
            "type": "string",
            "minLength": 1,
            "description": "Identificador fiscal normalizado a mayúsculas y sin espacios ni guiones; la longitud se comprueba tras normalizar. En España se validan formato y carácter de control de DNI, NIE y NIF de entidades (CIF). Los NIF especiales K, L y M se comprueban solo por formato: prefijo, siete caracteres alfanuméricos y letra final. Para otros países se exige formato alfanumérico sin verificar su control fiscal. Esta validación no acredita existencia ni alta censal.",
            "maxLength": 32
          },
          "address": {
            "description": "Domicilio opcional del transportista. Si se envía, debe incluir todos los campos de Address; el artículo 6 no lo exige como dato mínimo.",
            "type": "object",
            "additionalProperties": false,
            "required": [
              "street",
              "postal_code",
              "city",
              "country"
            ],
            "properties": {
              "street": {
                "type": "string",
                "minLength": 1,
                "description": "Vía y número del domicilio.",
                "maxLength": 255
              },
              "postal_code": {
                "type": "string",
                "minLength": 1,
                "description": "Código postal como texto para conservar ceros iniciales.",
                "maxLength": 255
              },
              "city": {
                "type": "string",
                "minLength": 1,
                "description": "Localidad del domicilio.",
                "maxLength": 255
              },
              "country": {
                "$ref": "#/components/schemas/CountryCode"
              }
            }
          }
        }
      },
      "TransportLocation": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "place",
          "city",
          "country"
        ],
        "properties": {
          "place": {
            "type": "string",
            "minLength": 1,
            "description": "Lugar real de carga o descarga, identificable por nombre de la instalación o dirección. Junto con city y country debe permitir identificarlo claramente; street y postal_code son opcionales. No se exige identificar a la empresa destinataria como dato mínimo.",
            "maxLength": 255
          },
          "city": {
            "type": "string",
            "minLength": 1,
            "description": "Localidad.",
            "maxLength": 255
          },
          "country": {
            "type": "string",
            "enum": [
              "ES"
            ],
            "description": "El alcance inicial cubre transportes interiores en España; no sustituye al CMR internacional."
          },
          "street": {
            "type": "string",
            "minLength": 1,
            "maxLength": 255,
            "description": "Vía y número opcionales del lugar de carga o descarga para precisar su ubicación."
          },
          "postal_code": {
            "type": "string",
            "minLength": 1,
            "maxLength": 255,
            "description": "Código postal opcional del lugar de carga o descarga, como texto para conservar ceros iniciales."
          }
        }
      },
      "AlternativeMeasure": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "value",
          "unit"
        ],
        "description": "Otra magnitud cuando resulte difícil determinar el peso. Enviar esta magnitud o el peso, nunca ambas.",
        "properties": {
          "value": {
            "$ref": "#/components/schemas/PositiveDecimal"
          },
          "unit": {
            "type": "string",
            "minLength": 1,
            "description": "Unidad explícita de la magnitud, hasta 255 caracteres.",
            "maxLength": 255,
            "examples": [
              "palés"
            ]
          }
        }
      },
      "Goods": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "description"
        ],
        "description": "Naturaleza y peso de la mercancía, o magnitud alternativa cuando corresponda. Enviar exactamente una medida.",
        "properties": {
          "description": {
            "type": "string",
            "minLength": 1,
            "description": "Descripción de la naturaleza de la mercancía.",
            "maxLength": 2000
          },
          "weight_kg": {
            "$ref": "#/components/schemas/WeightKilograms"
          },
          "alternative_measure": {
            "$ref": "#/components/schemas/AlternativeMeasure"
          }
        },
        "oneOf": [
          {
            "required": [
              "weight_kg"
            ]
          },
          {
            "required": [
              "alternative_measure"
            ]
          }
        ]
      },
      "Vehicle": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "country",
          "registration"
        ],
        "description": "Vehículo efectivo; si hay remolque o semirremolque, informar sus dos campos juntos.",
        "properties": {
          "country": {
            "$ref": "#/components/schemas/CountryCode"
          },
          "registration": {
            "type": "string",
            "minLength": 1,
            "description": "Matrícula normalizada a mayúsculas y sin espacios ni guiones. Para España se admite la matrícula ordinaria vigente, las provinciales anteriores y la serie especial E. Para otros países, entre 3 y 20 caracteres alfanuméricos.",
            "maxLength": 20,
            "examples": [
              "4827 MNL"
            ]
          },
          "trailer_country": {
            "$ref": "#/components/schemas/CountryCode"
          },
          "trailer_registration": {
            "type": "string",
            "minLength": 1,
            "description": "Matrícula del remolque o semirremolque, normalizada como la del tractor. Para España se exige la serie R. Para otros países, entre 3 y 20 caracteres alfanuméricos.",
            "maxLength": 20,
            "examples": [
              "R 5832 BCD"
            ]
          }
        },
        "oneOf": [
          {
            "required": [
              "trailer_country",
              "trailer_registration"
            ]
          },
          {
            "not": {
              "anyOf": [
                {
                  "properties": {
                    "trailer_country": {}
                  },
                  "required": [
                    "trailer_country"
                  ]
                },
                {
                  "properties": {
                    "trailer_registration": {}
                  },
                  "required": [
                    "trailer_registration"
                  ]
                }
              ]
            }
          }
        ]
      },
      "SpecialAuthorization": {
        "type": [
          "object",
          "null"
        ],
        "additionalProperties": false,
        "required": [
          "number"
        ],
        "description": "Autorización especial de circulación cuando sea necesaria. Omitir o enviar null si no procede.",
        "properties": {
          "number": {
            "type": "string",
            "minLength": 1,
            "description": "Número o referencia de la autorización.",
            "maxLength": 255
          },
          "issuer": {
            "type": "string",
            "minLength": 1,
            "description": "Organismo emisor, si se informa.",
            "maxLength": 255
          }
        }
      },
      "CreateDocumentRequest": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "transport_date",
          "shipper",
          "carrier",
          "origin",
          "destination",
          "goods",
          "vehicle"
        ],
        "properties": {
          "reference": {
            "type": [
              "string",
              "null"
            ],
            "minLength": 1,
            "description": "Referencia propia opcional, única por organización y entorno e impresa en el PDF. Identifica los reintentos y permite consultar por referencia. No se libera al eliminar el documento.",
            "pattern": "^[A-Za-z0-9-]+(?![\\s\\S])",
            "maxLength": 120
          },
          "transport_date": {
            "type": "string",
            "format": "date",
            "description": "Fecha real del transporte en formato YYYY-MM-DD; no es el instante de creación del PDF."
          },
          "shipper": {
            "$ref": "#/components/schemas/Shipper"
          },
          "carrier": {
            "$ref": "#/components/schemas/Carrier"
          },
          "origin": {
            "$ref": "#/components/schemas/TransportLocation"
          },
          "destination": {
            "$ref": "#/components/schemas/TransportLocation"
          },
          "goods": {
            "$ref": "#/components/schemas/Goods"
          },
          "vehicle": {
            "$ref": "#/components/schemas/Vehicle"
          },
          "special_authorization": {
            "$ref": "#/components/schemas/SpecialAuthorization"
          },
          "observations": {
            "type": [
              "string",
              "null"
            ],
            "description": "Observaciones que los intervinientes soliciten hacer constar en el documento.",
            "maxLength": 4000
          },
          "metadata": {
            "type": [
              "object",
              "null"
            ],
            "additionalProperties": true,
            "description": "Objeto JSON opcional, conservado y devuelto sin modificación semántica. No se imprime en el PDF. Máximo 16 KiB al serializarlo y 8 niveles de profundidad."
          }
        }
      },
      "DocumentResult": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "uuid",
          "reference",
          "created_at",
          "url",
          "qr",
          "pdf_download_url",
          "rectifies_uuid",
          "normalized_data",
          "metadata",
          "document_locale"
        ],
        "properties": {
          "uuid": {
            "type": "string",
            "format": "uuid",
            "description": "UUID del documento emitido."
          },
          "reference": {
            "type": [
              "string",
              "null"
            ],
            "description": "Referencia del cliente o null.",
            "pattern": "^[A-Za-z0-9-]+(?![\\s\\S])"
          },
          "created_at": {
            "type": "string",
            "format": "date-time",
            "description": "Instante original de creación, en UTC."
          },
          "url": {
            "type": "string",
            "format": "uri",
            "description": "URL HTTPS completa de descarga por QR, incluido su secreto. No registrar ni publicar."
          },
          "qr": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "mime_type",
              "encoding",
              "data"
            ],
            "properties": {
              "mime_type": {
                "type": "string",
                "enum": [
                  "image/png"
                ]
              },
              "encoding": {
                "type": "string",
                "enum": [
                  "base64"
                ]
              },
              "data": {
                "type": "string",
                "description": "Bytes de la imagen PNG en Base64, sin prefijo data URI.",
                "contentEncoding": "base64"
              }
            }
          },
          "pdf_download_url": {
            "type": "string",
            "format": "uri",
            "description": "URL de descarga autenticada; requiere Bearer de la credencial propietaria dentro de 24 horas."
          },
          "rectifies_uuid": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid",
            "description": "UUID rectificado o null en una emisión inicial."
          },
          "normalized_data": {
            "$ref": "#/components/schemas/CreateDocumentRequest"
          },
          "metadata": {
            "type": [
              "object",
              "null"
            ],
            "additionalProperties": true,
            "description": "La misma metadata de la petición; null si no se proporcionó."
          },
          "document_locale": {
            "type": "string",
            "enum": [
              "es",
              "ca",
              "gl",
              "ca-valencia",
              "eu",
              "en",
              "pt-pt",
              "fr"
            ],
            "description": "Idioma del PDF elegido al emitirlo según la organización. Se conserva en el resultado; cambiar el idioma de la organización no modifica documentos existentes. No cambia campos JSON ni mensajes de error de la API."
          }
        }
      },
      "DocumentSummary": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "uuid",
          "reference",
          "created_at",
          "transport_date",
          "rectifies_uuid"
        ],
        "properties": {
          "uuid": {
            "type": "string",
            "format": "uuid"
          },
          "reference": {
            "type": [
              "string",
              "null"
            ],
            "pattern": "^[A-Za-z0-9-]+(?![\\s\\S])"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "transport_date": {
            "type": "string",
            "format": "date"
          },
          "rectifies_uuid": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid"
          }
        }
      },
      "DocumentList": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "data",
          "meta"
        ],
        "properties": {
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/DocumentSummary"
            }
          },
          "meta": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "current_page",
              "limit",
              "total",
              "last_page"
            ],
            "properties": {
              "current_page": {
                "type": "integer",
                "minimum": 1
              },
              "limit": {
                "type": "integer",
                "minimum": 1,
                "maximum": 100
              },
              "total": {
                "type": "integer",
                "minimum": 0
              },
              "last_page": {
                "type": "integer",
                "minimum": 1
              }
            }
          }
        }
      },
      "ErrorResponse": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "errors"
        ],
        "properties": {
          "errors": {
            "type": "array",
            "minItems": 1,
            "description": "Todos los errores independientes detectados; sin datos de petición, secretos ni trazas.",
            "items": {
              "type": "object",
              "additionalProperties": false,
              "required": [
                "field",
                "code",
                "message"
              ],
              "properties": {
                "field": {
                  "type": [
                    "string",
                    "null"
                  ],
                  "description": "Ruta del campo con puntos; null para un error general."
                },
                "code": {
                  "type": "string",
                  "pattern": "^[A-Z][A-Z0-9_]*$",
                  "description": "Código estable para lógica de integración."
                },
                "message": {
                  "type": "string",
                  "description": "Mensaje breve en inglés. No utilizarlo como identificador del error."
                }
              }
            }
          }
        }
      },
      "RectifyDocumentRequest": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "transport_date",
          "shipper",
          "carrier",
          "origin",
          "destination",
          "goods",
          "vehicle"
        ],
        "properties": {
          "reference": {
            "type": [
              "string",
              "null"
            ],
            "minLength": 1,
            "description": "Referencia nueva del documento rectificativo, opcional y única por organización y entorno. No reutilizar la referencia del documento identificado en la URL.",
            "pattern": "^[A-Za-z0-9-]+(?![\\s\\S])",
            "maxLength": 120
          },
          "transport_date": {
            "type": "string",
            "format": "date",
            "description": "Fecha real del transporte en formato YYYY-MM-DD; no es el instante de creación del PDF."
          },
          "shipper": {
            "$ref": "#/components/schemas/Shipper"
          },
          "carrier": {
            "$ref": "#/components/schemas/Carrier"
          },
          "origin": {
            "$ref": "#/components/schemas/TransportLocation"
          },
          "destination": {
            "$ref": "#/components/schemas/TransportLocation"
          },
          "goods": {
            "$ref": "#/components/schemas/Goods"
          },
          "vehicle": {
            "$ref": "#/components/schemas/Vehicle"
          },
          "special_authorization": {
            "$ref": "#/components/schemas/SpecialAuthorization"
          },
          "observations": {
            "type": [
              "string",
              "null"
            ],
            "description": "Observaciones que los intervinientes soliciten hacer constar en el documento.",
            "maxLength": 4000
          },
          "metadata": {
            "type": [
              "object",
              "null"
            ],
            "additionalProperties": true,
            "description": "Objeto JSON opcional, conservado y devuelto sin modificación semántica. No se imprime en el PDF. Máximo 16 KiB al serializarlo y 8 niveles de profundidad."
          }
        },
        "description": "Todos los datos corregidos del transporte. El documento anterior se identifica en la URL. Si se incluye reference, debe ser una referencia nueva para el documento rectificativo."
      },
      "DeleteDocumentRequest": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "reason",
          "confirm_not_delivered",
          "confirm_not_used"
        ],
        "properties": {
          "reason": {
            "type": "string",
            "minLength": 1,
            "description": "Motivo del error detectado al generar el documento.",
            "maxLength": 2000
          },
          "confirm_not_delivered": {
            "type": "boolean",
            "description": "Debe ser true para eliminar: confirma que no se ha entregado al transportista ni al conductor. false devuelve 409 DOCUMENT_ALREADY_DELIVERED."
          },
          "confirm_not_used": {
            "type": "boolean",
            "description": "Debe ser true para eliminar: confirma que no se ha utilizado en un transporte. false devuelve 409 DOCUMENT_ALREADY_USED."
          }
        }
      },
      "DocumentStatus": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "status"
        ],
        "properties": {
          "status": {
            "type": "string",
            "enum": [
              "active",
              "deleted"
            ],
            "description": "Estado del documento. No incluye información del transporte, PDF, QR, metadata ni fechas."
          }
        }
      },
      "DemoTokenResult": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "token",
          "expires_at",
          "document_locale"
        ],
        "properties": {
          "token": {
            "type": "string",
            "pattern": "^sk_demo_[0-9a-f]{64}(_(ca|gl|ca-valencia|eu|en|pt-pt|fr))?$",
            "description": "Puedes solicitar un token `sk_demo_` sin una cuenta ni un alta de organización. Dura **24 horas desde su emisión** y solo funciona desde la **IP pública que lo solicitó**. Cambiar de red o de IP requiere solicitar otro token."
          },
          "expires_at": {
            "type": "string",
            "format": "date-time",
            "description": "Caducidad del token en UTC. Todos sus documentos y enlaces vencen en este instante."
          },
          "document_locale": {
            "type": "string",
            "enum": [
              "es",
              "ca",
              "gl",
              "ca-valencia",
              "eu",
              "en",
              "pt-pt",
              "fr"
            ],
            "description": "Idioma de los PDF de este token. Español por defecto."
          }
        }
      },
      "DemoDocumentResult": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "uuid",
          "reference",
          "created_at",
          "url",
          "qr",
          "pdf_download_url",
          "rectifies_uuid",
          "normalized_data",
          "metadata",
          "document_locale",
          "demo",
          "expires_at",
          "qr_url",
          "pdf_url"
        ],
        "properties": {
          "uuid": {
            "type": "string",
            "format": "uuid",
            "description": "UUID del documento emitido."
          },
          "reference": {
            "type": [
              "string",
              "null"
            ],
            "description": "Referencia del cliente o null.",
            "pattern": "^[A-Za-z0-9-]+(?![\\s\\S])"
          },
          "created_at": {
            "type": "string",
            "format": "date-time",
            "description": "Instante original de creación, en UTC."
          },
          "url": {
            "type": "string",
            "format": "uri",
            "description": "URL HTTPS completa de descarga por QR, incluido su secreto. No registrar ni publicar."
          },
          "qr": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "mime_type",
              "encoding",
              "data"
            ],
            "properties": {
              "mime_type": {
                "type": "string",
                "enum": [
                  "image/png"
                ]
              },
              "encoding": {
                "type": "string",
                "enum": [
                  "base64"
                ]
              },
              "data": {
                "type": "string",
                "description": "Bytes de la imagen PNG en Base64, sin prefijo data URI.",
                "contentEncoding": "base64"
              }
            }
          },
          "pdf_download_url": {
            "type": "string",
            "format": "uri",
            "description": "Puedes solicitar un token `sk_demo_` sin una cuenta ni un alta de organización. Dura **24 horas desde su emisión** y solo funciona desde la **IP pública que lo solicitó**. Cambiar de red o de IP requiere solicitar otro token."
          },
          "rectifies_uuid": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid",
            "description": "UUID rectificado o null en una emisión inicial."
          },
          "normalized_data": {
            "$ref": "#/components/schemas/CreateDocumentRequest"
          },
          "metadata": {
            "type": [
              "object",
              "null"
            ],
            "additionalProperties": true,
            "description": "La misma metadata de la petición; null si no se proporcionó."
          },
          "document_locale": {
            "type": "string",
            "enum": [
              "es",
              "ca",
              "gl",
              "ca-valencia",
              "eu",
              "en",
              "pt-pt",
              "fr"
            ],
            "description": "Idioma de los PDF de este token. Español por defecto."
          },
          "demo": {
            "type": "boolean",
            "enum": [
              true
            ],
            "description": "Solo en la demo; siempre true. No aparece en resultados de desarrollo o producción."
          },
          "expires_at": {
            "type": "string",
            "format": "date-time",
            "description": "Caducidad del token en UTC. Todos sus documentos y enlaces vencen en este instante."
          },
          "qr_url": {
            "type": "string",
            "format": "uri",
            "description": "Solo en la demo; alias de url."
          },
          "pdf_url": {
            "type": "string",
            "format": "uri",
            "description": "Solo en la demo; alias de pdf_download_url. Requiere el token demo y su IP."
          }
        },
        "description": "La demo añade demo: true, expires_at, qr_url (alias de url) y pdf_url (alias de pdf_download_url). document_locale es el idioma elegido al solicitar el token (es por defecto). Los datos y PDF temporales quedan separados del archivo real y no se cifran como los documentos reales."
      }
    },
    "examples": {
      "DocumentResult": {
        "summary": "Resultado completo con datos ficticios y QR ilustrativo",
        "value": {
          "uuid": "75b10a04-1a2d-4f9e-9277-5f3c270f476e",
          "reference": "EXP-2026-004587",
          "created_at": "2026-10-05T14:43:00Z",
          "url": "https://deca.sertecpesage.com/d/75b10a04-1a2d-4f9e-9277-5f3c270f476e?k=EXAMPLE_SECRET",
          "qr": {
            "mime_type": "image/png",
            "encoding": "base64",
            "data": "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mP8/x8AAwMCAO+jRZkAAAAASUVORK5CYII="
          },
          "pdf_download_url": "https://deca.sertecpesage.com/api/v1/documents/75b10a04-1a2d-4f9e-9277-5f3c270f476e/pdf",
          "rectifies_uuid": null,
          "normalized_data": {
            "reference": "EXP-2026-004587",
            "transport_date": "2026-10-05",
            "shipper": {
              "name": "Distribuciones Demo, S. L.",
              "tax_country": "ES",
              "tax_id": "B12345674",
              "address": {
                "street": "Carrer de la Logística, 18",
                "postal_code": "08040",
                "city": "Barcelona",
                "country": "ES"
              }
            },
            "carrier": {
              "name": "Transportes Demo, S. L.",
              "tax_country": "ES",
              "tax_id": "B87654323"
            },
            "origin": {
              "place": "Almacén Barcelona",
              "city": "Barcelona",
              "country": "ES"
            },
            "destination": {
              "place": "Almacén Zaragoza",
              "city": "Zaragoza",
              "country": "ES"
            },
            "goods": {
              "description": "Vino embotellado",
              "weight_kg": "1850"
            },
            "vehicle": {
              "country": "ES",
              "registration": "4827MNL",
              "trailer_country": "ES",
              "trailer_registration": "R5832BCD"
            },
            "special_authorization": null,
            "observations": null,
            "metadata": {
              "order_id": "4587",
              "terminal": "POS-02"
            }
          },
          "metadata": {
            "order_id": "4587",
            "terminal": "POS-02"
          },
          "document_locale": "es"
        }
      },
      "RectifiedDocumentResult": {
        "summary": "Documento rectificativo con UUID, PDF y QR nuevos",
        "value": {
          "uuid": "dbf53b0c-e0ec-49a4-952d-ed59d9b1b0d1",
          "reference": "EXP-2026-004587-R1",
          "created_at": "2026-10-05T15:00:00Z",
          "url": "https://deca.sertecpesage.com/d/dbf53b0c-e0ec-49a4-952d-ed59d9b1b0d1?k=EXAMPLE_RECTIFICATION_SECRET",
          "qr": {
            "mime_type": "image/png",
            "encoding": "base64",
            "data": "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mP8/x8AAwMCAO+jRZkAAAAASUVORK5CYII="
          },
          "pdf_download_url": "https://deca.sertecpesage.com/api/v1/documents/dbf53b0c-e0ec-49a4-952d-ed59d9b1b0d1/pdf",
          "rectifies_uuid": "75b10a04-1a2d-4f9e-9277-5f3c270f476e",
          "normalized_data": {
            "reference": "EXP-2026-004587-R1",
            "transport_date": "2026-10-05",
            "shipper": {
              "name": "Distribuciones Demo, S. L.",
              "tax_country": "ES",
              "tax_id": "B12345674",
              "address": {
                "street": "Carrer de la Logística, 18",
                "postal_code": "08040",
                "city": "Barcelona",
                "country": "ES"
              }
            },
            "carrier": {
              "name": "Transportes Demo, S. L.",
              "tax_country": "ES",
              "tax_id": "B87654323"
            },
            "origin": {
              "place": "Almacén Barcelona",
              "city": "Barcelona",
              "country": "ES"
            },
            "destination": {
              "place": "Almacén Zaragoza",
              "city": "Zaragoza",
              "country": "ES"
            },
            "goods": {
              "description": "Vino embotellado",
              "weight_kg": "1900"
            },
            "vehicle": {
              "country": "ES",
              "registration": "4827MNL",
              "trailer_country": "ES",
              "trailer_registration": "R5832BCD"
            },
            "special_authorization": null,
            "observations": "Corrección del peso de la mercancía.",
            "metadata": {
              "order_id": "4587",
              "terminal": "POS-02"
            }
          },
          "metadata": {
            "order_id": "4587",
            "terminal": "POS-02"
          },
          "document_locale": "es"
        }
      }
    },
    "responses": {
      "PdfDownload": {
        "description": "PDF original nativo, de hasta 5 MB. Descarga directa sin regeneración.",
        "headers": {
          "Content-Disposition": {
            "description": "Nombre seguro que no incorpora datos del cliente.",
            "schema": {
              "type": "string"
            },
            "examples": {
              "default": {
                "value": "attachment; filename=\"deca-75b10a04-1a2d-4f9e-9277-5f3c270f476e.pdf\""
              }
            }
          },
          "Cache-Control": {
            "$ref": "#/components/headers/NoStore"
          },
          "Referrer-Policy": {
            "schema": {
              "type": "string",
              "enum": [
                "no-referrer"
              ]
            },
            "description": "Evitar propagación de la URL secreta."
          },
          "X-Content-Type-Options": {
            "schema": {
              "type": "string",
              "enum": [
                "nosniff"
              ]
            },
            "description": "Impedir interpretación del contenido como otro tipo."
          }
        },
        "content": {
          "application/pdf": {
            "schema": {
              "type": "string",
              "contentMediaType": "application/octet-stream"
            }
          }
        }
      },
      "PdfPreview": {
        "description": "PDF original nativo, de hasta 5 MB. Visualización en el navegador sin regeneración.",
        "headers": {
          "Content-Disposition": {
            "description": "Nombre seguro que no incorpora datos del cliente.",
            "schema": {
              "type": "string"
            },
            "examples": {
              "default": {
                "value": "inline; filename=\"deca-75b10a04-1a2d-4f9e-9277-5f3c270f476e.pdf\""
              }
            }
          },
          "Cache-Control": {
            "$ref": "#/components/headers/NoStore"
          },
          "Referrer-Policy": {
            "schema": {
              "type": "string",
              "enum": [
                "no-referrer"
              ]
            },
            "description": "Evitar propagación de la URL secreta."
          },
          "X-Content-Type-Options": {
            "schema": {
              "type": "string",
              "enum": [
                "nosniff"
              ]
            },
            "description": "Impedir interpretación del contenido como otro tipo."
          }
        },
        "content": {
          "application/pdf": {
            "schema": {
              "type": "string",
              "contentMediaType": "application/octet-stream"
            }
          }
        }
      },
      "BadRequest": {
        "description": "JSON malformado; no se procesa la solicitud.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ErrorResponse"
            },
            "examples": {
              "default": {
                "value": {
                  "errors": [
                    {
                      "field": null,
                      "code": "INVALID_JSON",
                      "message": "Request body must contain valid JSON."
                    }
                  ]
                }
              }
            }
          }
        }
      },
      "Unauthorized": {
        "description": "Bearer ausente, inválido o revocado.\n\nUn token demo vencido o enviado desde otra IP devuelve `401`. Un QR demo vencido devuelve `404`. Si la demo está desactivada, devuelve `503 DEMO_UNAVAILABLE`. El primer acceso al QR o una rectificación impiden eliminar el documento; se mantienen las confirmaciones de borrado del contrato habitual.",
        "headers": {
          "WWW-Authenticate": {
            "schema": {
              "type": "string",
              "examples": [
                "Bearer"
              ]
            },
            "description": "Esquema de autenticación requerido."
          }
        },
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ErrorResponse"
            },
            "examples": {
              "default": {
                "value": {
                  "errors": [
                    {
                      "field": null,
                      "code": "UNAUTHORIZED",
                      "message": "Valid API credentials are required."
                    }
                  ]
                }
              }
            }
          }
        }
      },
      "Forbidden": {
        "description": "IP no autorizada o acción no permitida para la credencial.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ErrorResponse"
            },
            "examples": {
              "default": {
                "value": {
                  "errors": [
                    {
                      "field": null,
                      "code": "FORBIDDEN",
                      "message": "This request is not allowed."
                    }
                  ]
                }
              }
            }
          }
        }
      },
      "NotFound": {
        "description": "Documento no disponible para este acceso; no revela existencia, propietario ni motivo.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ErrorResponse"
            },
            "examples": {
              "default": {
                "value": {
                  "errors": [
                    {
                      "field": null,
                      "code": "DOCUMENT_NOT_AVAILABLE",
                      "message": "Document is not available."
                    }
                  ]
                }
              }
            }
          }
        }
      },
      "UnsupportedMediaType": {
        "description": "Las peticiones POST, PUT y DELETE con cuerpo exigen Content-Type application/json.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ErrorResponse"
            },
            "examples": {
              "default": {
                "value": {
                  "errors": [
                    {
                      "field": null,
                      "code": "UNSUPPORTED_MEDIA_TYPE",
                      "message": "Content-Type must be application/json."
                    }
                  ]
                }
              }
            }
          }
        }
      },
      "ValidationError": {
        "description": "Campos inválidos; devuelve todos los errores independientes detectados. Los ocho idiomas traducen los títulos y etiquetas del PDF, no los nombres, direcciones, observaciones ni otros datos enviados por la integración. Los textos que se imprimen admiten caracteres latinos, griegos y cirílicos compatibles con las fuentes; emoji, CJK y escrituras de derecha a izquierda devuelven 422 UNSUPPORTED_CHARACTERS en el campo afectado. metadata conserva Unicode arbitrario y no se imprime.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ErrorResponse"
            },
            "examples": {
              "default": {
                "value": {
                  "errors": [
                    {
                      "field": "shipper.tax_id",
                      "code": "INVALID_CHECKSUM",
                      "message": "Invalid tax ID checksum."
                    },
                    {
                      "field": "vehicle.registration",
                      "code": "INVALID_REGISTRATION",
                      "message": "Invalid vehicle registration."
                    },
                    {
                      "field": "goods.weight_kg",
                      "code": "INVALID_WEIGHT",
                      "message": "Weight must be greater than zero."
                    },
                    {
                      "field": "goods.description",
                      "code": "UNSUPPORTED_CHARACTERS",
                      "message": "Printable text must use supported Latin, Greek or Cyrillic characters."
                    }
                  ]
                }
              }
            }
          }
        }
      },
      "TooManyRequests": {
        "description": "Límite alcanzado: 60 peticiones por minuto y token para /api/v1, o 30 peticiones por minuto para la misma IP y documento en la descarga por QR.\n\nLímite antiabuso o capacidad temporal de la demostración alcanzados; respeta Retry-After. La demo admite cuerpos de hasta 32 KiB y diez documentos por sesión. La emisión de tokens admite tres por minuto y diez por hora desde una misma IP; la creación, cinco por minuto y veinte por hora desde una misma IP. Los límites compartidos por red, región y carga global pueden aplicarse antes. Estas cifras corresponden a la configuración predeterminada.",
        "headers": {
          "Retry-After": {
            "description": "Segundos que deben transcurrir antes del siguiente intento.",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "examples": [
                60
              ]
            }
          }
        },
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ErrorResponse"
            },
            "examples": {
              "default": {
                "value": {
                  "errors": [
                    {
                      "field": null,
                      "code": "RATE_LIMIT_EXCEEDED",
                      "message": "Too many requests."
                    }
                  ]
                }
              }
            }
          }
        }
      },
      "InternalError": {
        "description": "Error interno sin detalles sensibles. Una emisión fallida no deja un documento parcial.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ErrorResponse"
            },
            "examples": {
              "default": {
                "value": {
                  "errors": [
                    {
                      "field": null,
                      "code": "INTERNAL_ERROR",
                      "message": "An internal error occurred."
                    }
                  ]
                }
              }
            }
          }
        }
      },
      "RequestTooLarge": {
        "description": "El cuerpo supera el límite de 128 KiB.\n\nLa demo admite cuerpos de hasta 32 KiB y diez documentos por sesión. La emisión de tokens admite tres por minuto y diez por hora desde una misma IP; la creación, cinco por minuto y veinte por hora desde una misma IP. Los límites compartidos por red, región y carga global pueden aplicarse antes. Estas cifras corresponden a la configuración predeterminada.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ErrorResponse"
            },
            "examples": {
              "default": {
                "value": {
                  "errors": [
                    {
                      "field": null,
                      "code": "REQUEST_TOO_LARGE",
                      "message": "Request body exceeds the 128 KiB limit."
                    }
                  ]
                }
              }
            }
          }
        }
      },
      "ServiceUnavailable": {
        "description": "Servicio no disponible por una configuración incorrecta de la URL pública HTTPS. Una emisión fallida no persiste un documento parcial.\n\nLa demostración está desactivada o no está disponible. DEMO_UNAVAILABLE.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ErrorResponse"
            },
            "examples": {
              "default": {
                "value": {
                  "errors": [
                    {
                      "field": null,
                      "code": "SERVICE_UNAVAILABLE",
                      "message": "The document service requires a configured HTTPS public URL."
                    }
                  ]
                }
              }
            }
          }
        }
      },
      "DemoUnavailable": {
        "description": "La demostración está desactivada o no está disponible.",
        "headers": {
          "Cache-Control": {
            "$ref": "#/components/headers/NoStore"
          }
        },
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ErrorResponse"
            },
            "examples": {
              "default": {
                "value": {
                  "errors": [
                    {
                      "field": null,
                      "code": "DEMO_UNAVAILABLE",
                      "message": "The demo is temporarily unavailable."
                    }
                  ]
                }
              }
            }
          }
        }
      },
      "DemoRateLimited": {
        "description": "Límite antiabuso o capacidad temporal de la demostración alcanzados; respeta Retry-After.",
        "headers": {
          "Retry-After": {
            "description": "Segundos que deben transcurrir antes del siguiente intento.",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "examples": [
                60
              ]
            }
          },
          "Cache-Control": {
            "$ref": "#/components/headers/NoStore"
          }
        },
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ErrorResponse"
            },
            "examples": {
              "rateLimit": {
                "value": {
                  "errors": [
                    {
                      "field": null,
                      "code": "RATE_LIMIT_EXCEEDED",
                      "message": "Too many demo requests. Try again later."
                    }
                  ]
                }
              },
              "capacity": {
                "value": {
                  "errors": [
                    {
                      "field": null,
                      "code": "DEMO_CAPACITY_REACHED",
                      "message": "The demo has reached its temporary capacity. Try again later."
                    }
                  ]
                }
              }
            }
          }
        }
      }
    }
  }
}
