{
  "openapi": "3.1.1",
  "info": {
    "title": "API DeCA",
    "version": "1.0.0",
    "description": "API pour émettre, rectifier, consulter et télécharger des documents espagnols de contrôle administratif du transport de marchandises. Contactez le support SERTEC PESAGE pour obtenir un jeton. Les jetons sk_test_ utilisent le développement et les sk_live_ la production, avec des données séparées. La restriction par IP est facultative.\n\nChaque identifiant permet de consulter et télécharger ses documents pendant les 24 heures suivant leur création. Le téléchargement par QR a une durée de validité indépendante. Les rectifications conservent le document original.\n\nLa suppression logique n'est autorisée que durant les 2 premières heures pour corriger des erreurs de génération. L'intégration doit confirmer que le document n'a été ni remis au transporteur ni utilisé pour le transport. Le fichier et la traçabilité sont conservés, mais l'accès est bloqué.\n\nVous pouvez demander un jeton `sk_demo_` sans compte ni organisation enregistrée. Il dure **24 heures à partir de son émission** et fonctionne uniquement depuis **l’IP publique qui l’a demandé**. Un changement de réseau ou d’IP nécessite un autre jeton. Utilisez uniquement des **données fictives**. Chaque page du PDF affiche **SANS VALIDITÉ** à côté du logo et un filigrane diagonal **DEMO**. Ces documents ne sont valables ni pour transporter des marchandises ni pour un usage administratif. La démo ajoute demo: true, expires_at, qr_url (alias de url) et pdf_url (alias de pdf_download_url). document_locale est la langue choisie lors de la demande du jeton (es par défaut). Les données et PDF temporaires sont distincts de l’archive réelle et ne sont pas chiffrés comme les documents réels. La démo accepte des corps de requête jusqu’à 32 KiB et dix documents par session. L’émission de jetons permet trois par minute et dix par heure depuis la même IP ; la création de documents permet cinq par minute et vingt par heure depuis la même IP. Les limites partagées par réseau, région et charge globale peuvent s’appliquer plus tôt. Ces valeurs correspondent à la configuration par défaut."
  },
  "servers": [
    {
      "url": "https://deca.sertecpesage.com",
      "description": "Production"
    }
  ],
  "security": [
    {
      "bearerAuth": []
    }
  ],
  "tags": [
    {
      "name": "Documents",
      "description": "Émission, rectification et accès à vos documents récents."
    },
    {
      "name": "Téléchargement par QR",
      "description": "Téléchargement direct du PDF à l'aide du secret d'un document."
    },
    {
      "name": "Démonstration de l’API",
      "description": "Obtenez un jeton public temporaire et générez des documents de démonstration avec des données fictives."
    }
  ],
  "paths": {
    "/api/v1/documents": {
      "post": {
        "operationId": "createDocument",
        "tags": [
          "Documents"
        ],
        "summary": "Créer un document",
        "description": "Envoyez toutes les données du transport. La réponse est renvoyée après génération et stockage du PDF chiffré.\n\nUne référence non nulle est unique par organisation et environnement. Répéter la référence avec les mêmes données normalisées et metadata renvoie le résultat original dans les 24 heures (200), sans émettre un autre PDF. Une nouvelle création renvoie 201. Des données différentes ou une référence d'un autre identifiant produisent 409 sans divulguer de documents. Sans référence, la déduplication n'est pas garantie.\n\nUne référence dont le résultat a expiré renvoie 409 DOCUMENT_WINDOW_EXPIRED. Si le document est supprimé, son contenu n'est pas renvoyé et sa référence n'est pas réutilisable ; la nouvelle tentative renvoie 404. Utilisez les endpoints PUT pour rectifier.\n\nVous pouvez demander un jeton `sk_demo_` sans compte ni organisation enregistrée. Il dure **24 heures à partir de son émission** et fonctionne uniquement depuis **l’IP publique qui l’a demandé**. Un changement de réseau ou d’IP nécessite un autre jeton. La démo ajoute demo: true, expires_at, qr_url (alias de url) et pdf_url (alias de pdf_download_url). document_locale est la langue choisie lors de la demande du jeton (es par défaut). Les données et PDF temporaires sont distincts de l’archive réelle et ne sont pas chiffrés comme les documents réels.",
        "requestBody": {
          "required": true,
          "description": "Données complètes du document à créer.",
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateDocumentRequest"
              },
              "examples": {
                "create": {
                  "summary": "Émission avec poids et remorque",
                  "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": "Mesure alternative lorsque le poids est difficile à déterminer",
                  "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": "Nouvelle tentative idempotente ; renvoie le résultat original sans générer un autre 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": "Nouveau document généré et stocké.",
            "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": "Référence en conflit ou résultat de nouvelle tentative expiré.",
            "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": "Lister les documents récents",
        "description": "Renvoie des résumés sans PDF, secret QR ni metadata. Le serveur applique toujours la fenêtre de 24 heures et l'isolation des identifiants. Tri décroissant par date de création, avec UUID pour départager, pagination par page et filtres inclusifs de date de transport. La fenêtre est évaluée à chaque requête ; elle ne constitue pas un instantané stable entre les pages. L'historique n'est pas accessible.\n\nLes documents supprimés ne figurent pas dans la liste.",
        "parameters": [
          {
            "name": "page",
            "in": "query",
            "description": "Page demandée, à partir de 1.",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "default": 1
            }
          },
          {
            "name": "limit",
            "in": "query",
            "description": "Documents par page, de 1 à 100.",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 25
            }
          },
          {
            "name": "from",
            "in": "query",
            "description": "Date minimale de transport, incluse. N'étend pas la fenêtre de lecture.",
            "schema": {
              "type": "string",
              "format": "date"
            },
            "examples": {
              "default": {
                "value": "2026-10-05"
              }
            }
          },
          {
            "name": "to",
            "in": "query",
            "description": "Date maximale de transport, incluse ; égale ou postérieure à from.",
            "schema": {
              "type": "string",
              "format": "date"
            },
            "examples": {
              "default": {
                "value": "2026-10-05"
              }
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Page de documents récents. Une page sans résultats contient un tableau data vide.",
            "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": "Rectifier un document",
        "x-deca-order": 20,
        "description": "Identifiez le document précédent par UUID et envoyez toutes les données corrigées, sans modifications partielles. Seule la dernière version du même identifiant peut être rectifiée. Un document de plus de 24 heures peut être rectifié sans renvoyer son contenu précédent. Un document supprimé renvoie 404 et ne peut pas être rectifié.\n\nUn document rectificatif est généré avec de nouveaux UUID, PDF et QR ; l'original et son QR sont conservés. Remettez le nouveau PDF au conducteur. Le champ reference du corps correspond à la nouvelle version, pas au document de l'URL.\n\nRépéter le même PUT avec les mêmes données normalisées et metadata renvoie la rectification déjà émise pendant les 24 heures suivant sa création, sans générer une autre version. Cette vérification précède le rejet d'un document déjà rectifié et fonctionne aussi si la nouvelle tentative identifie le même document par référence. Modifier les données d'un document déjà rectifié renvoie 409. Un résultat expiré renvoie 409 DOCUMENT_WINDOW_EXPIRED. Si la rectification renvoyée est supprimée, la nouvelle tentative renvoie 404. Pour une autre rectification, utilisez l'UUID ou la référence de la dernière version.",
        "requestBody": {
          "required": true,
          "description": "Données complètes corrigées du transport.",
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/RectifyDocumentRequest"
              },
              "examples": {
                "rectify": {
                  "summary": "Rectification complète avec une nouvelle référence",
                  "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": "Document rectificatif émis, ou résultat d'une nouvelle tentative identique sans créer une autre 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 déjà rectifié, nouvelle référence en conflit ou résultat de nouvelle tentative expiré.",
            "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": "Supprimer un document",
        "x-deca-order": 40,
        "description": "Suppression logique pour corriger des erreurs détectées lors de la génération d'un document, exclusivement **avant sa remise au transporteur ou au conducteur et avant son utilisation pour un transport**. L'intégration doit confirmer les deux conditions ; respecter le délai ne suffit pas. Si le document a déjà été remis ou utilisé, il doit être rectifié par PUT.\n\nAutorisée uniquement tant que l'heure du serveur est antérieure à created_at + **2 heures** ; à exactement 2 heures, elle n'est plus autorisée. Ni une consultation ni une nouvelle tentative ne redémarrent le délai. Chaque version utilise sa propre date de création. Seule la dernière version de l'identifiant authentifié peut être supprimée.\n\nLe PDF, les données et la traçabilité sont conservés, mais le document disparaît de la liste. Les consultations par UUID ou référence, le téléchargement authentifié, le QR et les nouvelles tentatives qui renverraient le contenu répondent 404. La consultation d'état répond uniquement avec status: deleted pendant la fenêtre de consultation de 24 heures. La référence reste réservée. Aucun endpoint de réactivation n'existe ; la suppression conserve le fichier pour permettre une récupération future.\n\nRépéter DELETE dans les mêmes 2 heures renvoie 204 sans modifier la date de suppression. Au-delà, la réponse est 409.\n\nLe premier accès réussi au lien QR bloque aussi la suppression avec 409 DOCUMENT_ALREADY_ACCESSED. Cet accès ne prouve pas, à lui seul, que le transport a eu lieu.",
        "requestBody": {
          "required": true,
          "description": "Motif et confirmation que le document n'a pas encore été remis ni utilisé.",
          "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 marqué comme supprimé. Aucun corps de réponse.",
            "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": "Délai de 2 heures expiré, document remis ou utilisé, ou version déjà rectifiée. Le premier accès réussi au lien QR bloque aussi la suppression avec 409 DOCUMENT_ALREADY_ACCESSED. Cet accès ne prouve pas, à lui seul, que le transport a eu lieu.",
            "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": "Consulter un document",
        "description": "Récupère le résultat original dans les 24 heures, uniquement pour l'identifiant ayant émis le document. Inclut tous les champs acceptés et normalisés et la metadata sans modification sémantique.\n\nUn UUID ou une référence inexistant, appartenant à un autre identifiant ou hors fenêtre renvoie le même `404`, sans confirmer l'existence ni le propriétaire. Chaque référence identifie sa propre version et ne redirige pas vers une rectification ultérieure.\n\nUn document supprimé renvoie 404. Utilisez la consultation d'état pour savoir s'il est supprimé.",
        "responses": {
          "200": {
            "description": "Résultat original du document.",
            "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": "Consulter l'état",
        "x-deca-order": 80,
        "description": "Renvoie uniquement l'état d'un document de l'identifiant authentifié pendant les 24 heures suivant sa création. Un document supprimé répond 200 avec {\"status\":\"deleted\"} ; aucune donnée de transport, PDF, lien QR ni metadata n'est renvoyé. Un document actif répond avec {\"status\":\"active\"}.\n\nUn UUID inexistant, d'un autre identifiant ou hors des 24 heures renvoie le même 404. La suppression n'étend pas cette fenêtre.",
        "responses": {
          "200": {
            "description": "État du document sans données de contenu.",
            "headers": {
              "Cache-Control": {
                "$ref": "#/components/headers/NoStore"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DocumentStatus"
                },
                "examples": {
                  "deleted": {
                    "summary": "Document supprimé",
                    "value": {
                      "status": "deleted"
                    }
                  },
                  "active": {
                    "summary": "Document actif",
                    "value": {
                      "status": "active"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        },
        "x-deca-alternative-path": "/api/v1/documents/ref/{reference}/status"
      }
    },
    "/api/v1/documents/{uuid}/pdf": {
      "parameters": [
        {
          "name": "uuid",
          "in": "path",
          "required": true,
          "description": "UUID public du document. Vous pouvez aussi utiliser `GET /api/v1/documents/ref/{reference}/pdf`. Exemple : `/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": "Télécharger le PDF",
        "description": "Renvoie les octets exacts du PDF stocké, sans le régénérer. Exige l'identifiant propriétaire et un document de moins de 24 heures. Cette limite ne modifie ni la conservation légale ni la validité du QR.\n\nUn document supprimé renvoie 404 sans livrer le 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": [
          "Téléchargement par QR"
        ],
        "summary": "Télécharger le PDF par QR",
        "description": "Lien inclus dans le QR. Le secret k autorise uniquement ce document. Aucun Bearer, session, CAPTCHA ni page intermédiaire n'est requis. Toute personne connaissant le lien peut le télécharger. N'enregistrez pas l'URL complète dans les journaux, les analyses ni les traces.\n\nLe téléchargement n'expire pas après 24 heures et n'est pas automatiquement révoqué lors de la rotation ou révocation du Bearer. Le QR précédent conserve son PDF après une rectification ; il ne redirige pas vers la nouvelle version.\n\nLe PDF est livré avec `Content-Disposition: inline` pour s'ouvrir dans le lecteur du navigateur.\n\nUn document supprimé renvoie 404, même avec le bon 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": [
          "Démonstration de l’API"
        ],
        "summary": "Demander un jeton demo",
        "description": "Envoyez un objet JSON vide à cet endpoint public. Aucun en-tête Bearer n’est nécessaire. La réponse `201` contient `token` et `expires_at` ; la date d’expiration est exprimée en UTC.\n\nVous pouvez demander un jeton `sk_demo_` sans compte ni organisation enregistrée. Il dure **24 heures à partir de son émission** et fonctionne uniquement depuis **l’IP publique qui l’a demandé**. Un changement de réseau ou d’IP nécessite un autre jeton.\n\nUtilisez uniquement des **données fictives**. Chaque page du PDF affiche **SANS VALIDITÉ** à côté du logo et un filigrane diagonal **DEMO**. Ces documents ne sont valables ni pour transporter des marchandises ni pour un usage administratif.\n\nLa démo accepte des corps de requête jusqu’à 32 KiB et dix documents par session. L’émission de jetons permet trois par minute et dix par heure depuis la même IP ; la création de documents permet cinq par minute et vingt par heure depuis la même IP. Les limites partagées par réseau, région et charge globale peuvent s’appliquer plus tôt. Ces valeurs correspondent à la configuration par défaut. Envoyez document_locale pour choisir la langue de tous les PDF du jeton : es, ca, gl, ca-valencia, eu, en, pt-pt ou fr. Sans ce champ, es est utilisé. La réponse inclut aussi document_locale ; demandez un nouveau jeton pour le modifier.",
        "security": [],
        "requestBody": {
          "required": true,
          "description": "Envoyez un objet JSON vide à cet endpoint public. Aucun en-tête Bearer n’est nécessaire. La réponse `201` contient `token` et `expires_at` ; la date d’expiration est exprimée en UTC. Envoyez document_locale pour choisir la langue de tous les PDF du jeton : es, ca, gl, ca-valencia, eu, en, pt-pt ou fr. Sans ce champ, es est utilisé. La réponse inclut aussi document_locale ; demandez un nouveau jeton pour le modifier.",
          "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": "Langue des PDF de ce jeton. Espagnol par défaut."
                  }
                }
              },
              "examples": {
                "default": {
                  "value": {}
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Jeton demo émis ; valable 24 heures et lié à l’IP du demandeur.",
            "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": [
          "Démonstration de l’API"
        ],
        "summary": "Ouvrir le PDF demo par QR",
        "description": "Le QR ouvre directement le PDF dans le navigateur via `/demo/d/{uuid}?k=...`, sans Bearer. Le document et ses liens expirent avec la session demo ; créer ou rectifier un document ne prolonge pas la durée du jeton.\n\nUtilisez uniquement des **données fictives**. Chaque page du PDF affiche **SANS VALIDITÉ** à côté du logo et un filigrane diagonal **DEMO**. Ces documents ne sont valables ni pour transporter des marchandises ni pour un usage administratif.",
        "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": "Jeton d'intégration fourni par le support SERTEC PESAGE. Envoyez-le dans Authorization: Bearer, exclusivement via HTTPS. sk_test_ sélectionne le développement et sk_live_ la production. Si le jeton possède une liste d'IP autorisées, la requête doit provenir de l'une d'elles ; une liste vide autorise toute IP. N'envoyez pas le jeton dans l'URL et ne l'exposez pas dans les applications clientes.\n\nVous pouvez demander un jeton `sk_demo_` sans compte ni organisation enregistrée. Il dure **24 heures à partir de son émission** et fonctionne uniquement depuis **l’IP publique qui l’a demandé**. Un changement de réseau ou d’IP nécessite un autre jeton. Utilisez uniquement des **données fictives**. Chaque page du PDF affiche **SANS VALIDITÉ** à côté du logo et un filigrane diagonal **DEMO**. Ces documents ne sont valables ni pour transporter des marchandises ni pour un usage administratif."
      },
      "qrSecret": {
        "type": "apiKey",
        "in": "query",
        "name": "k",
        "description": "Secret opaque du lien QR ; copiez exactement la valeur renvoyée dans url."
      }
    },
    "parameters": {
      "DocumentUuid": {
        "name": "uuid",
        "in": "path",
        "required": true,
        "description": "UUID public du document ; le connaître ne donne pas accès.",
        "schema": {
          "type": "string",
          "format": "uuid"
        },
        "examples": {
          "default": {
            "value": "75b10a04-1a2d-4f9e-9277-5f3c270f476e"
          }
        }
      },
      "DocumentUuidWithReferenceAlternative": {
        "name": "uuid",
        "in": "path",
        "required": true,
        "description": "UUID public du document. Vous pouvez aussi utiliser `/api/v1/documents/ref/{reference}` avec la même méthode. Exemple : `/api/v1/documents/ref/EXP-2026-004587`.",
        "schema": {
          "type": "string",
          "format": "uuid"
        },
        "examples": {
          "default": {
            "value": "75b10a04-1a2d-4f9e-9277-5f3c270f476e"
          }
        }
      },
      "DocumentStatusUuidWithReferenceAlternative": {
        "name": "uuid",
        "in": "path",
        "required": true,
        "description": "UUID public du document. Vous pouvez aussi utiliser `GET /api/v1/documents/ref/{reference}/status`. Exemple : `/api/v1/documents/ref/EXP-2026-004587/status`.",
        "schema": {
          "type": "string",
          "format": "uuid"
        },
        "examples": {
          "default": {
            "value": "75b10a04-1a2d-4f9e-9277-5f3c270f476e"
          }
        }
      }
    },
    "headers": {
      "NoStore": {
        "description": "La réponse contient des informations sensibles et ne doit pas être mise en cache.",
        "schema": {
          "type": "string",
          "examples": [
            "no-store, private"
          ]
        }
      }
    },
    "schemas": {
      "CountryCode": {
        "type": "string",
        "pattern": "^[A-Z]{2}$",
        "description": "Code pays à deux lettres ; normalisé en majuscules. Le format ne vérifie pas l'appartenance au catalogue ISO 3166-1.",
        "examples": [
          "ES"
        ]
      },
      "PositiveDecimal": {
        "type": "string",
        "pattern": "^(?!0+(?:\\.0+)?$)(?:0|[1-9][0-9]{0,14})(?:\\.[0-9]{1,6})?$",
        "description": "Décimal positif sous forme de string, avec un point, sans virgule ni notation scientifique. Jusqu'à 15 chiffres entiers et 6 décimales. La réponse supprime les zéros décimaux finaux.",
        "examples": [
          "1850.000"
        ]
      },
      "WeightKilograms": {
        "oneOf": [
          {
            "$ref": "#/components/schemas/PositiveDecimal"
          },
          {
            "type": "number",
            "exclusiveMinimum": 0,
            "exclusiveMaximum": 1000000000000000,
            "multipleOf": 0.000001
          }
        ],
        "description": "Poids positif en kg, sous forme de nombre JSON ou de chaîne décimale. Jusqu’à 15 chiffres entiers et 6 décimales. La réponse normalise en chaîne et conserve la précision ; le PDF affiche jusqu’à 3 décimales, avec une virgule décimale et des points pour les milliers. Utilisez une chaîne décimale pour éviter toute perte de précision avec les grandes valeurs.",
        "examples": [
          1850.5
        ]
      },
      "Address": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "street",
          "postal_code",
          "city",
          "country"
        ],
        "properties": {
          "street": {
            "type": "string",
            "minLength": 1,
            "description": "Voie et numéro de l'adresse.",
            "maxLength": 255
          },
          "postal_code": {
            "type": "string",
            "minLength": 1,
            "description": "Code postal sous forme de texte pour conserver les zéros initiaux.",
            "maxLength": 255
          },
          "city": {
            "type": "string",
            "minLength": 1,
            "description": "Localité de l'adresse.",
            "maxLength": 255
          },
          "country": {
            "$ref": "#/components/schemas/CountryCode"
          }
        }
      },
      "Shipper": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "name",
          "tax_country",
          "tax_id",
          "address"
        ],
        "description": "Chargeur contractuel ; peut différer de l'entrepôt de départ et du client DeCA.",
        "properties": {
          "name": {
            "type": "string",
            "minLength": 1,
            "description": "Nom ou raison sociale.",
            "maxLength": 255
          },
          "tax_country": {
            "$ref": "#/components/schemas/CountryCode"
          },
          "tax_id": {
            "type": "string",
            "minLength": 1,
            "description": "Identifiant fiscal normalisé en majuscules sans espaces ni traits d'union ; la longueur est vérifiée après normalisation. En Espagne, le format et le caractère de contrôle des DNI, NIE et NIF d'entités (CIF) sont validés. Les NIF spéciaux K, L et M sont vérifiés uniquement par format : préfixe, sept caractères alphanumériques et lettre finale. Les autres pays exigent un format alphanumérique sans vérification du contrôle fiscal. La validation ne prouve ni l'existence ni l'immatriculation fiscale.",
            "maxLength": 32
          },
          "address": {
            "$ref": "#/components/schemas/Address"
          }
        }
      },
      "Carrier": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "name",
          "tax_country",
          "tax_id"
        ],
        "description": "Transporteur effectuant réellement le transport, y compris le sous-traitant.",
        "properties": {
          "name": {
            "type": "string",
            "minLength": 1,
            "description": "Nom ou raison sociale.",
            "maxLength": 255
          },
          "tax_country": {
            "$ref": "#/components/schemas/CountryCode"
          },
          "tax_id": {
            "type": "string",
            "minLength": 1,
            "description": "Identifiant fiscal normalisé en majuscules sans espaces ni traits d'union ; la longueur est vérifiée après normalisation. En Espagne, le format et le caractère de contrôle des DNI, NIE et NIF d'entités (CIF) sont validés. Les NIF spéciaux K, L et M sont vérifiés uniquement par format : préfixe, sept caractères alphanumériques et lettre finale. Les autres pays exigent un format alphanumérique sans vérification du contrôle fiscal. La validation ne prouve ni l'existence ni l'immatriculation fiscale.",
            "maxLength": 32
          },
          "address": {
            "description": "Adresse facultative du transporteur. Si elle est fournie, tous les champs d'Address sont requis ; l'article 6 ne l'exige pas comme donnée minimale.",
            "type": "object",
            "additionalProperties": false,
            "required": [
              "street",
              "postal_code",
              "city",
              "country"
            ],
            "properties": {
              "street": {
                "type": "string",
                "minLength": 1,
                "description": "Voie et numéro de l'adresse.",
                "maxLength": 255
              },
              "postal_code": {
                "type": "string",
                "minLength": 1,
                "description": "Code postal sous forme de texte pour conserver les zéros initiaux.",
                "maxLength": 255
              },
              "city": {
                "type": "string",
                "minLength": 1,
                "description": "Localité de l'adresse.",
                "maxLength": 255
              },
              "country": {
                "$ref": "#/components/schemas/CountryCode"
              }
            }
          }
        }
      },
      "TransportLocation": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "place",
          "city",
          "country"
        ],
        "properties": {
          "place": {
            "type": "string",
            "minLength": 1,
            "description": "Lieu réel de chargement ou déchargement, identifiable par nom du site ou adresse. Avec city et country, il doit permettre de l'identifier clairement ; street et postal_code sont facultatifs. L'identité de l'entreprise destinataire n'est pas une donnée minimale obligatoire.",
            "maxLength": 255
          },
          "city": {
            "type": "string",
            "minLength": 1,
            "description": "Localité.",
            "maxLength": 255
          },
          "country": {
            "type": "string",
            "enum": [
              "ES"
            ],
            "description": "Le périmètre initial couvre les transports intérieurs en Espagne ; il ne remplace pas la CMR internationale."
          },
          "street": {
            "type": "string",
            "minLength": 1,
            "maxLength": 255,
            "description": "Voie et numéro facultatifs du lieu de chargement ou déchargement pour préciser sa localisation."
          },
          "postal_code": {
            "type": "string",
            "minLength": 1,
            "maxLength": 255,
            "description": "Code postal facultatif du lieu de chargement ou déchargement, sous forme de texte pour conserver les zéros initiaux."
          }
        }
      },
      "AlternativeMeasure": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "value",
          "unit"
        ],
        "description": "Autre grandeur lorsque le poids est difficile à déterminer. Envoyez cette grandeur ou le poids, jamais les deux.",
        "properties": {
          "value": {
            "$ref": "#/components/schemas/PositiveDecimal"
          },
          "unit": {
            "type": "string",
            "minLength": 1,
            "description": "Unité explicite de la grandeur, jusqu'à 255 caractères.",
            "maxLength": 255,
            "examples": [
              "palés"
            ]
          }
        }
      },
      "Goods": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "description"
        ],
        "description": "Nature et poids de la marchandise, ou grandeur alternative si nécessaire. Envoyez exactement une mesure.",
        "properties": {
          "description": {
            "type": "string",
            "minLength": 1,
            "description": "Description de la nature de la marchandise.",
            "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": "Véhicule effectif ; si une remorque ou semi-remorque est présente, renseignez ses deux champs ensemble.",
        "properties": {
          "country": {
            "$ref": "#/components/schemas/CountryCode"
          },
          "registration": {
            "type": "string",
            "minLength": 1,
            "description": "Immatriculation normalisée en majuscules sans espaces ni traits d'union. En Espagne, les immatriculations ordinaires actuelles, anciennes provinciales et la série spéciale E sont acceptées. Pour les autres pays : de 3 à 20 caractères alphanumériques.",
            "maxLength": 20,
            "examples": [
              "4827 MNL"
            ]
          },
          "trailer_country": {
            "$ref": "#/components/schemas/CountryCode"
          },
          "trailer_registration": {
            "type": "string",
            "minLength": 1,
            "description": "Immatriculation de la remorque ou semi-remorque, normalisée comme celle du tracteur. En Espagne, la série R est requise. Pour les autres pays : de 3 à 20 caractères alphanumériques.",
            "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": "Autorisation spéciale de circulation si nécessaire. Omettez-la ou envoyez null si elle ne s'applique pas.",
        "properties": {
          "number": {
            "type": "string",
            "minLength": 1,
            "description": "Numéro ou référence de l'autorisation.",
            "maxLength": 255
          },
          "issuer": {
            "type": "string",
            "minLength": 1,
            "description": "Organisme émetteur, s'il est renseigné.",
            "maxLength": 255
          }
        }
      },
      "CreateDocumentRequest": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "transport_date",
          "shipper",
          "carrier",
          "origin",
          "destination",
          "goods",
          "vehicle"
        ],
        "properties": {
          "reference": {
            "type": [
              "string",
              "null"
            ],
            "minLength": 1,
            "description": "Référence personnelle facultative, unique par organisation et environnement et imprimée sur le PDF. Identifie les nouvelles tentatives et permet la consultation par référence. N'est pas libérée lors de la suppression du document.",
            "pattern": "^[A-Za-z0-9-]+(?![\\s\\S])",
            "maxLength": 120
          },
          "transport_date": {
            "type": "string",
            "format": "date",
            "description": "Date réelle du transport au format YYYY-MM-DD ; ce n'est pas l'instant de création du PDF."
          },
          "shipper": {
            "$ref": "#/components/schemas/Shipper"
          },
          "carrier": {
            "$ref": "#/components/schemas/Carrier"
          },
          "origin": {
            "$ref": "#/components/schemas/TransportLocation"
          },
          "destination": {
            "$ref": "#/components/schemas/TransportLocation"
          },
          "goods": {
            "$ref": "#/components/schemas/Goods"
          },
          "vehicle": {
            "$ref": "#/components/schemas/Vehicle"
          },
          "special_authorization": {
            "$ref": "#/components/schemas/SpecialAuthorization"
          },
          "observations": {
            "type": [
              "string",
              "null"
            ],
            "description": "Observations que les intervenants demandent de faire figurer dans le document.",
            "maxLength": 4000
          },
          "metadata": {
            "type": [
              "object",
              "null"
            ],
            "additionalProperties": true,
            "description": "Objet JSON facultatif, conservé et renvoyé sans modification sémantique. Non imprimé sur le PDF. Maximum 16 KiB après sérialisation et 8 niveaux de profondeur."
          }
        }
      },
      "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 du document émis."
          },
          "reference": {
            "type": [
              "string",
              "null"
            ],
            "description": "Référence du client ou null.",
            "pattern": "^[A-Za-z0-9-]+(?![\\s\\S])"
          },
          "created_at": {
            "type": "string",
            "format": "date-time",
            "description": "Instant original de création, en UTC."
          },
          "url": {
            "type": "string",
            "format": "uri",
            "description": "URL HTTPS complète de téléchargement par QR, avec son secret. Ne pas enregistrer ni publier."
          },
          "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": "Octets de l'image PNG en Base64, sans préfixe data URI.",
                "contentEncoding": "base64"
              }
            }
          },
          "pdf_download_url": {
            "type": "string",
            "format": "uri",
            "description": "URL de téléchargement authentifié ; exige le Bearer de l'identifiant propriétaire dans les 24 heures."
          },
          "rectifies_uuid": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid",
            "description": "UUID rectifié ou null lors d'une émission initiale."
          },
          "normalized_data": {
            "$ref": "#/components/schemas/CreateDocumentRequest"
          },
          "metadata": {
            "type": [
              "object",
              "null"
            ],
            "additionalProperties": true,
            "description": "La même metadata que dans la requête ; null si elle n'a pas été fournie."
          },
          "document_locale": {
            "type": "string",
            "enum": [
              "es",
              "ca",
              "gl",
              "ca-valencia",
              "eu",
              "en",
              "pt-pt",
              "fr"
            ],
            "description": "Langue du PDF choisie à l'émission selon l'organisation. Conservée dans le résultat ; changer la langue de l'organisation ne modifie pas les documents existants. Ne change ni les champs JSON ni les messages d'erreur de l'API."
          }
        }
      },
      "DocumentSummary": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "uuid",
          "reference",
          "created_at",
          "transport_date",
          "rectifies_uuid"
        ],
        "properties": {
          "uuid": {
            "type": "string",
            "format": "uuid"
          },
          "reference": {
            "type": [
              "string",
              "null"
            ],
            "pattern": "^[A-Za-z0-9-]+(?![\\s\\S])"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "transport_date": {
            "type": "string",
            "format": "date"
          },
          "rectifies_uuid": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid"
          }
        }
      },
      "DocumentList": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "data",
          "meta"
        ],
        "properties": {
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/DocumentSummary"
            }
          },
          "meta": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "current_page",
              "limit",
              "total",
              "last_page"
            ],
            "properties": {
              "current_page": {
                "type": "integer",
                "minimum": 1
              },
              "limit": {
                "type": "integer",
                "minimum": 1,
                "maximum": 100
              },
              "total": {
                "type": "integer",
                "minimum": 0
              },
              "last_page": {
                "type": "integer",
                "minimum": 1
              }
            }
          }
        }
      },
      "ErrorResponse": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "errors"
        ],
        "properties": {
          "errors": {
            "type": "array",
            "minItems": 1,
            "description": "Toutes les erreurs indépendantes détectées ; sans données de requête, secrets ni traces.",
            "items": {
              "type": "object",
              "additionalProperties": false,
              "required": [
                "field",
                "code",
                "message"
              ],
              "properties": {
                "field": {
                  "type": [
                    "string",
                    "null"
                  ],
                  "description": "Chemin du champ séparé par des points ; null pour une erreur générale."
                },
                "code": {
                  "type": "string",
                  "pattern": "^[A-Z][A-Z0-9_]*$",
                  "description": "Code stable pour la logique d'intégration."
                },
                "message": {
                  "type": "string",
                  "description": "Message bref en anglais. Ne pas l'utiliser comme identifiant de l'erreur."
                }
              }
            }
          }
        }
      },
      "RectifyDocumentRequest": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "transport_date",
          "shipper",
          "carrier",
          "origin",
          "destination",
          "goods",
          "vehicle"
        ],
        "properties": {
          "reference": {
            "type": [
              "string",
              "null"
            ],
            "minLength": 1,
            "description": "Nouvelle référence du document rectificatif, facultative et unique par organisation et environnement. Ne réutilisez pas la référence du document identifié dans l'URL.",
            "pattern": "^[A-Za-z0-9-]+(?![\\s\\S])",
            "maxLength": 120
          },
          "transport_date": {
            "type": "string",
            "format": "date",
            "description": "Date réelle du transport au format YYYY-MM-DD ; ce n'est pas l'instant de création du PDF."
          },
          "shipper": {
            "$ref": "#/components/schemas/Shipper"
          },
          "carrier": {
            "$ref": "#/components/schemas/Carrier"
          },
          "origin": {
            "$ref": "#/components/schemas/TransportLocation"
          },
          "destination": {
            "$ref": "#/components/schemas/TransportLocation"
          },
          "goods": {
            "$ref": "#/components/schemas/Goods"
          },
          "vehicle": {
            "$ref": "#/components/schemas/Vehicle"
          },
          "special_authorization": {
            "$ref": "#/components/schemas/SpecialAuthorization"
          },
          "observations": {
            "type": [
              "string",
              "null"
            ],
            "description": "Observations que les intervenants demandent de faire figurer dans le document.",
            "maxLength": 4000
          },
          "metadata": {
            "type": [
              "object",
              "null"
            ],
            "additionalProperties": true,
            "description": "Objet JSON facultatif, conservé et renvoyé sans modification sémantique. Non imprimé sur le PDF. Maximum 16 KiB après sérialisation et 8 niveaux de profondeur."
          }
        },
        "description": "Toutes les données corrigées du transport. Le document précédent est identifié dans l'URL. Si reference est fourni, il doit s'agir d'une nouvelle référence pour le document rectificatif."
      },
      "DeleteDocumentRequest": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "reason",
          "confirm_not_delivered",
          "confirm_not_used"
        ],
        "properties": {
          "reason": {
            "type": "string",
            "minLength": 1,
            "description": "Motif de l'erreur détectée lors de la génération du document.",
            "maxLength": 2000
          },
          "confirm_not_delivered": {
            "type": "boolean",
            "description": "Doit être true pour supprimer : confirme l'absence de remise au transporteur ou au conducteur. false renvoie 409 DOCUMENT_ALREADY_DELIVERED."
          },
          "confirm_not_used": {
            "type": "boolean",
            "description": "Doit être true pour supprimer : confirme l'absence d'utilisation pour un transport. false renvoie 409 DOCUMENT_ALREADY_USED."
          }
        }
      },
      "DocumentStatus": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "status"
        ],
        "properties": {
          "status": {
            "type": "string",
            "enum": [
              "active",
              "deleted"
            ],
            "description": "État du document. N'inclut ni données de transport, PDF, QR, metadata ni 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": "Vous pouvez demander un jeton `sk_demo_` sans compte ni organisation enregistrée. Il dure **24 heures à partir de son émission** et fonctionne uniquement depuis **l’IP publique qui l’a demandé**. Un changement de réseau ou d’IP nécessite un autre jeton."
          },
          "expires_at": {
            "type": "string",
            "format": "date-time",
            "description": "Date d’expiration du jeton en UTC. Tous ses documents et liens expirent à cet instant."
          },
          "document_locale": {
            "type": "string",
            "enum": [
              "es",
              "ca",
              "gl",
              "ca-valencia",
              "eu",
              "en",
              "pt-pt",
              "fr"
            ],
            "description": "Langue des PDF de ce jeton. Espagnol par défaut."
          }
        }
      },
      "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 du document émis."
          },
          "reference": {
            "type": [
              "string",
              "null"
            ],
            "description": "Référence du client ou null.",
            "pattern": "^[A-Za-z0-9-]+(?![\\s\\S])"
          },
          "created_at": {
            "type": "string",
            "format": "date-time",
            "description": "Instant original de création, en UTC."
          },
          "url": {
            "type": "string",
            "format": "uri",
            "description": "URL HTTPS complète de téléchargement par QR, avec son secret. Ne pas enregistrer ni publier."
          },
          "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": "Octets de l'image PNG en Base64, sans préfixe data URI.",
                "contentEncoding": "base64"
              }
            }
          },
          "pdf_download_url": {
            "type": "string",
            "format": "uri",
            "description": "Vous pouvez demander un jeton `sk_demo_` sans compte ni organisation enregistrée. Il dure **24 heures à partir de son émission** et fonctionne uniquement depuis **l’IP publique qui l’a demandé**. Un changement de réseau ou d’IP nécessite un autre jeton."
          },
          "rectifies_uuid": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid",
            "description": "UUID rectifié ou null lors d'une émission initiale."
          },
          "normalized_data": {
            "$ref": "#/components/schemas/CreateDocumentRequest"
          },
          "metadata": {
            "type": [
              "object",
              "null"
            ],
            "additionalProperties": true,
            "description": "La même metadata que dans la requête ; null si elle n'a pas été fournie."
          },
          "document_locale": {
            "type": "string",
            "enum": [
              "es",
              "ca",
              "gl",
              "ca-valencia",
              "eu",
              "en",
              "pt-pt",
              "fr"
            ],
            "description": "Langue des PDF de ce jeton. Espagnol par défaut."
          },
          "demo": {
            "type": "boolean",
            "enum": [
              true
            ],
            "description": "Uniquement en démo ; toujours true. Absent des résultats de développement et de production."
          },
          "expires_at": {
            "type": "string",
            "format": "date-time",
            "description": "Date d’expiration du jeton en UTC. Tous ses documents et liens expirent à cet instant."
          },
          "qr_url": {
            "type": "string",
            "format": "uri",
            "description": "Uniquement en démo ; alias de url."
          },
          "pdf_url": {
            "type": "string",
            "format": "uri",
            "description": "Uniquement en démo ; alias de pdf_download_url. Nécessite le jeton demo et son IP."
          }
        },
        "description": "La démo ajoute demo: true, expires_at, qr_url (alias de url) et pdf_url (alias de pdf_download_url). document_locale est la langue choisie lors de la demande du jeton (es par défaut). Les données et PDF temporaires sont distincts de l’archive réelle et ne sont pas chiffrés comme les documents réels."
      }
    },
    "examples": {
      "DocumentResult": {
        "summary": "Résultat complet avec données fictives et QR illustratif",
        "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": "Document rectificatif avec de nouveaux UUID, PDF et 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": "PDF original natif, jusqu'à 5 MB. Téléchargement direct sans régénération.",
        "headers": {
          "Content-Disposition": {
            "description": "Nom sûr ne contenant pas de données du client.",
            "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": "Éviter la propagation de l'URL secrète."
          },
          "X-Content-Type-Options": {
            "schema": {
              "type": "string",
              "enum": [
                "nosniff"
              ]
            },
            "description": "Empêcher l'interprétation du contenu comme un autre type."
          }
        },
        "content": {
          "application/pdf": {
            "schema": {
              "type": "string",
              "contentMediaType": "application/octet-stream"
            }
          }
        }
      },
      "PdfPreview": {
        "description": "PDF original natif, jusqu'à 5 MB. Affichage dans le navigateur sans régénération.",
        "headers": {
          "Content-Disposition": {
            "description": "Nom sûr ne contenant pas de données du client.",
            "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": "Éviter la propagation de l'URL secrète."
          },
          "X-Content-Type-Options": {
            "schema": {
              "type": "string",
              "enum": [
                "nosniff"
              ]
            },
            "description": "Empêcher l'interprétation du contenu comme un autre type."
          }
        },
        "content": {
          "application/pdf": {
            "schema": {
              "type": "string",
              "contentMediaType": "application/octet-stream"
            }
          }
        }
      },
      "BadRequest": {
        "description": "JSON mal formé ; la requête n'est pas traitée.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ErrorResponse"
            },
            "examples": {
              "default": {
                "value": {
                  "errors": [
                    {
                      "field": null,
                      "code": "INVALID_JSON",
                      "message": "Request body must contain valid JSON."
                    }
                  ]
                }
              }
            }
          }
        }
      },
      "Unauthorized": {
        "description": "Bearer absent, invalide ou révoqué.\n\nUn jeton demo expiré ou envoyé depuis une autre IP renvoie `401`. Un QR demo expiré renvoie `404`. Lorsque la démo est désactivée, elle renvoie `503 DEMO_UNAVAILABLE`. Le premier accès au QR ou une rectification empêche la suppression du document ; les confirmations de suppression du contrat habituel restent requises.",
        "headers": {
          "WWW-Authenticate": {
            "schema": {
              "type": "string",
              "examples": [
                "Bearer"
              ]
            },
            "description": "Schéma d'authentification requis."
          }
        },
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ErrorResponse"
            },
            "examples": {
              "default": {
                "value": {
                  "errors": [
                    {
                      "field": null,
                      "code": "UNAUTHORIZED",
                      "message": "Valid API credentials are required."
                    }
                  ]
                }
              }
            }
          }
        }
      },
      "Forbidden": {
        "description": "IP non autorisée ou action non permise pour l'identifiant.",
        "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 indisponible pour cet accès ; ne révèle ni existence, propriétaire ni motif.",
        "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": "Les requêtes POST, PUT et DELETE avec un corps exigent 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": "Champs invalides ; renvoie toutes les erreurs indépendantes détectées. Les huit langues traduisent les titres et libellés du PDF, pas les noms, adresses, observations ni autres données transmises par l'intégration. Les textes imprimés acceptent les caractères latins, grecs et cyrilliques compatibles avec les polices ; emoji, CJK et écritures de droite à gauche renvoient 422 UNSUPPORTED_CHARACTERS pour le champ concerné. metadata conserve tout Unicode et n'est pas imprimée.",
        "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": "Limite atteinte : 60 requêtes par minute et jeton pour /api/v1, ou 30 requêtes par minute pour la même IP et le même document en téléchargement par QR.\n\nUne limite de prévention des abus ou de capacité temporaire de la démonstration a été atteinte ; respectez Retry-After. La démo accepte des corps de requête jusqu’à 32 KiB et dix documents par session. L’émission de jetons permet trois par minute et dix par heure depuis la même IP ; la création de documents permet cinq par minute et vingt par heure depuis la même IP. Les limites partagées par réseau, région et charge globale peuvent s’appliquer plus tôt. Ces valeurs correspondent à la configuration par défaut.",
        "headers": {
          "Retry-After": {
            "description": "Secondes à attendre avant la prochaine tentative.",
            "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": "Erreur interne sans détails sensibles. Une émission échouée ne laisse pas de document partiel.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ErrorResponse"
            },
            "examples": {
              "default": {
                "value": {
                  "errors": [
                    {
                      "field": null,
                      "code": "INTERNAL_ERROR",
                      "message": "An internal error occurred."
                    }
                  ]
                }
              }
            }
          }
        }
      },
      "RequestTooLarge": {
        "description": "Le corps dépasse la limite de 128 KiB.\n\nLa démo accepte des corps de requête jusqu’à 32 KiB et dix documents par session. L’émission de jetons permet trois par minute et dix par heure depuis la même IP ; la création de documents permet cinq par minute et vingt par heure depuis la même IP. Les limites partagées par réseau, région et charge globale peuvent s’appliquer plus tôt. Ces valeurs correspondent à la configuration par défaut.",
        "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 indisponible car l'URL HTTPS publique est mal configurée. Une émission échouée ne conserve aucun document partiel.\n\nLa démonstration est désactivée ou indisponible. DEMO_UNAVAILABLE.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ErrorResponse"
            },
            "examples": {
              "default": {
                "value": {
                  "errors": [
                    {
                      "field": null,
                      "code": "SERVICE_UNAVAILABLE",
                      "message": "The document service requires a configured HTTPS public URL."
                    }
                  ]
                }
              }
            }
          }
        }
      },
      "DemoUnavailable": {
        "description": "La démonstration est désactivée ou indisponible.",
        "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": "Une limite de prévention des abus ou de capacité temporaire de la démonstration a été atteinte ; respectez Retry-After.",
        "headers": {
          "Retry-After": {
            "description": "Secondes à attendre avant la prochaine tentative.",
            "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."
                    }
                  ]
                }
              }
            }
          }
        }
      }
    }
  }
}
