{
  "openapi": "3.1.1",
  "info": {
    "title": "API DeCA",
    "version": "1.0.0",
    "description": "API for issuing, correcting, retrieving and downloading Spanish administrative control documents for goods transport. Contact SERTEC PESAGE support to obtain a token. sk_test_ tokens use development and sk_live_ tokens use production, with separate data. IP restrictions are optional.\n\nEach credential can retrieve and download its documents for 24 hours after creation. QR downloads have an independent lifetime. Corrections preserve the original document.\n\nLogical deletion is allowed only within the first 2 hours to correct generation errors. The integration must confirm that the document has neither been handed to the carrier nor used for transport. The file and audit trail are retained, but access is blocked.\n\nYou can request an `sk_demo_` token without an account or a registered organisation. It lasts **24 hours from issuance** and works only from the **public IP that requested it**. Changing network or IP requires another token. Use **fictitious data only**. Every PDF page displays **NOT VALID** beside the logo and a diagonal **DEMO** watermark. These documents are not valid for transporting goods or for administrative use. The demo adds demo: true, expires_at, qr_url (alias of url) and pdf_url (alias of pdf_download_url). document_locale is the language selected when requesting the token (es by default). Temporary data and PDFs are separate from the real archive and are not encrypted like real documents. The demo accepts bodies up to 32 KiB and ten documents per session. Token issuance allows three per minute and ten per hour from the same IP; document creation allows five per minute and twenty per hour from the same IP. Shared network, region and global load limits may apply earlier. These figures describe the default configuration."
  },
  "servers": [
    {
      "url": "https://deca.sertecpesage.com",
      "description": "Production"
    }
  ],
  "security": [
    {
      "bearerAuth": []
    }
  ],
  "tags": [
    {
      "name": "Documents",
      "description": "Issuing, correcting and accessing your own recent documents."
    },
    {
      "name": "QR download",
      "description": "Direct PDF download using a document secret."
    },
    {
      "name": "API demonstration",
      "description": "Get a temporary public token and generate demonstration documents using fictitious data."
    }
  ],
  "paths": {
    "/api/v1/documents": {
      "post": {
        "operationId": "createDocument",
        "tags": [
          "Documents"
        ],
        "summary": "Create document",
        "description": "Send the complete transport details. The response is returned after the encrypted PDF has been generated and stored.\n\nA non-null reference is unique per organization and environment. Repeating a reference with identical normalized data and metadata returns the original result within 24 hours (200), without issuing another PDF. A new creation returns 201. Different data or a reference owned by another credential returns 409 without disclosing documents. Deduplication is not guaranteed without a reference.\n\nA reference whose result has expired returns 409 DOCUMENT_WINDOW_EXPIRED. A deleted document is not returned and its reference cannot be reused; the retry returns 404. Use the PUT endpoints for corrections.\n\nYou can request an `sk_demo_` token without an account or a registered organisation. It lasts **24 hours from issuance** and works only from the **public IP that requested it**. Changing network or IP requires another token. The demo adds demo: true, expires_at, qr_url (alias of url) and pdf_url (alias of pdf_download_url). document_locale is the language selected when requesting the token (es by default). Temporary data and PDFs are separate from the real archive and are not encrypted like real documents.",
        "requestBody": {
          "required": true,
          "description": "Complete details of the document to create.",
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateDocumentRequest"
              },
              "examples": {
                "create": {
                  "summary": "Issue with weight and trailer",
                  "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": "Alternative measurement when weight is difficult to determine",
                  "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": "Idempotent retry; returns the original result without generating another 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": "New document generated and stored.",
            "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": "Conflicting reference or expired retry result.",
            "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": [
          "Documents"
        ],
        "summary": "List recent documents",
        "description": "Returns summaries without the PDF, QR secret or metadata. The server always enforces the 24-hour window and credential isolation. Results are ordered by creation time descending, with UUID as a tie-breaker, page-based pagination and inclusive transport-date filters. The window is evaluated on every request; it does not provide a stable snapshot across pages. Historical access is unavailable.\n\nDeleted documents do not appear in the list.",
        "parameters": [
          {
            "name": "page",
            "in": "query",
            "description": "Requested page, starting at 1.",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "default": 1
            }
          },
          {
            "name": "limit",
            "in": "query",
            "description": "Documents per page, from 1 to 100.",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 25
            }
          },
          {
            "name": "from",
            "in": "query",
            "description": "Earliest transport date, inclusive. Does not extend the read window.",
            "schema": {
              "type": "string",
              "format": "date"
            },
            "examples": {
              "default": {
                "value": "2026-10-05"
              }
            }
          },
          {
            "name": "to",
            "in": "query",
            "description": "Latest transport date, inclusive; must be on or after from.",
            "schema": {
              "type": "string",
              "format": "date"
            },
            "examples": {
              "default": {
                "value": "2026-10-05"
              }
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Page of recent documents. A page without results has an empty data array.",
            "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": [
          "Documents"
        ],
        "summary": "Correct document",
        "x-deca-order": 20,
        "description": "Identify the previous document by UUID and send all corrected details, without patches. Only the latest version belonging to the same credential can be corrected. Documents older than 24 hours can be corrected without returning their previous content. A deleted document returns 404 and cannot be corrected.\n\nA corrective document is generated with a new UUID, PDF and QR; the original and its QR are retained. Give the new PDF to the driver. The request body's reference identifies the new version, not the document in the URL.\n\nRepeating the same PUT with identical normalized data and metadata returns the already-issued correction within 24 hours of its creation, without generating another version. This check occurs before rejecting an already-corrected document and also works when the retry identifies the same document by reference. Changing data on an already-corrected document returns 409. An expired retry result returns 409 DOCUMENT_WINDOW_EXPIRED. If the returned correction is deleted, the retry returns 404. For another correction, use the UUID or reference of the latest version.",
        "requestBody": {
          "required": true,
          "description": "Complete corrected transport details.",
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/RectifyDocumentRequest"
              },
              "examples": {
                "rectify": {
                  "summary": "Full correction with a new reference",
                  "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": "Corrective document issued, or an identical retry result without creating another version.",
            "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": "Document already corrected, conflicting new reference or expired retry result.",
            "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": [
          "Documents"
        ],
        "summary": "Delete document",
        "x-deca-order": 40,
        "description": "Logical deletion to correct errors found when generating a document, exclusively **before handing it to the carrier or driver and before using it for transport**. The integration must confirm both conditions; being within the time limit is not enough. If the document has already been handed over or used, correct it with PUT.\n\nAllowed only while the server time is earlier than created_at + **2 hours**; it is no longer allowed at exactly 2 hours. Neither retrieval nor retries restart the period. Each version uses its own creation time. Only the latest version belonging to the authenticated credential can be deleted.\n\nThe PDF, data and audit trail are retained, but the document disappears from the list. UUID/reference retrieval, authenticated download, QR access and retries that would return its content respond with 404. The status endpoint returns only status: deleted within the 24-hour read window. The reference remains reserved. There is no reactivation endpoint; deletion retains the file to allow future recovery.\n\nRepeating DELETE within the same 2 hours returns 204 without changing the deletion time. After that period it returns 409.\n\nThe first successful access through the QR link also blocks deletion with 409 DOCUMENT_ALREADY_ACCESSED. This access alone does not prove that transport has taken place.",
        "requestBody": {
          "required": true,
          "description": "Reason and confirmation that the document has not yet been handed over or used.",
          "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": "Document marked as deleted. No response body.",
            "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": "2-hour period expired, document handed over or used, or version already corrected. The first successful access through the QR link also blocks deletion with 409 DOCUMENT_ALREADY_ACCESSED. This access alone does not prove that transport has taken place.",
            "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": [
          "Documents"
        ],
        "summary": "Retrieve document",
        "description": "Retrieves the original result within 24 hours, only for the credential that issued the document. Includes all accepted and normalized fields and semantically unchanged metadata.\n\nA nonexistent UUID or reference, one belonging to another credential or one outside the window returns the same `404`, without confirming existence or ownership. Each reference identifies its own version and does not redirect to a later correction.\n\nA deleted document returns 404. Use the status endpoint to check whether it has been deleted.",
        "responses": {
          "200": {
            "description": "Original document result.",
            "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": [
          "Documents"
        ],
        "summary": "Retrieve status",
        "x-deca-order": 80,
        "description": "Returns only the status of a document belonging to the authenticated credential, within 24 hours of creation. A deleted document returns 200 with {\"status\":\"deleted\"}; no transport data, PDF, QR link or metadata is returned. An active document returns {\"status\":\"active\"}.\n\nA nonexistent UUID, one belonging to another credential or one outside the 24-hour window returns the same 404. Deletion does not extend this window.",
        "responses": {
          "200": {
            "description": "Document status without its content.",
            "headers": {
              "Cache-Control": {
                "$ref": "#/components/headers/NoStore"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DocumentStatus"
                },
                "examples": {
                  "deleted": {
                    "summary": "Deleted document",
                    "value": {
                      "status": "deleted"
                    }
                  },
                  "active": {
                    "summary": "Active document",
                    "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": "Public document UUID. Alternatively, use `GET /api/v1/documents/ref/{reference}/pdf`. Example: `/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": [
          "Documents"
        ],
        "summary": "Download PDF",
        "description": "Returns the exact bytes of the stored PDF without regenerating it. Requires the owning credential and a document age of less than 24 hours. This limit does not affect legal retention or QR validity.\n\nA deleted document returns 404 without delivering the PDF.",
        "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": [
          "QR download"
        ],
        "summary": "Download PDF by QR",
        "description": "Link included in the QR. The k secret authorizes access to this document only. No Bearer, session, CAPTCHA or intermediate page is required. Anyone who knows the link can download it. Do not record the full URL in logs, analytics or traces.\n\nDownloads do not expire after 24 hours and are not automatically revoked when the Bearer token is rotated or revoked. The previous QR retains its PDF after a correction; it does not redirect to the new version.\n\nThe PDF is delivered with `Content-Disposition: inline` to open in the browser viewer.\n\nA deleted document returns 404, even with the correct secret.",
        "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": [
          "API demonstration"
        ],
        "summary": "Request a demo token",
        "description": "Send an empty JSON object to this public endpoint. No Bearer header is required. The `201` response contains `token` and `expires_at`; the expiry time is expressed in UTC.\n\nYou can request an `sk_demo_` token without an account or a registered organisation. It lasts **24 hours from issuance** and works only from the **public IP that requested it**. Changing network or IP requires another token.\n\nUse **fictitious data only**. Every PDF page displays **NOT VALID** beside the logo and a diagonal **DEMO** watermark. These documents are not valid for transporting goods or for administrative use.\n\nThe demo accepts bodies up to 32 KiB and ten documents per session. Token issuance allows three per minute and ten per hour from the same IP; document creation allows five per minute and twenty per hour from the same IP. Shared network, region and global load limits may apply earlier. These figures describe the default configuration. Send document_locale to choose the language of every PDF for this token: es, ca, gl, ca-valencia, eu, en, pt-pt or fr. If omitted, es is used. The response also includes document_locale; changing it requires a new token.",
        "security": [],
        "requestBody": {
          "required": true,
          "description": "Send an empty JSON object to this public endpoint. No Bearer header is required. The `201` response contains `token` and `expires_at`; the expiry time is expressed in UTC. Send document_locale to choose the language of every PDF for this token: es, ca, gl, ca-valencia, eu, en, pt-pt or fr. If omitted, es is used. The response also includes document_locale; changing it requires a new 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": "Language of this token’s PDFs. Spanish by default."
                  }
                }
              },
              "examples": {
                "default": {
                  "value": {}
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Demo token issued; valid for 24 hours and bound to the requesting IP.",
            "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": [
          "API demonstration"
        ],
        "summary": "Open the demo PDF by QR",
        "description": "The QR opens the PDF directly in the browser through `/demo/d/{uuid}?k=...`, without Bearer authentication. The document and its links expire with the demo session; creating or rectifying a document does not extend the token’s lifetime.\n\nUse **fictitious data only**. Every PDF page displays **NOT VALID** beside the logo and a diagonal **DEMO** watermark. These documents are not valid for transporting goods or for administrative use.",
        "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": "Integration token supplied by SERTEC PESAGE support. Send in Authorization: Bearer, exclusively over HTTPS. sk_test_ selects development and sk_live_ selects production. If the token has an authorized IP list, requests must originate from one of those addresses; an empty list permits any IP. Do not send the token in URLs or expose it in client applications.\n\nYou can request an `sk_demo_` token without an account or a registered organisation. It lasts **24 hours from issuance** and works only from the **public IP that requested it**. Changing network or IP requires another token. Use **fictitious data only**. Every PDF page displays **NOT VALID** beside the logo and a diagonal **DEMO** watermark. These documents are not valid for transporting goods or for administrative use."
      },
      "qrSecret": {
        "type": "apiKey",
        "in": "query",
        "name": "k",
        "description": "Opaque QR link secret; copy the value returned in url exactly."
      }
    },
    "parameters": {
      "DocumentUuid": {
        "name": "uuid",
        "in": "path",
        "required": true,
        "description": "Public document UUID; knowing it does not grant access.",
        "schema": {
          "type": "string",
          "format": "uuid"
        },
        "examples": {
          "default": {
            "value": "75b10a04-1a2d-4f9e-9277-5f3c270f476e"
          }
        }
      },
      "DocumentUuidWithReferenceAlternative": {
        "name": "uuid",
        "in": "path",
        "required": true,
        "description": "Public document UUID. Alternatively, use `/api/v1/documents/ref/{reference}` with the same method. Example: `/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": "Public document UUID. Alternatively, use `GET /api/v1/documents/ref/{reference}/status`. Example: `/api/v1/documents/ref/EXP-2026-004587/status`.",
        "schema": {
          "type": "string",
          "format": "uuid"
        },
        "examples": {
          "default": {
            "value": "75b10a04-1a2d-4f9e-9277-5f3c270f476e"
          }
        }
      }
    },
    "headers": {
      "NoStore": {
        "description": "The response contains sensitive information and must not be cached.",
        "schema": {
          "type": "string",
          "examples": [
            "no-store, private"
          ]
        }
      }
    },
    "schemas": {
      "CountryCode": {
        "type": "string",
        "pattern": "^[A-Z]{2}$",
        "description": "Two-letter country code; normalized to uppercase. This format does not verify membership of the ISO 3166-1 catalogue.",
        "examples": [
          "ES"
        ]
      },
      "PositiveDecimal": {
        "type": "string",
        "pattern": "^(?!0+(?:\\.0+)?$)(?:0|[1-9][0-9]{0,14})(?:\\.[0-9]{1,6})?$",
        "description": "Positive decimal as a string, using a dot, without commas or scientific notation. Up to 15 integer digits and 6 decimal places. Responses strip trailing decimal zeros.",
        "examples": [
          "1850.000"
        ]
      },
      "WeightKilograms": {
        "oneOf": [
          {
            "$ref": "#/components/schemas/PositiveDecimal"
          },
          {
            "type": "number",
            "exclusiveMinimum": 0,
            "exclusiveMaximum": 1000000000000000,
            "multipleOf": 0.000001
          }
        ],
        "description": "Positive weight in kg, as a JSON number or a decimal string. Up to 15 integer digits and 6 decimal places. Responses normalise to a string and preserve precision; PDFs display up to 3 decimal places, using a decimal comma and dots for thousands. Use a decimal string to avoid losing precision with large values.",
        "examples": [
          1850.5
        ]
      },
      "Address": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "street",
          "postal_code",
          "city",
          "country"
        ],
        "properties": {
          "street": {
            "type": "string",
            "minLength": 1,
            "description": "Street and building number.",
            "maxLength": 255
          },
          "postal_code": {
            "type": "string",
            "minLength": 1,
            "description": "Postal code as text to preserve leading zeros.",
            "maxLength": 255
          },
          "city": {
            "type": "string",
            "minLength": 1,
            "description": "Address locality.",
            "maxLength": 255
          },
          "country": {
            "$ref": "#/components/schemas/CountryCode"
          }
        }
      },
      "Shipper": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "name",
          "tax_country",
          "tax_id",
          "address"
        ],
        "description": "Contractual shipper; may differ from the origin warehouse and the DeCA customer.",
        "properties": {
          "name": {
            "type": "string",
            "minLength": 1,
            "description": "Name or legal business name.",
            "maxLength": 255
          },
          "tax_country": {
            "$ref": "#/components/schemas/CountryCode"
          },
          "tax_id": {
            "type": "string",
            "minLength": 1,
            "description": "Tax ID normalized to uppercase without spaces or hyphens; length is checked after normalization. Spain validates the format and check character of DNI, NIE and entity NIF (CIF). Special K, L and M tax IDs are checked by format only: prefix, seven alphanumeric characters and a final letter. Other countries require alphanumeric format without tax checksum verification. Validation does not establish existence or tax registration.",
            "maxLength": 32
          },
          "address": {
            "$ref": "#/components/schemas/Address"
          }
        }
      },
      "Carrier": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "name",
          "tax_country",
          "tax_id"
        ],
        "description": "Carrier actually performing the transport, including subcontractors.",
        "properties": {
          "name": {
            "type": "string",
            "minLength": 1,
            "description": "Name or legal business name.",
            "maxLength": 255
          },
          "tax_country": {
            "$ref": "#/components/schemas/CountryCode"
          },
          "tax_id": {
            "type": "string",
            "minLength": 1,
            "description": "Tax ID normalized to uppercase without spaces or hyphens; length is checked after normalization. Spain validates the format and check character of DNI, NIE and entity NIF (CIF). Special K, L and M tax IDs are checked by format only: prefix, seven alphanumeric characters and a final letter. Other countries require alphanumeric format without tax checksum verification. Validation does not establish existence or tax registration.",
            "maxLength": 32
          },
          "address": {
            "description": "Optional carrier address. If supplied, all Address fields are required; article 6 does not require it as a minimum data item.",
            "type": "object",
            "additionalProperties": false,
            "required": [
              "street",
              "postal_code",
              "city",
              "country"
            ],
            "properties": {
              "street": {
                "type": "string",
                "minLength": 1,
                "description": "Street and building number.",
                "maxLength": 255
              },
              "postal_code": {
                "type": "string",
                "minLength": 1,
                "description": "Postal code as text to preserve leading zeros.",
                "maxLength": 255
              },
              "city": {
                "type": "string",
                "minLength": 1,
                "description": "Address locality.",
                "maxLength": 255
              },
              "country": {
                "$ref": "#/components/schemas/CountryCode"
              }
            }
          }
        }
      },
      "TransportLocation": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "place",
          "city",
          "country"
        ],
        "properties": {
          "place": {
            "type": "string",
            "minLength": 1,
            "description": "Actual loading or unloading location, identifiable by site name or address. Together with city and country it must identify the location clearly; street and postal_code are optional. A consignee company's identity is not a mandatory minimum item.",
            "maxLength": 255
          },
          "city": {
            "type": "string",
            "minLength": 1,
            "description": "Locality.",
            "maxLength": 255
          },
          "country": {
            "type": "string",
            "enum": [
              "ES"
            ],
            "description": "The initial scope covers domestic transport in Spain; it does not replace the international CMR."
          },
          "street": {
            "type": "string",
            "minLength": 1,
            "maxLength": 255,
            "description": "Optional street and building number of the loading or unloading location to identify it precisely."
          },
          "postal_code": {
            "type": "string",
            "minLength": 1,
            "maxLength": 255,
            "description": "Optional postal code of the loading or unloading location, as text to preserve leading zeros."
          }
        }
      },
      "AlternativeMeasure": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "value",
          "unit"
        ],
        "description": "Alternative measurement when weight is difficult to determine. Send either this measurement or weight, never both.",
        "properties": {
          "value": {
            "$ref": "#/components/schemas/PositiveDecimal"
          },
          "unit": {
            "type": "string",
            "minLength": 1,
            "description": "Explicit measurement unit, up to 255 characters.",
            "maxLength": 255,
            "examples": [
              "palés"
            ]
          }
        }
      },
      "Goods": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "description"
        ],
        "description": "Nature and weight of the goods, or an alternative measurement where applicable. Send exactly one measurement.",
        "properties": {
          "description": {
            "type": "string",
            "minLength": 1,
            "description": "Description of the nature of the goods.",
            "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": "Actual vehicle; if a trailer or semi-trailer is present, supply both trailer fields together.",
        "properties": {
          "country": {
            "$ref": "#/components/schemas/CountryCode"
          },
          "registration": {
            "type": "string",
            "minLength": 1,
            "description": "Registration normalized to uppercase without spaces or hyphens. Spain accepts current ordinary registrations, former provincial registrations and the special E series. Other countries accept 3 to 20 alphanumeric characters.",
            "maxLength": 20,
            "examples": [
              "4827 MNL"
            ]
          },
          "trailer_country": {
            "$ref": "#/components/schemas/CountryCode"
          },
          "trailer_registration": {
            "type": "string",
            "minLength": 1,
            "description": "Trailer or semi-trailer registration, normalized like the tractor registration. Spain requires the R series. Other countries accept 3 to 20 alphanumeric characters.",
            "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": "Special circulation authorization when required. Omit or send null if not applicable.",
        "properties": {
          "number": {
            "type": "string",
            "minLength": 1,
            "description": "Authorization number or reference.",
            "maxLength": 255
          },
          "issuer": {
            "type": "string",
            "minLength": 1,
            "description": "Issuing authority, if supplied.",
            "maxLength": 255
          }
        }
      },
      "CreateDocumentRequest": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "transport_date",
          "shipper",
          "carrier",
          "origin",
          "destination",
          "goods",
          "vehicle"
        ],
        "properties": {
          "reference": {
            "type": [
              "string",
              "null"
            ],
            "minLength": 1,
            "description": "Optional customer reference, unique per organization and environment and printed on the PDF. Identifies retries and allows retrieval by reference. It is not released when the document is deleted.",
            "pattern": "^[A-Za-z0-9-]+(?![\\s\\S])",
            "maxLength": 120
          },
          "transport_date": {
            "type": "string",
            "format": "date",
            "description": "Actual transport date in YYYY-MM-DD format; not the PDF creation timestamp."
          },
          "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": "Remarks requested by the parties for inclusion in the document.",
            "maxLength": 4000
          },
          "metadata": {
            "type": [
              "object",
              "null"
            ],
            "additionalProperties": true,
            "description": "Optional JSON object, retained and returned without semantic changes. Not printed on the PDF. Maximum serialized size 16 KiB and nesting depth 8."
          }
        }
      },
      "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 of the issued document."
          },
          "reference": {
            "type": [
              "string",
              "null"
            ],
            "description": "Customer reference or null.",
            "pattern": "^[A-Za-z0-9-]+(?![\\s\\S])"
          },
          "created_at": {
            "type": "string",
            "format": "date-time",
            "description": "Original creation timestamp, in UTC."
          },
          "url": {
            "type": "string",
            "format": "uri",
            "description": "Full HTTPS QR download URL including its secret. Do not log or publish it."
          },
          "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": "PNG image bytes in Base64, without a data URI prefix.",
                "contentEncoding": "base64"
              }
            }
          },
          "pdf_download_url": {
            "type": "string",
            "format": "uri",
            "description": "Authenticated download URL; requires the owning credential's Bearer within 24 hours."
          },
          "rectifies_uuid": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid",
            "description": "Corrected UUID, or null for an initial issue."
          },
          "normalized_data": {
            "$ref": "#/components/schemas/CreateDocumentRequest"
          },
          "metadata": {
            "type": [
              "object",
              "null"
            ],
            "additionalProperties": true,
            "description": "The request's unchanged metadata; null if omitted."
          },
          "document_locale": {
            "type": "string",
            "enum": [
              "es",
              "ca",
              "gl",
              "ca-valencia",
              "eu",
              "en",
              "pt-pt",
              "fr"
            ],
            "description": "PDF language selected from the organization at issue time. Preserved in the result; changing the organization language does not modify existing documents. Does not change JSON fields or API error messages."
          }
        }
      },
      "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": "All independently detected errors; no request data, secrets or stack traces.",
            "items": {
              "type": "object",
              "additionalProperties": false,
              "required": [
                "field",
                "code",
                "message"
              ],
              "properties": {
                "field": {
                  "type": [
                    "string",
                    "null"
                  ],
                  "description": "Dot-separated field path; null for a general error."
                },
                "code": {
                  "type": "string",
                  "pattern": "^[A-Z][A-Z0-9_]*$",
                  "description": "Stable code for integration logic."
                },
                "message": {
                  "type": "string",
                  "description": "Short message in English. Do not use it as an error identifier."
                }
              }
            }
          }
        }
      },
      "RectifyDocumentRequest": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "transport_date",
          "shipper",
          "carrier",
          "origin",
          "destination",
          "goods",
          "vehicle"
        ],
        "properties": {
          "reference": {
            "type": [
              "string",
              "null"
            ],
            "minLength": 1,
            "description": "Optional new reference for the corrective document, unique per organization and environment. Do not reuse the reference of the document identified in the URL.",
            "pattern": "^[A-Za-z0-9-]+(?![\\s\\S])",
            "maxLength": 120
          },
          "transport_date": {
            "type": "string",
            "format": "date",
            "description": "Actual transport date in YYYY-MM-DD format; not the PDF creation timestamp."
          },
          "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": "Remarks requested by the parties for inclusion in the document.",
            "maxLength": 4000
          },
          "metadata": {
            "type": [
              "object",
              "null"
            ],
            "additionalProperties": true,
            "description": "Optional JSON object, retained and returned without semantic changes. Not printed on the PDF. Maximum serialized size 16 KiB and nesting depth 8."
          }
        },
        "description": "All corrected transport details. The previous document is identified in the URL. If reference is supplied, it must be a new reference for the corrective document."
      },
      "DeleteDocumentRequest": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "reason",
          "confirm_not_delivered",
          "confirm_not_used"
        ],
        "properties": {
          "reason": {
            "type": "string",
            "minLength": 1,
            "description": "Reason for the error detected while generating the document.",
            "maxLength": 2000
          },
          "confirm_not_delivered": {
            "type": "boolean",
            "description": "Must be true to delete: confirms it has not been handed to the carrier or driver. false returns 409 DOCUMENT_ALREADY_DELIVERED."
          },
          "confirm_not_used": {
            "type": "boolean",
            "description": "Must be true to delete: confirms it has not been used for transport. false returns 409 DOCUMENT_ALREADY_USED."
          }
        }
      },
      "DocumentStatus": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "status"
        ],
        "properties": {
          "status": {
            "type": "string",
            "enum": [
              "active",
              "deleted"
            ],
            "description": "Document status. Does not include transport data, PDF, QR, metadata or dates."
          }
        }
      },
      "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": "You can request an `sk_demo_` token without an account or a registered organisation. It lasts **24 hours from issuance** and works only from the **public IP that requested it**. Changing network or IP requires another token."
          },
          "expires_at": {
            "type": "string",
            "format": "date-time",
            "description": "Token expiry time in UTC. All its documents and links expire at this instant."
          },
          "document_locale": {
            "type": "string",
            "enum": [
              "es",
              "ca",
              "gl",
              "ca-valencia",
              "eu",
              "en",
              "pt-pt",
              "fr"
            ],
            "description": "Language of this token’s PDFs. Spanish by default."
          }
        }
      },
      "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 of the issued document."
          },
          "reference": {
            "type": [
              "string",
              "null"
            ],
            "description": "Customer reference or null.",
            "pattern": "^[A-Za-z0-9-]+(?![\\s\\S])"
          },
          "created_at": {
            "type": "string",
            "format": "date-time",
            "description": "Original creation timestamp, in UTC."
          },
          "url": {
            "type": "string",
            "format": "uri",
            "description": "Full HTTPS QR download URL including its secret. Do not log or publish it."
          },
          "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": "PNG image bytes in Base64, without a data URI prefix.",
                "contentEncoding": "base64"
              }
            }
          },
          "pdf_download_url": {
            "type": "string",
            "format": "uri",
            "description": "You can request an `sk_demo_` token without an account or a registered organisation. It lasts **24 hours from issuance** and works only from the **public IP that requested it**. Changing network or IP requires another token."
          },
          "rectifies_uuid": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid",
            "description": "Corrected UUID, or null for an initial issue."
          },
          "normalized_data": {
            "$ref": "#/components/schemas/CreateDocumentRequest"
          },
          "metadata": {
            "type": [
              "object",
              "null"
            ],
            "additionalProperties": true,
            "description": "The request's unchanged metadata; null if omitted."
          },
          "document_locale": {
            "type": "string",
            "enum": [
              "es",
              "ca",
              "gl",
              "ca-valencia",
              "eu",
              "en",
              "pt-pt",
              "fr"
            ],
            "description": "Language of this token’s PDFs. Spanish by default."
          },
          "demo": {
            "type": "boolean",
            "enum": [
              true
            ],
            "description": "Demo only; always true. Absent from development and production results."
          },
          "expires_at": {
            "type": "string",
            "format": "date-time",
            "description": "Token expiry time in UTC. All its documents and links expire at this instant."
          },
          "qr_url": {
            "type": "string",
            "format": "uri",
            "description": "Demo only; alias of url."
          },
          "pdf_url": {
            "type": "string",
            "format": "uri",
            "description": "Demo only; alias of pdf_download_url. Requires the demo token and its IP."
          }
        },
        "description": "The demo adds demo: true, expires_at, qr_url (alias of url) and pdf_url (alias of pdf_download_url). document_locale is the language selected when requesting the token (es by default). Temporary data and PDFs are separate from the real archive and are not encrypted like real documents."
      }
    },
    "examples": {
      "DocumentResult": {
        "summary": "Complete result with fictitious data and an illustrative QR",
        "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": "Corrective document with new UUID, PDF and QR",
        "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": "Original native PDF, up to 5 MB. Direct download without regeneration.",
        "headers": {
          "Content-Disposition": {
            "description": "Safe filename containing no customer data.",
            "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": "Prevent disclosure of the secret URL through referrers."
          },
          "X-Content-Type-Options": {
            "schema": {
              "type": "string",
              "enum": [
                "nosniff"
              ]
            },
            "description": "Prevent interpreting the content as another type."
          }
        },
        "content": {
          "application/pdf": {
            "schema": {
              "type": "string",
              "contentMediaType": "application/octet-stream"
            }
          }
        }
      },
      "PdfPreview": {
        "description": "Original native PDF, up to 5 MB. Browser viewing without regeneration.",
        "headers": {
          "Content-Disposition": {
            "description": "Safe filename containing no customer data.",
            "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": "Prevent disclosure of the secret URL through referrers."
          },
          "X-Content-Type-Options": {
            "schema": {
              "type": "string",
              "enum": [
                "nosniff"
              ]
            },
            "description": "Prevent interpreting the content as another type."
          }
        },
        "content": {
          "application/pdf": {
            "schema": {
              "type": "string",
              "contentMediaType": "application/octet-stream"
            }
          }
        }
      },
      "BadRequest": {
        "description": "Malformed JSON; the request is not processed.",
        "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": "Missing, invalid or revoked Bearer.\n\nAn expired demo token or one sent from a different IP returns `401`. An expired demo QR returns `404`. When the demo is disabled, it returns `503 DEMO_UNAVAILABLE`. The first QR access or a rectification prevents deletion; the standard contract’s deletion confirmations still apply.",
        "headers": {
          "WWW-Authenticate": {
            "schema": {
              "type": "string",
              "examples": [
                "Bearer"
              ]
            },
            "description": "Required authentication scheme."
          }
        },
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ErrorResponse"
            },
            "examples": {
              "default": {
                "value": {
                  "errors": [
                    {
                      "field": null,
                      "code": "UNAUTHORIZED",
                      "message": "Valid API credentials are required."
                    }
                  ]
                }
              }
            }
          }
        }
      },
      "Forbidden": {
        "description": "Unauthorized IP or action not allowed for the credential.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ErrorResponse"
            },
            "examples": {
              "default": {
                "value": {
                  "errors": [
                    {
                      "field": null,
                      "code": "FORBIDDEN",
                      "message": "This request is not allowed."
                    }
                  ]
                }
              }
            }
          }
        }
      },
      "NotFound": {
        "description": "Document unavailable for this access; does not disclose existence, ownership or reason.",
        "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": "POST, PUT and DELETE requests with a body require 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": "Invalid fields; returns all independently detected errors. The eight languages translate PDF headings and labels, not names, addresses, remarks or other data submitted by the integration. Printed text supports font-compatible Latin, Greek and Cyrillic characters; emoji, CJK and right-to-left scripts return 422 UNSUPPORTED_CHARACTERS for the affected field. metadata preserves arbitrary Unicode and is not printed.",
        "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": "Rate limit reached: 60 requests per minute per token for /api/v1, or 30 requests per minute for the same IP and document for QR downloads.\n\nAn abuse prevention or temporary demo capacity limit has been reached; respect Retry-After. The demo accepts bodies up to 32 KiB and ten documents per session. Token issuance allows three per minute and ten per hour from the same IP; document creation allows five per minute and twenty per hour from the same IP. Shared network, region and global load limits may apply earlier. These figures describe the default configuration.",
        "headers": {
          "Retry-After": {
            "description": "Seconds to wait before the next attempt.",
            "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": "Internal error without sensitive details. A failed issue leaves no partial document.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ErrorResponse"
            },
            "examples": {
              "default": {
                "value": {
                  "errors": [
                    {
                      "field": null,
                      "code": "INTERNAL_ERROR",
                      "message": "An internal error occurred."
                    }
                  ]
                }
              }
            }
          }
        }
      },
      "RequestTooLarge": {
        "description": "Body exceeds the 128 KiB limit.\n\nThe demo accepts bodies up to 32 KiB and ten documents per session. Token issuance allows three per minute and ten per hour from the same IP; document creation allows five per minute and twenty per hour from the same IP. Shared network, region and global load limits may apply earlier. These figures describe the default configuration.",
        "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": "Service unavailable because the public HTTPS URL is incorrectly configured. A failed issue does not persist a partial document.\n\nThe demonstration is disabled or unavailable. 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": "The demonstration is disabled or unavailable.",
        "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": "An abuse prevention or temporary demo capacity limit has been reached; respect Retry-After.",
        "headers": {
          "Retry-After": {
            "description": "Seconds to wait before the next attempt.",
            "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."
                    }
                  ]
                }
              }
            }
          }
        }
      }
    }
  }
}
