{
  "openapi": "3.1.0",
  "jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
  "info": {
    "title": "Kevmi — API serveur",
    "version": "2026-10-23.12",
    "description": "API Kevmi : connexion Telegram, sessions, factures marchand, paiement acheteur chiffré, assistance, notifications, applications et livraison webhook. Paiements directs ponctuels uniquement. Les réponses JSON utilisent no-store. Les horodatages sont en millisecondes Unix UTC sauf indication contraire. Le paiement en direct utilise une passerelle externe configurée ; les champs de carte et de code bancaire sont chiffrés dans le navigateur et ne sont déchiffrés que par la passerelle. Les nouveaux débits nécessitent l'approbation de la dernière application de site marchand. La création d'une facture ne débite pas l'acheteur.",
    "x-updated-at": "2026-10-23"
  },
  "servers": [
    {
      "url": "https://kevmi.one",
      "description": "L'origine publique du projet ; la plateforme d'hébergement peut appliquer des contrôles d'accès supplémentaires."
    }
  ],
  "externalDocs": {
    "description": "Documentation publique du produit",
    "url": "https://kevmi.one/docs"
  },
  "tags": [
    {
      "name": "Service",
      "description": "État du service public."
    },
    {
      "name": "Auth",
      "description": "L'inscription et la connexion partagent le flux Telegram vérifié."
    },
    {
      "name": "Account",
      "description": "Paramètres de l'utilisateur actuel uniquement."
    },
    {
      "name": "Payment links",
      "description": "Factures serveur pour le magasin actuel. Le prix, la devise, l'assujetti à la TVA, le nom du vendeur et le calcul sont immuables."
    },
    {
      "name": "Checkout",
      "description": "Lire une facture sans compte. La disponibilité du paiement est vérifiée séparément."
    },
    {
      "name": "Support",
      "description": "Base de connaissances publique et demandes d'assistance de l'utilisateur actuel."
    },
    {
      "name": "Notifications",
      "description": "Centre d'événements du compte, état de lecture du site et état de livraison Telegram indépendant."
    },
    {
      "name": "Telegram",
      "description": "Webhook de bot ; pas un webhook de paiement."
    },
    {
      "name": "Disabled payments",
      "description": "Routes monétaires héritées non prises en charge et exécution des versements. Le flux de paiement public a ses propres points de terminaison documentés."
    },
    {
      "name": "Merchant API",
      "description": "Factures serveur-à-serveur limitées et historique d'événements immuable."
    },
    {
      "name": "Webhooks",
      "description": "Points de terminaison HTTPS vérifiés, événements signés, tentatives de livraison et nouvelles tentatives."
    },
    {
      "name": "Applications",
      "description": "Applications de site marchand authentifiées et suivi individuel."
    },
    {
      "name": "Moderation",
      "description": "Modérateurs Telegram sur liste blanche ; chaque décision est vérifiée par version."
    },
    {
      "name": "Gateway events",
      "description": "Les événements de passerelle natifs à la source sont vérifiés, puis le paiement faisant autorité est lu via un transport serveur authentifié. Les webhooks destinés aux marchands utilisent une signature et une file de livraison distinctes."
    }
  ],
  "security": [],
  "x-disabled-route-prefixes": [
    "/api/payments",
    "/api/payouts",
    "/api/gateway",
    "/api/linzapay"
  ],
  "x-disabled-route-behavior": "Legacy unscoped direct-money routes and payout execution remain disabled. Authenticated /api/v1/payments creates invoices; /api/checkout/{token} exposes the separate buyer checkout flow. POST /api/gateway/webhook accepts signed gateway events.",
  "paths": {
    "/api/config": {
      "get": {
        "tags": [
          "Service"
        ],
        "operationId": "getConfig",
        "summary": "Préparation d'intégration actuelle",
        "responses": {
          "200": {
            "description": "Configuration sans secrets.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Config"
                }
              }
            }
          },
          "503": {
            "description": "Une intégration ou un stockage est indisponible, un événement est en cours de traitement, ou le service est indisponible.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "DATABASE_NOT_CONFIGURED",
              "ORIGIN_NOT_CONFIGURED",
              "TELEGRAM_NOT_CONFIGURED",
              "UPDATE_IN_PROGRESS",
              "SERVICE_UNAVAILABLE",
              "GATEWAY_NOT_CONNECTED"
            ]
          }
        }
      }
    },
    "/api/health": {
      "get": {
        "tags": [
          "Service"
        ],
        "operationId": "getHealth",
        "summary": "État de l'API et des tables D1",
        "description": "not_configured peut avoir status=ok : les pages publiques et la recherche peuvent fonctionner sans base de données. Une base de données configurée avec des tables indisponibles renvoie 503/dégradé. Cette réponse ne teste pas la connectivité réseau Telegram ou modèle.",
        "responses": {
          "200": {
            "description": "Le stockage est prêt ou not_configured.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Health"
                }
              }
            }
          },
          "503": {
            "description": "D1 est indisponible ou une erreur de service inattendue s'est produite.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "oneOf": [
                    {
                      "$ref": "#/components/schemas/Health"
                    },
                    {
                      "$ref": "#/components/schemas/Error"
                    }
                  ]
                }
              }
            }
          }
        }
      }
    },
    "/api/auth/me": {
      "get": {
        "tags": [
          "Auth"
        ],
        "operationId": "getCurrentSession",
        "summary": "Session actuelle et CSRF",
        "security": [
          {},
          {
            "cookieSession": []
          }
        ],
        "responses": {
          "200": {
            "description": "authenticated=false lorsqu'aucune session valide n'existe.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "oneOf": [
                    {
                      "$ref": "#/components/schemas/Anonymous"
                    },
                    {
                      "$ref": "#/components/schemas/CurrentSession"
                    }
                  ]
                }
              }
            }
          },
          "503": {
            "description": "Une intégration ou un stockage est indisponible, un événement est en cours de traitement, ou le service est indisponible.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "DATABASE_NOT_CONFIGURED",
              "ORIGIN_NOT_CONFIGURED",
              "TELEGRAM_NOT_CONFIGURED",
              "UPDATE_IN_PROGRESS",
              "SERVICE_UNAVAILABLE",
              "GATEWAY_NOT_CONNECTED"
            ]
          }
        }
      }
    },
    "/api/auth/telegram/start": {
      "post": {
        "tags": [
          "Auth"
        ],
        "operationId": "startTelegramLogin",
        "summary": "Démarrer la connexion ou l'inscription",
        "description": "Crée un défi de 300 secondes et un cookie de preuve de navigateur HttpOnly ; au maximum six démarrages par minute par adresse hachée. Comparez le code à six chiffres sur le site et dans le chat privé du bot.",
        "parameters": [
          {
            "name": "Origin",
            "in": "header",
            "required": true,
            "description": "Doit correspondre exactement à APP_ORIGIN. Sec-Fetch-Site : cross-site est rejeté. Le serveur ne fournit pas d'accès CORS.",
            "schema": {
              "type": "string",
              "format": "uri"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "description": "Objet JSON. Maximum 16 384 octets. Les champs inconnus sont rejetés.",
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/EmptyObject"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Demande de connexion créée ; le cookie de preuve la lie au navigateur d'origine.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              },
              "Set-Cookie": {
                "schema": {
                  "type": "string"
                },
                "description": "Secure ; HttpOnly ; SameSite=Lax ; Path=/. JavaScript ne peut pas accéder à la valeur de session. Terminer la connexion efface également le cookie de preuve du navigateur."
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/LoginChallenge"
                }
              }
            }
          },
          "400": {
            "description": "JSON invalide, champ inconnu ou valeur invalide.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "INVALID_JSON",
              "UNKNOWN_FIELD",
              "INVALID_INPUT",
              "INVALID_VAT_PAYER",
              "INVALID_NOTIFICATIONS",
              "INVALID_TICKET_STATUS",
              "INVALID_UPDATE",
              "INVALID_HISTORY",
              "INVALID_PAGE_CONTEXT",
              "INVALID_LOCALE",
              "INVALID_FILTER",
              "INVALID_NOTIFICATION_SELECTION",
              "INVALID_AMOUNT",
              "INVALID_CURRENCY",
              "INVALID_METHODS",
              "INVALID_EXPIRY",
              "INVALID_QUOTE",
              "INVALID_LIMIT",
              "INVALID_CURSOR",
              "INVALID_STATUS",
              "IDEMPOTENCY_KEY_REQUIRED",
              "METHOD_UNAVAILABLE"
            ]
          },
          "403": {
            "description": "La validation d'origine, CSRF ou de liaison navigateur a échoué.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "INVALID_ORIGIN",
              "CSRF_REQUIRED",
              "BROWSER_PROOF_REQUIRED",
              "INVALID_CHALLENGE"
            ]
          },
          "413": {
            "description": "Le corps dépasse 16 384 octets ; la limite est de 65 536 octets pour /api/support/ask et le webhook Telegram.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "REQUEST_TOO_LARGE"
            ]
          },
          "415": {
            "description": "Content-Type : application/json est requis.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "JSON_REQUIRED"
            ]
          },
          "429": {
            "description": "Limite de débit dépassée.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              },
              "Retry-After": {
                "schema": {
                  "type": "string",
                  "const": "60"
                },
                "description": "Le serveur renvoie toujours 60 ; cela ne garantit pas qu'un quota horaire a expiré."
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "RATE_LIMITED"
            ]
          },
          "503": {
            "description": "Une intégration ou un stockage est indisponible, un événement est en cours de traitement, ou le service est indisponible.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "DATABASE_NOT_CONFIGURED",
              "ORIGIN_NOT_CONFIGURED",
              "TELEGRAM_NOT_CONFIGURED",
              "UPDATE_IN_PROGRESS",
              "SERVICE_UNAVAILABLE",
              "GATEWAY_NOT_CONNECTED"
            ]
          }
        }
      }
    },
    "/api/auth/telegram/complete": {
      "post": {
        "tags": [
          "Auth"
        ],
        "operationId": "completeTelegramLogin",
        "summary": "Terminer une connexion vérifiée",
        "description": "Nécessite le cookie de preuve du début et le même navigateur. En attente renvoie 202. Après confirmation du bot, la demande émet une session de 24 heures. Jusqu'à 90 vérifications par minute par défi. Un identifiant Telegram fourni par le client n'est pas accepté.",
        "security": [
          {
            "browserProof": []
          }
        ],
        "parameters": [
          {
            "name": "Origin",
            "in": "header",
            "required": true,
            "description": "Doit correspondre exactement à APP_ORIGIN. Sec-Fetch-Site : cross-site est rejeté. Le serveur ne fournit pas d'accès CORS.",
            "schema": {
              "type": "string",
              "format": "uri"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "description": "Objet JSON. Maximum 16 384 octets. Les champs inconnus sont rejetés.",
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "challengeId": {
                    "type": "string",
                    "pattern": "^[A-Za-z0-9_-]{43}$"
                  }
                },
                "required": [
                  "challengeId"
                ],
                "additionalProperties": false
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Compte créé ou mis à jour et session émise.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              },
              "Set-Cookie": {
                "schema": {
                  "type": "string"
                },
                "description": "Secure ; HttpOnly ; SameSite=Lax ; Path=/. JavaScript ne peut pas accéder à la valeur de session. Terminer la connexion efface également le cookie de preuve du navigateur."
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CompletedLogin"
                }
              }
            }
          },
          "202": {
            "description": "En attente de confirmation dans Telegram.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "status": {
                      "type": "string",
                      "const": "pending"
                    }
                  },
                  "required": [
                    "status"
                  ],
                  "additionalProperties": false
                }
              }
            }
          },
          "400": {
            "description": "JSON invalide, champ inconnu ou valeur invalide.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "INVALID_JSON",
              "UNKNOWN_FIELD",
              "INVALID_INPUT",
              "INVALID_VAT_PAYER",
              "INVALID_NOTIFICATIONS",
              "INVALID_TICKET_STATUS",
              "INVALID_UPDATE",
              "INVALID_HISTORY",
              "INVALID_PAGE_CONTEXT",
              "INVALID_LOCALE",
              "INVALID_FILTER",
              "INVALID_NOTIFICATION_SELECTION",
              "INVALID_AMOUNT",
              "INVALID_CURRENCY",
              "INVALID_METHODS",
              "INVALID_EXPIRY",
              "INVALID_QUOTE",
              "INVALID_LIMIT",
              "INVALID_CURSOR",
              "INVALID_STATUS",
              "IDEMPOTENCY_KEY_REQUIRED",
              "METHOD_UNAVAILABLE"
            ]
          },
          "403": {
            "description": "La validation d'origine, CSRF ou de liaison navigateur a échoué.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "INVALID_ORIGIN",
              "CSRF_REQUIRED",
              "BROWSER_PROOF_REQUIRED",
              "INVALID_CHALLENGE"
            ]
          },
          "409": {
            "description": "La demande de connexion est utilisée ou annulée, l'identité n'est pas confirmée, ou une demande d'assistance entre en conflit.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "CHALLENGE_UNAVAILABLE",
              "IDENTITY_INCOMPLETE",
              "TICKET_CONFLICT",
              "IDEMPOTENCY_CONFLICT",
              "LINK_CANCELLED",
              "LINK_EXPIRED",
              "LINK_UNAVAILABLE"
            ]
          },
          "410": {
            "description": "La demande de connexion de cinq minutes a expiré.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "CHALLENGE_EXPIRED"
            ]
          },
          "413": {
            "description": "Le corps dépasse 16 384 octets ; la limite est de 65 536 octets pour /api/support/ask et le webhook Telegram.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "REQUEST_TOO_LARGE"
            ]
          },
          "415": {
            "description": "Content-Type : application/json est requis.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "JSON_REQUIRED"
            ]
          },
          "429": {
            "description": "Limite de débit dépassée.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              },
              "Retry-After": {
                "schema": {
                  "type": "string",
                  "const": "60"
                },
                "description": "Le serveur renvoie toujours 60 ; cela ne garantit pas qu'un quota horaire a expiré."
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "RATE_LIMITED"
            ]
          },
          "503": {
            "description": "Une intégration ou un stockage est indisponible, un événement est en cours de traitement, ou le service est indisponible.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "DATABASE_NOT_CONFIGURED",
              "ORIGIN_NOT_CONFIGURED",
              "TELEGRAM_NOT_CONFIGURED",
              "UPDATE_IN_PROGRESS",
              "SERVICE_UNAVAILABLE",
              "GATEWAY_NOT_CONNECTED"
            ]
          }
        }
      }
    },
    "/api/auth/logout": {
      "post": {
        "tags": [
          "Auth"
        ],
        "operationId": "logoutCurrentSession",
        "summary": "Terminer la session actuelle",
        "description": "Aucun corps n'est requis ou lu. La commande /logout du bot termine séparément toutes les sessions du compte.",
        "security": [
          {
            "cookieSession": [],
            "csrfToken": []
          }
        ],
        "parameters": [
          {
            "name": "Origin",
            "in": "header",
            "required": true,
            "description": "Doit correspondre exactement à APP_ORIGIN. Sec-Fetch-Site : cross-site est rejeté. Le serveur ne fournit pas d'accès CORS.",
            "schema": {
              "type": "string",
              "format": "uri"
            }
          },
          {
            "name": "X-CSRF-Token",
            "in": "header",
            "required": true,
            "description": "Jeton de session actuel depuis GET /api/auth/me ou achèvement réussi de la connexion.",
            "schema": {
              "type": "string",
              "pattern": "^[A-Za-z0-9_-]{43}$"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Session supprimée et cookie effacé.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              },
              "Set-Cookie": {
                "schema": {
                  "type": "string"
                },
                "description": "Secure ; HttpOnly ; SameSite=Lax ; Path=/. JavaScript ne peut pas accéder à la valeur de session. Terminer la connexion efface également le cookie de preuve du navigateur."
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "loggedOut": {
                      "type": "boolean",
                      "const": true
                    }
                  },
                  "required": [
                    "loggedOut"
                  ],
                  "additionalProperties": false
                }
              }
            }
          },
          "401": {
            "description": "Aucune session valide ; pour un webhook, un secret invalide.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "AUTH_REQUIRED",
              "SESSION_EXPIRED",
              "WEBHOOK_UNAUTHORIZED"
            ]
          },
          "403": {
            "description": "La validation d'origine, CSRF ou de liaison navigateur a échoué.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "INVALID_ORIGIN",
              "CSRF_REQUIRED",
              "BROWSER_PROOF_REQUIRED",
              "INVALID_CHALLENGE"
            ]
          },
          "503": {
            "description": "Une intégration ou un stockage est indisponible, un événement est en cours de traitement, ou le service est indisponible.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "DATABASE_NOT_CONFIGURED",
              "ORIGIN_NOT_CONFIGURED",
              "TELEGRAM_NOT_CONFIGURED",
              "UPDATE_IN_PROGRESS",
              "SERVICE_UNAVAILABLE",
              "GATEWAY_NOT_CONNECTED"
            ]
          }
        }
      }
    },
    "/api/account/settings": {
      "get": {
        "tags": [
          "Account"
        ],
        "operationId": "getAccountSettings",
        "summary": "Paramètres de l'utilisateur actuel",
        "security": [
          {
            "cookieSession": []
          }
        ],
        "responses": {
          "200": {
            "description": "Champs publics du profil de l'utilisateur actuel.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/UserResponse"
                }
              }
            }
          },
          "401": {
            "description": "Aucune session valide ; pour un webhook, un secret invalide.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "AUTH_REQUIRED",
              "SESSION_EXPIRED",
              "WEBHOOK_UNAUTHORIZED"
            ]
          },
          "503": {
            "description": "Une intégration ou un stockage est indisponible, un événement est en cours de traitement, ou le service est indisponible.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "DATABASE_NOT_CONFIGURED",
              "ORIGIN_NOT_CONFIGURED",
              "TELEGRAM_NOT_CONFIGURED",
              "UPDATE_IN_PROGRESS",
              "SERVICE_UNAVAILABLE",
              "GATEWAY_NOT_CONNECTED"
            ]
          }
        },
        "parameters": [
          {
            "name": "X-Kevmi-Store",
            "in": "header",
            "required": false,
            "schema": {
              "type": "string",
              "pattern": "^sto_[A-Za-z0-9_-]+$"
            },
            "description": "Sélectionnez une boutique possédée pour une session de navigateur. Omett pour utiliser la première boutique. Les clés API ignorent cet en-tête et utilisent toujours leur liaison de boutique immuable. La valeur all est en lecture seule et n'est prise en charge que par le tableau de bord et les points de terminaison de liste de factures."
          }
        ]
      },
      "patch": {
        "tags": [
          "Account"
        ],
        "operationId": "updateAccountSettings",
        "summary": "Modifier le nom du magasin, l'assujetti à la TVA ou les notifications",
        "description": "Tous les champs sont facultatifs : correctif partiel. Enregistrer vatPayer ne crée pas d'opération financière. Les interrupteurs paiements et versements contrôlent les préférences, pas la disponibilité de la passerelle.",
        "security": [
          {
            "cookieSession": [],
            "csrfToken": []
          }
        ],
        "parameters": [
          {
            "name": "Origin",
            "in": "header",
            "required": true,
            "description": "Doit correspondre exactement à APP_ORIGIN. Sec-Fetch-Site : cross-site est rejeté. Le serveur ne fournit pas d'accès CORS.",
            "schema": {
              "type": "string",
              "format": "uri"
            }
          },
          {
            "name": "X-CSRF-Token",
            "in": "header",
            "required": true,
            "description": "Jeton de session actuel depuis GET /api/auth/me ou achèvement réussi de la connexion.",
            "schema": {
              "type": "string",
              "pattern": "^[A-Za-z0-9_-]{43}$"
            }
          },
          {
            "name": "X-Kevmi-Store",
            "in": "header",
            "required": false,
            "schema": {
              "type": "string",
              "pattern": "^sto_[A-Za-z0-9_-]+$"
            },
            "description": "Sélectionnez une boutique possédée pour une session de navigateur. Omett pour utiliser la première boutique. Les clés API ignorent cet en-tête et utilisent toujours leur liaison de boutique immuable. La valeur all est en lecture seule et n'est prise en charge que par le tableau de bord et les points de terminaison de liste de factures."
          }
        ],
        "requestBody": {
          "required": true,
          "description": "Objet JSON. Maximum 16 384 octets. Les champs inconnus sont rejetés.",
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/SettingsPatch"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Profil mis à jour.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/UserResponse"
                }
              }
            }
          },
          "400": {
            "description": "JSON invalide, champ inconnu ou valeur invalide.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "INVALID_JSON",
              "UNKNOWN_FIELD",
              "INVALID_INPUT",
              "INVALID_VAT_PAYER",
              "INVALID_NOTIFICATIONS",
              "INVALID_TICKET_STATUS",
              "INVALID_UPDATE",
              "INVALID_HISTORY",
              "INVALID_PAGE_CONTEXT",
              "INVALID_LOCALE",
              "INVALID_FILTER",
              "INVALID_NOTIFICATION_SELECTION",
              "INVALID_AMOUNT",
              "INVALID_CURRENCY",
              "INVALID_METHODS",
              "INVALID_EXPIRY",
              "INVALID_QUOTE",
              "INVALID_LIMIT",
              "INVALID_CURSOR",
              "INVALID_STATUS",
              "IDEMPOTENCY_KEY_REQUIRED",
              "METHOD_UNAVAILABLE"
            ]
          },
          "401": {
            "description": "Aucune session valide ; pour un webhook, un secret invalide.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "AUTH_REQUIRED",
              "SESSION_EXPIRED",
              "WEBHOOK_UNAUTHORIZED"
            ]
          },
          "403": {
            "description": "La validation d'origine, CSRF ou de liaison navigateur a échoué.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "INVALID_ORIGIN",
              "CSRF_REQUIRED",
              "BROWSER_PROOF_REQUIRED",
              "INVALID_CHALLENGE"
            ]
          },
          "413": {
            "description": "Le corps dépasse 16 384 octets ; la limite est de 65 536 octets pour /api/support/ask et le webhook Telegram.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "REQUEST_TOO_LARGE"
            ]
          },
          "415": {
            "description": "Content-Type : application/json est requis.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "JSON_REQUIRED"
            ]
          },
          "503": {
            "description": "Une intégration ou un stockage est indisponible, un événement est en cours de traitement, ou le service est indisponible.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "DATABASE_NOT_CONFIGURED",
              "ORIGIN_NOT_CONFIGURED",
              "TELEGRAM_NOT_CONFIGURED",
              "UPDATE_IN_PROGRESS",
              "SERVICE_UNAVAILABLE",
              "GATEWAY_NOT_CONNECTED"
            ]
          }
        }
      }
    },
    "/api/support/ask": {
      "post": {
        "tags": [
          "Support"
        ],
        "operationId": "askDocumentation",
        "summary": "Réponse de la base de connaissances publique complète",
        "description": "Question publique sans session ni CSRF. Une Origin fournie doit correspondre à APP_ORIGIN ; les agents peuvent l'omettre. En mode IA, le serveur envoie tous les articles publiés, faits, catalogue de pages et état de préparation des fonctionnalités. L'historique facultatif et le pageContext sont un contexte de question non fiable. Le modèle ne peut pas accéder aux comptes, à l'argent, aux demandes privées ni aux outils. Avec D1, la limite est de 12 questions par minute par adresse hachée. L'IA nécessite D1, une configuration de fournisseur valide et un quota : 60 générations par heure à l'échelle du service par défaut. L'état de préparation de la configuration ne prouve pas la disponibilité de l'API externe. Les erreurs de fournisseur, l'épuisement du quota ou une sortie invalide renvoient une recherche avec aiUnavailable=true et HTTP 200. Sans configuration de génération, une recherche régulière est renvoyée.",
        "parameters": [
          {
            "name": "Origin",
            "in": "header",
            "required": false,
            "description": "Doit correspondre exactement à APP_ORIGIN. Sec-Fetch-Site : cross-site est rejeté. Le serveur ne fournit pas d'accès CORS.",
            "schema": {
              "type": "string",
              "format": "uri"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "description": "Objet JSON jusqu'à 65 536 octets. Les champs inconnus sont rejetés. Les longueurs de texte sont vérifiées après suppression des espaces.",
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/SupportQuestion"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Une réponse ou une documentation correspondante avec des liens d'articles validés.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SupportAnswer"
                }
              }
            }
          },
          "400": {
            "description": "JSON invalide, champ inconnu ou valeur invalide.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "INVALID_JSON",
              "UNKNOWN_FIELD",
              "INVALID_INPUT",
              "INVALID_VAT_PAYER",
              "INVALID_NOTIFICATIONS",
              "INVALID_TICKET_STATUS",
              "INVALID_UPDATE",
              "INVALID_HISTORY",
              "INVALID_PAGE_CONTEXT",
              "INVALID_LOCALE",
              "INVALID_FILTER",
              "INVALID_NOTIFICATION_SELECTION",
              "INVALID_AMOUNT",
              "INVALID_CURRENCY",
              "INVALID_METHODS",
              "INVALID_EXPIRY",
              "INVALID_QUOTE",
              "INVALID_LIMIT",
              "INVALID_CURSOR",
              "INVALID_STATUS",
              "IDEMPOTENCY_KEY_REQUIRED",
              "METHOD_UNAVAILABLE"
            ]
          },
          "403": {
            "description": "La validation d'origine, CSRF ou de liaison navigateur a échoué.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "INVALID_ORIGIN",
              "CSRF_REQUIRED",
              "BROWSER_PROOF_REQUIRED",
              "INVALID_CHALLENGE"
            ]
          },
          "413": {
            "description": "Le corps dépasse 16 384 octets ; la limite est de 65 536 octets pour /api/support/ask et le webhook Telegram.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "REQUEST_TOO_LARGE"
            ]
          },
          "415": {
            "description": "Content-Type : application/json est requis.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "JSON_REQUIRED"
            ]
          },
          "429": {
            "description": "Limite de débit dépassée.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              },
              "Retry-After": {
                "schema": {
                  "type": "string",
                  "const": "60"
                },
                "description": "Le serveur renvoie toujours 60 ; cela ne garantit pas qu'un quota horaire a expiré."
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "RATE_LIMITED"
            ]
          },
          "503": {
            "description": "Une intégration ou un stockage est indisponible, un événement est en cours de traitement, ou le service est indisponible.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "DATABASE_NOT_CONFIGURED",
              "ORIGIN_NOT_CONFIGURED",
              "TELEGRAM_NOT_CONFIGURED",
              "UPDATE_IN_PROGRESS",
              "SERVICE_UNAVAILABLE",
              "GATEWAY_NOT_CONNECTED"
            ]
          }
        }
      }
    },
    "/api/support/tickets": {
      "get": {
        "tags": [
          "Support"
        ],
        "operationId": "listOwnTickets",
        "summary": "Les 30 dernières demandes de support possédées",
        "security": [
          {
            "cookieSession": []
          }
        ],
        "responses": {
          "200": {
            "description": "Plus récentes d'abord par createdAt ; renvoie les 30 derniers enregistrements.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "tickets": {
                      "type": "array",
                      "maxItems": 30,
                      "items": {
                        "$ref": "#/components/schemas/Ticket"
                      }
                    }
                  },
                  "required": [
                    "tickets"
                  ],
                  "additionalProperties": false
                }
              }
            }
          },
          "401": {
            "description": "Aucune session valide ; pour un webhook, un secret invalide.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "AUTH_REQUIRED",
              "SESSION_EXPIRED",
              "WEBHOOK_UNAUTHORIZED"
            ]
          },
          "503": {
            "description": "Une intégration ou un stockage est indisponible, un événement est en cours de traitement, ou le service est indisponible.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "DATABASE_NOT_CONFIGURED",
              "ORIGIN_NOT_CONFIGURED",
              "TELEGRAM_NOT_CONFIGURED",
              "UPDATE_IN_PROGRESS",
              "SERVICE_UNAVAILABLE",
              "GATEWAY_NOT_CONNECTED"
            ]
          }
        }
      },
      "post": {
        "tags": [
          "Support"
        ],
        "operationId": "createOwnTicket",
        "summary": "Enregistrer une demande de support",
        "description": "Jusqu'à six demandes par utilisateur par heure, partagées entre le site Web et le bot. L'enregistrement crée un enregistrement de compte. Conservez sa référence pour demander l'état.",
        "security": [
          {
            "cookieSession": [],
            "csrfToken": []
          }
        ],
        "parameters": [
          {
            "name": "Origin",
            "in": "header",
            "required": true,
            "description": "Doit correspondre exactement à APP_ORIGIN. Sec-Fetch-Site : cross-site est rejeté. Le serveur ne fournit pas d'accès CORS.",
            "schema": {
              "type": "string",
              "format": "uri"
            }
          },
          {
            "name": "X-CSRF-Token",
            "in": "header",
            "required": true,
            "description": "Jeton de session actuel depuis GET /api/auth/me ou achèvement réussi de la connexion.",
            "schema": {
              "type": "string",
              "pattern": "^[A-Za-z0-9_-]{43}$"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "description": "Objet JSON. Maximum 16 384 octets. Les champs inconnus sont rejetés.",
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "subject": {
                    "type": "string",
                    "minLength": 1,
                    "x-trimmedMaxLength": 120,
                    "x-trimmedMinLength": 1,
                    "description": "Les espaces environnants sont supprimés. Longueur après suppression : 1 à 120 caractères. Les caractères de contrôle, à l'exception des tabulations et des sauts de ligne, sont interdits."
                  },
                  "message": {
                    "type": "string",
                    "minLength": 5,
                    "x-trimmedMaxLength": 4000,
                    "x-trimmedMinLength": 5,
                    "description": "Les espaces environnants sont supprimés. Longueur après suppression : 5 à 4 000 caractères. Les caractères de contrôle, à l'exception des tabulations et des sauts de ligne, sont interdits."
                  }
                },
                "required": [
                  "subject",
                  "message"
                ],
                "additionalProperties": false
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Demande de support enregistrée.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TicketResponse"
                }
              }
            }
          },
          "400": {
            "description": "JSON invalide, champ inconnu ou valeur invalide.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "INVALID_JSON",
              "UNKNOWN_FIELD",
              "INVALID_INPUT",
              "INVALID_VAT_PAYER",
              "INVALID_NOTIFICATIONS",
              "INVALID_TICKET_STATUS",
              "INVALID_UPDATE",
              "INVALID_HISTORY",
              "INVALID_PAGE_CONTEXT",
              "INVALID_LOCALE",
              "INVALID_FILTER",
              "INVALID_NOTIFICATION_SELECTION",
              "INVALID_AMOUNT",
              "INVALID_CURRENCY",
              "INVALID_METHODS",
              "INVALID_EXPIRY",
              "INVALID_QUOTE",
              "INVALID_LIMIT",
              "INVALID_CURSOR",
              "INVALID_STATUS",
              "IDEMPOTENCY_KEY_REQUIRED",
              "METHOD_UNAVAILABLE"
            ]
          },
          "401": {
            "description": "Aucune session valide ; pour un webhook, un secret invalide.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "AUTH_REQUIRED",
              "SESSION_EXPIRED",
              "WEBHOOK_UNAUTHORIZED"
            ]
          },
          "403": {
            "description": "La validation d'origine, CSRF ou de liaison navigateur a échoué.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "INVALID_ORIGIN",
              "CSRF_REQUIRED",
              "BROWSER_PROOF_REQUIRED",
              "INVALID_CHALLENGE"
            ]
          },
          "409": {
            "description": "La demande de connexion est utilisée ou annulée, l'identité n'est pas confirmée, ou une demande d'assistance entre en conflit.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "CHALLENGE_UNAVAILABLE",
              "IDENTITY_INCOMPLETE",
              "TICKET_CONFLICT",
              "IDEMPOTENCY_CONFLICT",
              "LINK_CANCELLED",
              "LINK_EXPIRED",
              "LINK_UNAVAILABLE"
            ]
          },
          "413": {
            "description": "Le corps dépasse 16 384 octets ; la limite est de 65 536 octets pour /api/support/ask et le webhook Telegram.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "REQUEST_TOO_LARGE"
            ]
          },
          "415": {
            "description": "Content-Type : application/json est requis.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "JSON_REQUIRED"
            ]
          },
          "429": {
            "description": "Limite de débit dépassée.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              },
              "Retry-After": {
                "schema": {
                  "type": "string",
                  "const": "60"
                },
                "description": "Le serveur renvoie toujours 60 ; cela ne garantit pas qu'un quota horaire a expiré."
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "RATE_LIMITED"
            ]
          },
          "503": {
            "description": "Une intégration ou un stockage est indisponible, un événement est en cours de traitement, ou le service est indisponible.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "DATABASE_NOT_CONFIGURED",
              "ORIGIN_NOT_CONFIGURED",
              "TELEGRAM_NOT_CONFIGURED",
              "UPDATE_IN_PROGRESS",
              "SERVICE_UNAVAILABLE",
              "GATEWAY_NOT_CONNECTED"
            ]
          }
        }
      }
    },
    "/api/support/tickets/{id}": {
      "parameters": [
        {
          "name": "id",
          "in": "path",
          "required": true,
          "schema": {
            "type": "string",
            "pattern": "^tkt_[a-f0-9-]{36}$"
          },
          "description": "Identifiant d'une demande de support possédée."
        }
      ],
      "get": {
        "tags": [
          "Support"
        ],
        "operationId": "getOwnTicket",
        "summary": "Lire une demande de support possédée",
        "security": [
          {
            "cookieSession": []
          }
        ],
        "responses": {
          "200": {
            "description": "La demande d'un autre utilisateur et une demande manquante renvoient toutes deux 404.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TicketResponse"
                }
              }
            }
          },
          "401": {
            "description": "Aucune session valide ; pour un webhook, un secret invalide.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "AUTH_REQUIRED",
              "SESSION_EXPIRED",
              "WEBHOOK_UNAUTHORIZED"
            ]
          },
          "404": {
            "description": "L'enregistrement est manquant, le jeton public est invalide, ou l'enregistrement demandé appartient à un autre utilisateur.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "TICKET_NOT_FOUND",
              "LINK_NOT_FOUND"
            ]
          },
          "503": {
            "description": "Une intégration ou un stockage est indisponible, un événement est en cours de traitement, ou le service est indisponible.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "DATABASE_NOT_CONFIGURED",
              "ORIGIN_NOT_CONFIGURED",
              "TELEGRAM_NOT_CONFIGURED",
              "UPDATE_IN_PROGRESS",
              "SERVICE_UNAVAILABLE",
              "GATEWAY_NOT_CONNECTED"
            ]
          }
        }
      },
      "patch": {
        "tags": [
          "Support"
        ],
        "operationId": "closeOwnTicket",
        "summary": "Fermer une demande de support possédée",
        "description": "Le seul changement autorisé est le statut=closed. Cette API ne rouvre pas les demandes, ne modifie pas les messages et ne soumet pas de réponses d'opérateur.",
        "security": [
          {
            "cookieSession": [],
            "csrfToken": []
          }
        ],
        "parameters": [
          {
            "name": "Origin",
            "in": "header",
            "required": true,
            "description": "Doit correspondre exactement à APP_ORIGIN. Sec-Fetch-Site : cross-site est rejeté. Le serveur ne fournit pas d'accès CORS.",
            "schema": {
              "type": "string",
              "format": "uri"
            }
          },
          {
            "name": "X-CSRF-Token",
            "in": "header",
            "required": true,
            "description": "Jeton de session actuel depuis GET /api/auth/me ou achèvement réussi de la connexion.",
            "schema": {
              "type": "string",
              "pattern": "^[A-Za-z0-9_-]{43}$"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "description": "Objet JSON. Maximum 16 384 octets. Les champs inconnus sont rejetés.",
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "status": {
                    "type": "string",
                    "const": "closed"
                  }
                },
                "required": [
                  "status"
                ],
                "additionalProperties": false
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Demande de support fermée.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "closed": {
                      "type": "boolean",
                      "const": true
                    }
                  },
                  "required": [
                    "closed"
                  ],
                  "additionalProperties": false
                }
              }
            }
          },
          "400": {
            "description": "JSON invalide, champ inconnu ou valeur invalide.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "INVALID_JSON",
              "UNKNOWN_FIELD",
              "INVALID_INPUT",
              "INVALID_VAT_PAYER",
              "INVALID_NOTIFICATIONS",
              "INVALID_TICKET_STATUS",
              "INVALID_UPDATE",
              "INVALID_HISTORY",
              "INVALID_PAGE_CONTEXT",
              "INVALID_LOCALE",
              "INVALID_FILTER",
              "INVALID_NOTIFICATION_SELECTION",
              "INVALID_AMOUNT",
              "INVALID_CURRENCY",
              "INVALID_METHODS",
              "INVALID_EXPIRY",
              "INVALID_QUOTE",
              "INVALID_LIMIT",
              "INVALID_CURSOR",
              "INVALID_STATUS",
              "IDEMPOTENCY_KEY_REQUIRED",
              "METHOD_UNAVAILABLE"
            ]
          },
          "401": {
            "description": "Aucune session valide ; pour un webhook, un secret invalide.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "AUTH_REQUIRED",
              "SESSION_EXPIRED",
              "WEBHOOK_UNAUTHORIZED"
            ]
          },
          "403": {
            "description": "La validation d'origine, CSRF ou de liaison navigateur a échoué.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "INVALID_ORIGIN",
              "CSRF_REQUIRED",
              "BROWSER_PROOF_REQUIRED",
              "INVALID_CHALLENGE"
            ]
          },
          "404": {
            "description": "L'enregistrement est manquant, le jeton public est invalide, ou l'enregistrement demandé appartient à un autre utilisateur.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "TICKET_NOT_FOUND",
              "LINK_NOT_FOUND"
            ]
          },
          "413": {
            "description": "Le corps dépasse 16 384 octets ; la limite est de 65 536 octets pour /api/support/ask et le webhook Telegram.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "REQUEST_TOO_LARGE"
            ]
          },
          "415": {
            "description": "Content-Type : application/json est requis.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "JSON_REQUIRED"
            ]
          },
          "503": {
            "description": "Une intégration ou un stockage est indisponible, un événement est en cours de traitement, ou le service est indisponible.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "DATABASE_NOT_CONFIGURED",
              "ORIGIN_NOT_CONFIGURED",
              "TELEGRAM_NOT_CONFIGURED",
              "UPDATE_IN_PROGRESS",
              "SERVICE_UNAVAILABLE",
              "GATEWAY_NOT_CONNECTED"
            ]
          }
        }
      }
    },
    "/api/notifications": {
      "get": {
        "tags": [
          "Notifications"
        ],
        "operationId": "listOwnNotifications",
        "summary": "Une page de notifications possédées",
        "description": "Jusqu'à 30 enregistrements, triés par createdAt DESC puis id DESC. La liste ne modifie pas readAt et ne déclenche pas de livraison. unreadCount est le nombre total de non-lus de l'utilisateur actuel toutes pages confondues. Les champs du propriétaire, l'identifiant Telegram et les secrets sont exclus.",
        "security": [
          {
            "cookieSession": []
          }
        ],
        "parameters": [
          {
            "name": "unread",
            "in": "query",
            "description": "1 sélectionne uniquement les non-lus ; 0 ou une valeur omise sélectionne tous les événements.",
            "schema": {
              "type": "string",
              "enum": [
                "0",
                "1"
              ],
              "default": "0"
            }
          },
          {
            "name": "cursor",
            "in": "query",
            "description": "nextCursor de la réponse précédente. Passez-le inchangé via URLSearchParams ; format createdAt:id, avec createdAt un entier sûr en millisecondes.",
            "schema": {
              "type": "string",
              "pattern": "^\\d{1,16}:ntf_[A-Za-z0-9-]{1,80}$"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Événements avec états de lecture et de livraison indépendants.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/NotificationsPage"
                }
              }
            }
          },
          "400": {
            "description": "JSON invalide, champ inconnu ou valeur invalide.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "INVALID_JSON",
              "UNKNOWN_FIELD",
              "INVALID_INPUT",
              "INVALID_VAT_PAYER",
              "INVALID_NOTIFICATIONS",
              "INVALID_TICKET_STATUS",
              "INVALID_UPDATE",
              "INVALID_HISTORY",
              "INVALID_PAGE_CONTEXT",
              "INVALID_LOCALE",
              "INVALID_FILTER",
              "INVALID_NOTIFICATION_SELECTION",
              "INVALID_AMOUNT",
              "INVALID_CURRENCY",
              "INVALID_METHODS",
              "INVALID_EXPIRY",
              "INVALID_QUOTE",
              "INVALID_LIMIT",
              "INVALID_CURSOR",
              "INVALID_STATUS",
              "IDEMPOTENCY_KEY_REQUIRED",
              "METHOD_UNAVAILABLE"
            ]
          },
          "401": {
            "description": "Aucune session valide ; pour un webhook, un secret invalide.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "AUTH_REQUIRED",
              "SESSION_EXPIRED",
              "WEBHOOK_UNAUTHORIZED"
            ]
          },
          "503": {
            "description": "Une intégration ou un stockage est indisponible, un événement est en cours de traitement, ou le service est indisponible.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "DATABASE_NOT_CONFIGURED",
              "ORIGIN_NOT_CONFIGURED",
              "TELEGRAM_NOT_CONFIGURED",
              "UPDATE_IN_PROGRESS",
              "SERVICE_UNAVAILABLE",
              "GATEWAY_NOT_CONNECTED"
            ]
          }
        }
      }
    },
    "/api/notifications/read": {
      "patch": {
        "tags": [
          "Notifications"
        ],
        "operationId": "markOwnNotificationsRead",
        "summary": "Marquer les notifications possédées comme lues",
        "description": "Accepte soit ids, soit all:true avec before. before est l'heure du dernier événement observé et ne peut pas dépasser l'heure du serveur. Les événements plus récents restent non-lus. La demande est idempotente : les valeurs readAt existantes ne changent pas. Les identifiants d'autres utilisateurs et les identifiants manquants ne sont ni modifiés ni divulgués. Le statut Telegram et sentAt restent inchangés.",
        "security": [
          {
            "cookieSession": [],
            "csrfToken": []
          }
        ],
        "parameters": [
          {
            "name": "Origin",
            "in": "header",
            "required": true,
            "description": "Doit correspondre exactement à APP_ORIGIN. Sec-Fetch-Site : cross-site est rejeté. Le serveur ne fournit pas d'accès CORS.",
            "schema": {
              "type": "string",
              "format": "uri"
            }
          },
          {
            "name": "X-CSRF-Token",
            "in": "header",
            "required": true,
            "description": "Jeton de session actuel depuis GET /api/auth/me ou achèvement réussi de la connexion.",
            "schema": {
              "type": "string",
              "pattern": "^[A-Za-z0-9_-]{43}$"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "description": "Objet JSON. Maximum 16 384 octets. Les champs inconnus sont rejetés.",
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ReadNotifications"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Le nombre d'événements nouvellement lus, le temps d'opération et le nombre actuel de non-lus.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ReadNotificationsResult"
                }
              }
            }
          },
          "400": {
            "description": "JSON invalide, champ inconnu ou valeur invalide.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "INVALID_JSON",
              "UNKNOWN_FIELD",
              "INVALID_INPUT",
              "INVALID_VAT_PAYER",
              "INVALID_NOTIFICATIONS",
              "INVALID_TICKET_STATUS",
              "INVALID_UPDATE",
              "INVALID_HISTORY",
              "INVALID_PAGE_CONTEXT",
              "INVALID_LOCALE",
              "INVALID_FILTER",
              "INVALID_NOTIFICATION_SELECTION",
              "INVALID_AMOUNT",
              "INVALID_CURRENCY",
              "INVALID_METHODS",
              "INVALID_EXPIRY",
              "INVALID_QUOTE",
              "INVALID_LIMIT",
              "INVALID_CURSOR",
              "INVALID_STATUS",
              "IDEMPOTENCY_KEY_REQUIRED",
              "METHOD_UNAVAILABLE"
            ]
          },
          "401": {
            "description": "Aucune session valide ; pour un webhook, un secret invalide.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "AUTH_REQUIRED",
              "SESSION_EXPIRED",
              "WEBHOOK_UNAUTHORIZED"
            ]
          },
          "403": {
            "description": "La validation d'origine, CSRF ou de liaison navigateur a échoué.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "INVALID_ORIGIN",
              "CSRF_REQUIRED",
              "BROWSER_PROOF_REQUIRED",
              "INVALID_CHALLENGE"
            ]
          },
          "413": {
            "description": "Le corps dépasse 16 384 octets ; la limite est de 65 536 octets pour /api/support/ask et le webhook Telegram.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "REQUEST_TOO_LARGE"
            ]
          },
          "415": {
            "description": "Content-Type : application/json est requis.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "JSON_REQUIRED"
            ]
          },
          "503": {
            "description": "Une intégration ou un stockage est indisponible, un événement est en cours de traitement, ou le service est indisponible.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "DATABASE_NOT_CONFIGURED",
              "ORIGIN_NOT_CONFIGURED",
              "TELEGRAM_NOT_CONFIGURED",
              "UPDATE_IN_PROGRESS",
              "SERVICE_UNAVAILABLE",
              "GATEWAY_NOT_CONNECTED"
            ]
          }
        }
      }
    },
    "/api/notifications/test": {
      "post": {
        "tags": [
          "Notifications"
        ],
        "operationId": "testOwnNotification",
        "summary": "Envoyer une notification de test au Telegram du compte",
        "description": "Met un message en file d'attente et tente la livraison. Au maximum trois par utilisateur par heure. Le test n'est pas une notification de paiement et ne modifie pas de solde. HTTP 202 ne confirme pas la livraison ; vérifiez la liste des notifications.",
        "security": [
          {
            "cookieSession": [],
            "csrfToken": []
          }
        ],
        "parameters": [
          {
            "name": "Origin",
            "in": "header",
            "required": true,
            "description": "Doit correspondre exactement à APP_ORIGIN. Sec-Fetch-Site : cross-site est rejeté. Le serveur ne fournit pas d'accès CORS.",
            "schema": {
              "type": "string",
              "format": "uri"
            }
          },
          {
            "name": "X-CSRF-Token",
            "in": "header",
            "required": true,
            "description": "Jeton de session actuel depuis GET /api/auth/me ou achèvement réussi de la connexion.",
            "schema": {
              "type": "string",
              "pattern": "^[A-Za-z0-9_-]{43}$"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "description": "Objet JSON. Maximum 16 384 octets. Les champs inconnus sont rejetés.",
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/EmptyObject"
              }
            }
          }
        },
        "responses": {
          "202": {
            "description": "Résultat de la mise en file d'attente.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "queued": {
                      "type": "boolean"
                    },
                    "id": {
                      "type": [
                        "string",
                        "null"
                      ]
                    }
                  },
                  "required": [
                    "queued"
                  ],
                  "additionalProperties": false
                }
              }
            }
          },
          "400": {
            "description": "JSON invalide, champ inconnu ou valeur invalide.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "INVALID_JSON",
              "UNKNOWN_FIELD",
              "INVALID_INPUT",
              "INVALID_VAT_PAYER",
              "INVALID_NOTIFICATIONS",
              "INVALID_TICKET_STATUS",
              "INVALID_UPDATE",
              "INVALID_HISTORY",
              "INVALID_PAGE_CONTEXT",
              "INVALID_LOCALE",
              "INVALID_FILTER",
              "INVALID_NOTIFICATION_SELECTION",
              "INVALID_AMOUNT",
              "INVALID_CURRENCY",
              "INVALID_METHODS",
              "INVALID_EXPIRY",
              "INVALID_QUOTE",
              "INVALID_LIMIT",
              "INVALID_CURSOR",
              "INVALID_STATUS",
              "IDEMPOTENCY_KEY_REQUIRED",
              "METHOD_UNAVAILABLE"
            ]
          },
          "401": {
            "description": "Aucune session valide ; pour un webhook, un secret invalide.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "AUTH_REQUIRED",
              "SESSION_EXPIRED",
              "WEBHOOK_UNAUTHORIZED"
            ]
          },
          "403": {
            "description": "La validation d'origine, CSRF ou de liaison navigateur a échoué.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "INVALID_ORIGIN",
              "CSRF_REQUIRED",
              "BROWSER_PROOF_REQUIRED",
              "INVALID_CHALLENGE"
            ]
          },
          "413": {
            "description": "Le corps dépasse 16 384 octets ; la limite est de 65 536 octets pour /api/support/ask et le webhook Telegram.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "REQUEST_TOO_LARGE"
            ]
          },
          "415": {
            "description": "Content-Type : application/json est requis.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "JSON_REQUIRED"
            ]
          },
          "429": {
            "description": "Limite de débit dépassée.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              },
              "Retry-After": {
                "schema": {
                  "type": "string",
                  "const": "60"
                },
                "description": "Le serveur renvoie toujours 60 ; cela ne garantit pas qu'un quota horaire a expiré."
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "RATE_LIMITED"
            ]
          },
          "503": {
            "description": "Une intégration ou un stockage est indisponible, un événement est en cours de traitement, ou le service est indisponible.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "DATABASE_NOT_CONFIGURED",
              "ORIGIN_NOT_CONFIGURED",
              "TELEGRAM_NOT_CONFIGURED",
              "UPDATE_IN_PROGRESS",
              "SERVICE_UNAVAILABLE",
              "GATEWAY_NOT_CONNECTED"
            ]
          }
        }
      }
    },
    "/api/telegram/webhook": {
      "post": {
        "tags": [
          "Telegram"
        ],
        "operationId": "receiveTelegramUpdate",
        "summary": "Recevoir un événement Telegram Bot API",
        "description": "Nécessite uniquement l'en-tête secret Telegram, sans session, CSRF ni Origin. Des champs Update supplémentaires sont autorisés. Gère message et callback_query ; les actions personnelles nécessitent la discussion privée de l'expéditeur vérifié. Les autres mises à jour sont ignorées. update_id est dédoublonné ; une mise à jour en cours renvoie 503 pour nouvelle tentative. Ce n'est pas un webhook de fournisseur de paiement.",
        "security": [
          {
            "telegramWebhookSecret": []
          }
        ],
        "requestBody": {
          "required": true,
          "description": "Update Telegram, maximum 65 536 octets.",
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/TelegramUpdate"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Traité, ignoré ou déjà pris en charge.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean",
                      "const": true
                    }
                  },
                  "required": [
                    "ok"
                  ],
                  "additionalProperties": false
                }
              }
            }
          },
          "400": {
            "description": "JSON invalide, champ inconnu ou valeur invalide.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "INVALID_JSON",
              "UNKNOWN_FIELD",
              "INVALID_INPUT",
              "INVALID_VAT_PAYER",
              "INVALID_NOTIFICATIONS",
              "INVALID_TICKET_STATUS",
              "INVALID_UPDATE",
              "INVALID_HISTORY",
              "INVALID_PAGE_CONTEXT",
              "INVALID_LOCALE",
              "INVALID_FILTER",
              "INVALID_NOTIFICATION_SELECTION",
              "INVALID_AMOUNT",
              "INVALID_CURRENCY",
              "INVALID_METHODS",
              "INVALID_EXPIRY",
              "INVALID_QUOTE",
              "INVALID_LIMIT",
              "INVALID_CURSOR",
              "INVALID_STATUS",
              "IDEMPOTENCY_KEY_REQUIRED",
              "METHOD_UNAVAILABLE"
            ]
          },
          "401": {
            "description": "Aucune session valide ; pour un webhook, un secret invalide.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "AUTH_REQUIRED",
              "SESSION_EXPIRED",
              "WEBHOOK_UNAUTHORIZED"
            ]
          },
          "413": {
            "description": "Le corps dépasse 16 384 octets ; la limite est de 65 536 octets pour /api/support/ask et le webhook Telegram.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "REQUEST_TOO_LARGE"
            ]
          },
          "415": {
            "description": "Content-Type : application/json est requis.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "JSON_REQUIRED"
            ]
          },
          "429": {
            "description": "Limite de débit dépassée.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              },
              "Retry-After": {
                "schema": {
                  "type": "string",
                  "const": "60"
                },
                "description": "Le serveur renvoie toujours 60 ; cela ne garantit pas qu'un quota horaire a expiré."
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "RATE_LIMITED"
            ]
          },
          "502": {
            "description": "La demande Telegram a échoué.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "TELEGRAM_UNAVAILABLE",
              "TELEGRAM_INVALID_RESPONSE",
              "TELEGRAM_RATE_LIMITED",
              "TELEGRAM_DELIVERY_FAILED"
            ]
          },
          "503": {
            "description": "Une intégration ou un stockage est indisponible, un événement est en cours de traitement, ou le service est indisponible.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "DATABASE_NOT_CONFIGURED",
              "ORIGIN_NOT_CONFIGURED",
              "TELEGRAM_NOT_CONFIGURED",
              "UPDATE_IN_PROGRESS",
              "SERVICE_UNAVAILABLE",
              "GATEWAY_NOT_CONNECTED"
            ]
          }
        }
      }
    },
    "/api/payments": {
      "post": {
        "tags": [
          "Disabled payments"
        ],
        "operationId": "paymentsUnavailable",
        "summary": "Opération financière indisponible",
        "description": "Renvoie 503 GATEWAY_NOT_CONNECTED. Les opérations financières sont indisponibles. Le corps n'est pas traité et aucune demande de passerelle n'est effectuée.",
        "security": [],
        "responses": {
          "503": {
            "description": "Les opérations financières sont indisponibles.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "object",
                      "properties": {
                        "code": {
                          "type": "string",
                          "const": "GATEWAY_NOT_CONNECTED"
                        },
                        "message": {
                          "type": "string"
                        }
                      },
                      "required": [
                        "code",
                        "message"
                      ],
                      "additionalProperties": false
                    }
                  },
                  "required": [
                    "error"
                  ],
                  "additionalProperties": false
                }
              }
            }
          }
        }
      }
    },
    "/api/payouts": {
      "post": {
        "tags": [
          "Disabled payments"
        ],
        "operationId": "payoutsUnavailable",
        "summary": "Opération financière indisponible",
        "description": "Renvoie 503 GATEWAY_NOT_CONNECTED. Les opérations financières sont indisponibles. Le corps n'est pas traité et aucune demande de passerelle n'est effectuée.",
        "security": [],
        "responses": {
          "503": {
            "description": "Les opérations financières sont indisponibles.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "object",
                      "properties": {
                        "code": {
                          "type": "string",
                          "const": "GATEWAY_NOT_CONNECTED"
                        },
                        "message": {
                          "type": "string"
                        }
                      },
                      "required": [
                        "code",
                        "message"
                      ],
                      "additionalProperties": false
                    }
                  },
                  "required": [
                    "error"
                  ],
                  "additionalProperties": false
                }
              }
            }
          }
        }
      }
    },
    "/openapi.json": {
      "get": {
        "tags": [
          "Service"
        ],
        "operationId": "getOpenApi",
        "summary": "Contrat public lisible par machine",
        "description": "Lecture seule. Ne contient aucun secret, session, enregistrement d'utilisateur ni contrat opérationnel de fournisseur de paiement.",
        "responses": {
          "200": {
            "description": "Ce document OpenAPI 3.1.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "public, max-age=300"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "openapi",
                    "info",
                    "paths"
                  ],
                  "additionalProperties": true
                }
              }
            }
          }
        }
      }
    },
    "/api/payment-links": {
      "get": {
        "tags": [
          "Payment links"
        ],
        "operationId": "listOwnPaymentLinks",
        "summary": "Lister les liens de paiement possédés",
        "security": [
          {
            "cookieSession": []
          }
        ],
        "description": "Triés par createdAt DESC, id DESC. Les liens publics fonctionnent sur tous les appareils ; la liste nécessite un compte.",
        "parameters": [
          {
            "name": "limit",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 30
            }
          },
          {
            "name": "cursor",
            "in": "query",
            "schema": {
              "type": "string"
            },
            "description": "nextCursor de la réponse précédente. Le service utilise JSON base64 [createdAt,id] ; passez-le inchangé via URLSearchParams."
          },
          {
            "name": "X-Kevmi-Store",
            "in": "header",
            "required": false,
            "schema": {
              "type": "string",
              "pattern": "^sto_[A-Za-z0-9_-]+$"
            },
            "description": "Sélectionnez une boutique possédée pour une session de navigateur. Omett pour utiliser la première boutique. Les clés API ignorent cet en-tête et utilisent toujours leur liaison de boutique immuable. La valeur all est en lecture seule et n'est prise en charge que par le tableau de bord et les points de terminaison de liste de factures."
          }
        ],
        "responses": {
          "200": {
            "description": "Une page de liens de paiement possédés.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PaymentLinksPage"
                }
              }
            }
          },
          "400": {
            "description": "JSON invalide, champ inconnu ou valeur invalide.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "INVALID_JSON",
              "UNKNOWN_FIELD",
              "INVALID_INPUT",
              "INVALID_VAT_PAYER",
              "INVALID_NOTIFICATIONS",
              "INVALID_TICKET_STATUS",
              "INVALID_UPDATE",
              "INVALID_HISTORY",
              "INVALID_PAGE_CONTEXT",
              "INVALID_LOCALE",
              "INVALID_FILTER",
              "INVALID_NOTIFICATION_SELECTION",
              "INVALID_AMOUNT",
              "INVALID_CURRENCY",
              "INVALID_METHODS",
              "INVALID_EXPIRY",
              "INVALID_QUOTE",
              "INVALID_LIMIT",
              "INVALID_CURSOR",
              "INVALID_STATUS",
              "IDEMPOTENCY_KEY_REQUIRED",
              "METHOD_UNAVAILABLE"
            ]
          },
          "401": {
            "description": "Aucune session valide ; pour un webhook, un secret invalide.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "AUTH_REQUIRED",
              "SESSION_EXPIRED",
              "WEBHOOK_UNAUTHORIZED"
            ]
          },
          "503": {
            "description": "Une intégration ou un stockage est indisponible, un événement est en cours de traitement, ou le service est indisponible.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "DATABASE_NOT_CONFIGURED",
              "ORIGIN_NOT_CONFIGURED",
              "TELEGRAM_NOT_CONFIGURED",
              "UPDATE_IN_PROGRESS",
              "SERVICE_UNAVAILABLE",
              "GATEWAY_NOT_CONNECTED"
            ]
          }
        }
      },
      "post": {
        "tags": [
          "Payment links"
        ],
        "operationId": "createOwnPaymentLink",
        "summary": "Créer une facture avec conditions immuables",
        "security": [
          {
            "cookieSession": [],
            "csrfToken": []
          }
        ],
        "parameters": [
          {
            "name": "Origin",
            "in": "header",
            "required": true,
            "description": "Doit correspondre exactement à APP_ORIGIN. Sec-Fetch-Site : cross-site est rejeté. Le serveur ne fournit pas d'accès CORS.",
            "schema": {
              "type": "string",
              "format": "uri"
            }
          },
          {
            "name": "X-CSRF-Token",
            "in": "header",
            "required": true,
            "description": "Jeton de session actuel depuis GET /api/auth/me ou achèvement réussi de la connexion.",
            "schema": {
              "type": "string",
              "pattern": "^[A-Za-z0-9_-]{43}$"
            }
          },
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string",
              "pattern": "^[A-Za-z0-9_-]{16,100}$"
            },
            "description": "Une clé par demande logique. Une répétition avec une entrée canonique identique renvoie l'enregistrement original ; une entrée différente avec la même clé renvoie 409."
          },
          {
            "name": "X-Kevmi-Store",
            "in": "header",
            "required": false,
            "schema": {
              "type": "string",
              "pattern": "^sto_[A-Za-z0-9_-]+$"
            },
            "description": "Sélectionnez une boutique possédée pour une session de navigateur. Omett pour utiliser la première boutique. Les clés API ignorent cet en-tête et utilisent toujours leur liaison de boutique immuable. La valeur all est en lecture seule et n'est prise en charge que par le tableau de bord et les points de terminaison de liste de factures."
          }
        ],
        "description": "Créer une facture immuable et recevoir immédiatement checkoutUrl ; cela ne débite pas l'acheteur. USD : carte/Alipay ; RUB : SBP ; USDT : crypto. L'USD crypto reste accepté pour compatibilité mais ne peut pas être payé sans facture USDT. Les parts client des frais et de la TVA sont des entiers indépendants de 0 à 100 ; la carte/Alipay live actuelle prend en charge TVA client=100 et frais client=0. Le total client n'est jamais modifié silencieusement. La dernière application du site Web doit être approuvée avant un nouveau débit live. La création de facture et son événement marchand sont validés atomiquement.",
        "requestBody": {
          "required": true,
          "description": "Objet JSON. Maximum 16 384 octets. Les champs inconnus sont rejetés.",
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreatePaymentLink"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Relecture idempotente ; replayed=true.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CreatePaymentLinkResponse"
                }
              }
            }
          },
          "201": {
            "description": "Facture créée ; replayed=false.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CreatePaymentLinkResponse"
                }
              }
            }
          },
          "400": {
            "description": "JSON invalide, champ inconnu ou valeur invalide.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "INVALID_JSON",
              "UNKNOWN_FIELD",
              "INVALID_INPUT",
              "INVALID_VAT_PAYER",
              "INVALID_NOTIFICATIONS",
              "INVALID_TICKET_STATUS",
              "INVALID_UPDATE",
              "INVALID_HISTORY",
              "INVALID_PAGE_CONTEXT",
              "INVALID_LOCALE",
              "INVALID_FILTER",
              "INVALID_NOTIFICATION_SELECTION",
              "INVALID_AMOUNT",
              "INVALID_CURRENCY",
              "INVALID_METHODS",
              "INVALID_EXPIRY",
              "INVALID_QUOTE",
              "INVALID_LIMIT",
              "INVALID_CURSOR",
              "INVALID_STATUS",
              "IDEMPOTENCY_KEY_REQUIRED",
              "METHOD_UNAVAILABLE"
            ]
          },
          "401": {
            "description": "Aucune session valide ; pour un webhook, un secret invalide.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "AUTH_REQUIRED",
              "SESSION_EXPIRED",
              "WEBHOOK_UNAUTHORIZED"
            ]
          },
          "403": {
            "description": "La validation d'origine, CSRF ou de liaison navigateur a échoué.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "INVALID_ORIGIN",
              "CSRF_REQUIRED",
              "BROWSER_PROOF_REQUIRED",
              "INVALID_CHALLENGE"
            ]
          },
          "409": {
            "description": "La demande de connexion est utilisée ou annulée, l'identité n'est pas confirmée, ou une demande d'assistance entre en conflit.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "CHALLENGE_UNAVAILABLE",
              "IDENTITY_INCOMPLETE",
              "TICKET_CONFLICT",
              "IDEMPOTENCY_CONFLICT",
              "LINK_CANCELLED",
              "LINK_EXPIRED",
              "LINK_UNAVAILABLE"
            ]
          },
          "413": {
            "description": "Le corps dépasse 16 384 octets ; la limite est de 65 536 octets pour /api/support/ask et le webhook Telegram.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "REQUEST_TOO_LARGE"
            ]
          },
          "415": {
            "description": "Content-Type : application/json est requis.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "JSON_REQUIRED"
            ]
          },
          "429": {
            "description": "Limite de débit dépassée.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              },
              "Retry-After": {
                "schema": {
                  "type": "string",
                  "const": "60"
                },
                "description": "Le serveur renvoie toujours 60 ; cela ne garantit pas qu'un quota horaire a expiré."
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "RATE_LIMITED"
            ]
          },
          "503": {
            "description": "Une intégration ou un stockage est indisponible, un événement est en cours de traitement, ou le service est indisponible.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "DATABASE_NOT_CONFIGURED",
              "ORIGIN_NOT_CONFIGURED",
              "TELEGRAM_NOT_CONFIGURED",
              "UPDATE_IN_PROGRESS",
              "SERVICE_UNAVAILABLE",
              "GATEWAY_NOT_CONNECTED"
            ]
          }
        }
      }
    },
    "/api/payment-links/{id}": {
      "parameters": [
        {
          "name": "id",
          "in": "path",
          "required": true,
          "schema": {
            "type": "string",
            "pattern": "^lnk_[0-9a-f-]{36}$"
          },
          "description": "Identifiant de lien possédé, pas le jeton public."
        }
      ],
      "get": {
        "tags": [
          "Payment links"
        ],
        "operationId": "getOwnPaymentLink",
        "summary": "Lien possédé et calcul enregistré",
        "security": [
          {
            "cookieSession": []
          }
        ],
        "responses": {
          "200": {
            "description": "L'identifiant d'un autre utilisateur et un identifiant manquant renvoient tous deux 404.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PaymentLinkResponse"
                }
              }
            }
          },
          "401": {
            "description": "Aucune session valide ; pour un webhook, un secret invalide.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "AUTH_REQUIRED",
              "SESSION_EXPIRED",
              "WEBHOOK_UNAUTHORIZED"
            ]
          },
          "404": {
            "description": "L'enregistrement est manquant, le jeton public est invalide, ou l'enregistrement demandé appartient à un autre utilisateur.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "TICKET_NOT_FOUND",
              "LINK_NOT_FOUND"
            ]
          },
          "503": {
            "description": "Une intégration ou un stockage est indisponible, un événement est en cours de traitement, ou le service est indisponible.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "DATABASE_NOT_CONFIGURED",
              "ORIGIN_NOT_CONFIGURED",
              "TELEGRAM_NOT_CONFIGURED",
              "UPDATE_IN_PROGRESS",
              "SERVICE_UNAVAILABLE",
              "GATEWAY_NOT_CONNECTED"
            ]
          }
        },
        "parameters": [
          {
            "name": "X-Kevmi-Store",
            "in": "header",
            "required": false,
            "schema": {
              "type": "string",
              "pattern": "^sto_[A-Za-z0-9_-]+$"
            },
            "description": "Sélectionnez une boutique possédée pour une session de navigateur. Omett pour utiliser la première boutique. Les clés API ignorent cet en-tête et utilisent toujours leur liaison de boutique immuable. La valeur all est en lecture seule et n'est prise en charge que par le tableau de bord et les points de terminaison de liste de factures."
          }
        ]
      },
      "patch": {
        "tags": [
          "Payment links"
        ],
        "operationId": "changePaymentLinkStatus",
        "summary": "Suspendre, reprendre ou annuler un lien",
        "security": [
          {
            "cookieSession": [],
            "csrfToken": []
          }
        ],
        "parameters": [
          {
            "name": "Origin",
            "in": "header",
            "required": true,
            "description": "Doit correspondre exactement à APP_ORIGIN. Sec-Fetch-Site : cross-site est rejeté. Le serveur ne fournit pas d'accès CORS.",
            "schema": {
              "type": "string",
              "format": "uri"
            }
          },
          {
            "name": "X-CSRF-Token",
            "in": "header",
            "required": true,
            "description": "Jeton de session actuel depuis GET /api/auth/me ou achèvement réussi de la connexion.",
            "schema": {
              "type": "string",
              "pattern": "^[A-Za-z0-9_-]{43}$"
            }
          },
          {
            "name": "X-Kevmi-Store",
            "in": "header",
            "required": false,
            "schema": {
              "type": "string",
              "pattern": "^sto_[A-Za-z0-9_-]+$"
            },
            "description": "Sélectionnez une boutique possédée pour une session de navigateur. Omett pour utiliser la première boutique. Les clés API ignorent cet en-tête et utilisent toujours leur liaison de boutique immuable. La valeur all est en lecture seule et n'est prise en charge que par le tableau de bord et les points de terminaison de liste de factures."
          }
        ],
        "description": "Modifie uniquement le statut. La mise à jour et l'événement facture.updated immuable sont validés atomiquement. Les modifications simultanées utilisent une vérification de version ; actualisez et réessayez en cas de LINK_CHANGED. cancelled est terminal, l'expiration n'est jamais prolongée, et le prix, les méthodes, les parts de coûts et la description restent figés. Répéter le statut enregistré renvoie la facture actuelle.",
        "requestBody": {
          "required": true,
          "description": "Objet JSON. Maximum 16 384 octets. Les champs inconnus sont rejetés.",
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "status": {
                    "type": "string",
                    "enum": [
                      "active",
                      "paused",
                      "cancelled"
                    ]
                  }
                },
                "required": [
                  "status"
                ],
                "additionalProperties": false
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "État actuel du lien possédé.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PaymentLinkResponse"
                }
              }
            }
          },
          "400": {
            "description": "JSON invalide, champ inconnu ou valeur invalide.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "INVALID_JSON",
              "UNKNOWN_FIELD",
              "INVALID_INPUT",
              "INVALID_VAT_PAYER",
              "INVALID_NOTIFICATIONS",
              "INVALID_TICKET_STATUS",
              "INVALID_UPDATE",
              "INVALID_HISTORY",
              "INVALID_PAGE_CONTEXT",
              "INVALID_LOCALE",
              "INVALID_FILTER",
              "INVALID_NOTIFICATION_SELECTION",
              "INVALID_AMOUNT",
              "INVALID_CURRENCY",
              "INVALID_METHODS",
              "INVALID_EXPIRY",
              "INVALID_QUOTE",
              "INVALID_LIMIT",
              "INVALID_CURSOR",
              "INVALID_STATUS",
              "IDEMPOTENCY_KEY_REQUIRED",
              "METHOD_UNAVAILABLE"
            ]
          },
          "401": {
            "description": "Aucune session valide ; pour un webhook, un secret invalide.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "AUTH_REQUIRED",
              "SESSION_EXPIRED",
              "WEBHOOK_UNAUTHORIZED"
            ]
          },
          "403": {
            "description": "La validation d'origine, CSRF ou de liaison navigateur a échoué.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "INVALID_ORIGIN",
              "CSRF_REQUIRED",
              "BROWSER_PROOF_REQUIRED",
              "INVALID_CHALLENGE"
            ]
          },
          "404": {
            "description": "L'enregistrement est manquant, le jeton public est invalide, ou l'enregistrement demandé appartient à un autre utilisateur.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "TICKET_NOT_FOUND",
              "LINK_NOT_FOUND"
            ]
          },
          "409": {
            "description": "La demande de connexion est utilisée ou annulée, l'identité n'est pas confirmée, ou une demande d'assistance entre en conflit.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "CHALLENGE_UNAVAILABLE",
              "IDENTITY_INCOMPLETE",
              "TICKET_CONFLICT",
              "IDEMPOTENCY_CONFLICT",
              "LINK_CANCELLED",
              "LINK_EXPIRED",
              "LINK_UNAVAILABLE"
            ]
          },
          "413": {
            "description": "Le corps dépasse 16 384 octets ; la limite est de 65 536 octets pour /api/support/ask et le webhook Telegram.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "REQUEST_TOO_LARGE"
            ]
          },
          "415": {
            "description": "Content-Type : application/json est requis.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "JSON_REQUIRED"
            ]
          },
          "503": {
            "description": "Une intégration ou un stockage est indisponible, un événement est en cours de traitement, ou le service est indisponible.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "DATABASE_NOT_CONFIGURED",
              "ORIGIN_NOT_CONFIGURED",
              "TELEGRAM_NOT_CONFIGURED",
              "UPDATE_IN_PROGRESS",
              "SERVICE_UNAVAILABLE",
              "GATEWAY_NOT_CONNECTED"
            ]
          }
        }
      }
    },
    "/api/checkout/{token}": {
      "parameters": [
        {
          "name": "token",
          "in": "path",
          "required": true,
          "schema": {
            "type": "string",
            "pattern": "^[A-Za-z0-9_-]{43}$"
          },
          "description": "Identifiant public aléatoire de l'URL de paiement. Accorde l'accès en lecture uniquement à cette facture."
        }
      ],
      "get": {
        "tags": [
          "Checkout"
        ],
        "operationId": "getPublicCheckout",
        "summary": "Détails de facture publique pour le client",
        "description": "Lire le devis de facture publique immuable et son instantané de paiement persisté. method doit appartenir à la facture. Aucun compte n'est requis. N'expose pas l'identité du propriétaire, les métadonnées privées ni les revenus du marchand. Le jeton accorde l'accès uniquement à ce paiement. availability reflète la configuration, l'allocation, les limites techniques et l'approbation du marchand ; la confirmation de paiement est séparée.",
        "parameters": [
          {
            "name": "method",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "card",
                "alipay",
                "sbp",
                "crypto"
              ]
            },
            "description": "Doit être présent dans les méthodes enregistrées de la facture."
          }
        ],
        "responses": {
          "200": {
            "description": "Facture publique et paiement existant, s'il a été émis.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "invoice": {
                      "$ref": "#/components/schemas/CheckoutInvoice"
                    },
                    "payment": {
                      "oneOf": [
                        {
                          "$ref": "#/components/schemas/CheckoutPayment"
                        },
                        {
                          "type": "null"
                        }
                      ]
                    }
                  },
                  "required": [
                    "invoice",
                    "payment"
                  ],
                  "additionalProperties": false
                }
              }
            }
          },
          "400": {
            "description": "JSON invalide, champ inconnu ou valeur invalide.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "INVALID_JSON",
              "UNKNOWN_FIELD",
              "INVALID_INPUT",
              "INVALID_VAT_PAYER",
              "INVALID_NOTIFICATIONS",
              "INVALID_TICKET_STATUS",
              "INVALID_UPDATE",
              "INVALID_HISTORY",
              "INVALID_PAGE_CONTEXT",
              "INVALID_LOCALE",
              "INVALID_FILTER",
              "INVALID_NOTIFICATION_SELECTION",
              "INVALID_AMOUNT",
              "INVALID_CURRENCY",
              "INVALID_METHODS",
              "INVALID_EXPIRY",
              "INVALID_QUOTE",
              "INVALID_LIMIT",
              "INVALID_CURSOR",
              "INVALID_STATUS",
              "IDEMPOTENCY_KEY_REQUIRED",
              "METHOD_UNAVAILABLE"
            ]
          },
          "404": {
            "description": "L'enregistrement est manquant, le jeton public est invalide, ou l'enregistrement demandé appartient à un autre utilisateur.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "TICKET_NOT_FOUND",
              "LINK_NOT_FOUND"
            ]
          },
          "429": {
            "description": "Limite de débit dépassée.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              },
              "Retry-After": {
                "schema": {
                  "type": "string",
                  "const": "60"
                },
                "description": "Le serveur renvoie toujours 60 ; cela ne garantit pas qu'un quota horaire a expiré."
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "RATE_LIMITED"
            ]
          },
          "503": {
            "description": "Une intégration ou un stockage est indisponible, un événement est en cours de traitement, ou le service est indisponible.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "DATABASE_NOT_CONFIGURED",
              "ORIGIN_NOT_CONFIGURED",
              "TELEGRAM_NOT_CONFIGURED",
              "UPDATE_IN_PROGRESS",
              "SERVICE_UNAVAILABLE",
              "GATEWAY_NOT_CONNECTED"
            ]
          }
        }
      }
    },
    "/api/checkout/{token}/pay": {
      "parameters": [
        {
          "name": "token",
          "in": "path",
          "required": true,
          "schema": {
            "type": "string",
            "pattern": "^[A-Za-z0-9_-]{43}$"
          },
          "description": "Identifiant public aléatoire de l'URL de paiement. Accorde l'accès en lecture uniquement à cette facture."
        }
      ],
      "post": {
        "tags": [
          "Checkout"
        ],
        "operationId": "startCheckoutPayment",
        "summary": "Démarrer ou reprendre le paiement de la facture",
        "description": "Opération acheteur de même origine ; aucun compte ni jeton CSRF requis. Le serveur réserve une opération et une clé d'idempotence stable avant l'envoi à la passerelle. Une répétition récupère la même opération ; une réponse perdue doit être résolue via le statut. La méthode de paiement se verrouille après l'envoi. Les données de carte doivent être chiffrées par le navigateur avec une intention de paiement fraîche. SBP/crypto utilisent le total client enregistré ; carte/Alipay utilisent la configuration fiscale immuable prise en charge. Les nouveaux frais nécessitent une demande marchande approuvée. Les paiements envoyés existants peuvent être récupérés après l'expiration de la facture, une pause ou des changements d'approbation. Aucune route /v2 ni de carte hébergée n'est utilisée.",
        "parameters": [
          {
            "name": "Origin",
            "in": "header",
            "required": true,
            "description": "Doit correspondre exactement à APP_ORIGIN. Sec-Fetch-Site : cross-site est rejeté. Le serveur ne fournit pas d'accès CORS.",
            "schema": {
              "type": "string",
              "format": "uri"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "description": "Objet JSON. Maximum 16 384 octets. Les champs inconnus sont rejetés.",
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/StartCheckoutPayment"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Opération existante récupérée ; aucun second paiement.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "payment": {
                      "oneOf": [
                        {
                          "$ref": "#/components/schemas/CheckoutPayment"
                        },
                        {
                          "type": "null"
                        }
                      ]
                    },
                    "replayed": {
                      "type": "boolean"
                    }
                  },
                  "required": [
                    "payment"
                  ],
                  "additionalProperties": false
                }
              }
            }
          },
          "202": {
            "description": "Opération envoyée ; l'achèvement est asynchrone.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "payment": {
                      "oneOf": [
                        {
                          "$ref": "#/components/schemas/CheckoutPayment"
                        },
                        {
                          "type": "null"
                        }
                      ]
                    },
                    "replayed": {
                      "type": "boolean"
                    }
                  },
                  "required": [
                    "payment"
                  ],
                  "additionalProperties": false
                }
              }
            }
          },
          "400": {
            "description": "JSON invalide, champ inconnu ou valeur invalide.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "INVALID_JSON",
              "UNKNOWN_FIELD",
              "INVALID_INPUT",
              "INVALID_VAT_PAYER",
              "INVALID_NOTIFICATIONS",
              "INVALID_TICKET_STATUS",
              "INVALID_UPDATE",
              "INVALID_HISTORY",
              "INVALID_PAGE_CONTEXT",
              "INVALID_LOCALE",
              "INVALID_FILTER",
              "INVALID_NOTIFICATION_SELECTION",
              "INVALID_AMOUNT",
              "INVALID_CURRENCY",
              "INVALID_METHODS",
              "INVALID_EXPIRY",
              "INVALID_QUOTE",
              "INVALID_LIMIT",
              "INVALID_CURSOR",
              "INVALID_STATUS",
              "IDEMPOTENCY_KEY_REQUIRED",
              "METHOD_UNAVAILABLE"
            ]
          },
          "403": {
            "description": "La validation d'origine, CSRF ou de liaison navigateur a échoué.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "INVALID_ORIGIN",
              "CSRF_REQUIRED",
              "BROWSER_PROOF_REQUIRED",
              "INVALID_CHALLENGE"
            ]
          },
          "404": {
            "description": "L'enregistrement est manquant, le jeton public est invalide, ou l'enregistrement demandé appartient à un autre utilisateur.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "TICKET_NOT_FOUND",
              "LINK_NOT_FOUND"
            ]
          },
          "409": {
            "description": "La demande de connexion est utilisée ou annulée, l'identité n'est pas confirmée, ou une demande d'assistance entre en conflit.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "CHALLENGE_UNAVAILABLE",
              "IDENTITY_INCOMPLETE",
              "TICKET_CONFLICT",
              "IDEMPOTENCY_CONFLICT",
              "LINK_CANCELLED",
              "LINK_EXPIRED",
              "LINK_UNAVAILABLE"
            ]
          },
          "410": {
            "description": "L'intention de chiffrement de la passerelle ou le défi bancaire a expiré. Obtenez une nouvelle intention pour la même opération ; ne créez pas de paiement de remplacement.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "CARD_SESSION_INVALID"
            ]
          },
          "413": {
            "description": "Le corps dépasse 16 384 octets ; la limite est de 65 536 octets pour /api/support/ask et le webhook Telegram.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "REQUEST_TOO_LARGE"
            ]
          },
          "415": {
            "description": "Content-Type : application/json est requis.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "JSON_REQUIRED"
            ]
          },
          "429": {
            "description": "Limite de débit dépassée.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              },
              "Retry-After": {
                "schema": {
                  "type": "string",
                  "const": "60"
                },
                "description": "Le serveur renvoie toujours 60 ; cela ne garantit pas qu'un quota horaire a expiré."
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "RATE_LIMITED"
            ]
          },
          "502": {
            "description": "Réponse de la passerelle indisponible, malformée ou non vérifiable. Lisez le statut de paiement existant avant de tenter toute nouvelle tentative.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "GATEWAY_REQUEST_UNCERTAIN",
              "GATEWAY_INVALID_RESPONSE"
            ]
          },
          "503": {
            "description": "Une intégration ou un stockage est indisponible, un événement est en cours de traitement, ou le service est indisponible.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "DATABASE_NOT_CONFIGURED",
              "ORIGIN_NOT_CONFIGURED",
              "TELEGRAM_NOT_CONFIGURED",
              "UPDATE_IN_PROGRESS",
              "SERVICE_UNAVAILABLE",
              "GATEWAY_NOT_CONNECTED"
            ]
          }
        }
      }
    },
    "/api/v1/payments": {
      "get": {
        "tags": [
          "Merchant API"
        ],
        "operationId": "listOwnPaymentLinksViaPayments",
        "summary": "Lister les liens de paiement possédés",
        "security": [
          {
            "merchantKey": []
          }
        ],
        "description": "Triés par createdAt DESC, id DESC. Les liens publics fonctionnent sur tous les appareils ; la liste nécessite un compte.",
        "parameters": [
          {
            "name": "limit",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 30
            }
          },
          {
            "name": "cursor",
            "in": "query",
            "schema": {
              "type": "string"
            },
            "description": "nextCursor de la réponse précédente. Le service utilise JSON base64 [createdAt,id] ; passez-le inchangé via URLSearchParams."
          }
        ],
        "responses": {
          "200": {
            "description": "Une page de liens de paiement possédés.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PaymentLinksPage"
                }
              }
            }
          },
          "400": {
            "description": "JSON invalide, champ inconnu ou valeur invalide.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "INVALID_JSON",
              "UNKNOWN_FIELD",
              "INVALID_INPUT",
              "INVALID_VAT_PAYER",
              "INVALID_NOTIFICATIONS",
              "INVALID_TICKET_STATUS",
              "INVALID_UPDATE",
              "INVALID_HISTORY",
              "INVALID_PAGE_CONTEXT",
              "INVALID_LOCALE",
              "INVALID_FILTER",
              "INVALID_NOTIFICATION_SELECTION",
              "INVALID_AMOUNT",
              "INVALID_CURRENCY",
              "INVALID_METHODS",
              "INVALID_EXPIRY",
              "INVALID_QUOTE",
              "INVALID_LIMIT",
              "INVALID_CURSOR",
              "INVALID_STATUS",
              "IDEMPOTENCY_KEY_REQUIRED",
              "METHOD_UNAVAILABLE"
            ]
          },
          "401": {
            "description": "Aucune session valide ; pour un webhook, un secret invalide.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "AUTH_REQUIRED",
              "SESSION_EXPIRED",
              "WEBHOOK_UNAUTHORIZED"
            ]
          },
          "503": {
            "description": "Une intégration ou un stockage est indisponible, un événement est en cours de traitement, ou le service est indisponible.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "DATABASE_NOT_CONFIGURED",
              "ORIGIN_NOT_CONFIGURED",
              "TELEGRAM_NOT_CONFIGURED",
              "UPDATE_IN_PROGRESS",
              "SERVICE_UNAVAILABLE",
              "GATEWAY_NOT_CONNECTED"
            ]
          }
        },
        "x-required-scope": "invoices:read"
      },
      "post": {
        "tags": [
          "Merchant API"
        ],
        "operationId": "createOwnPaymentLinkViaPayments",
        "summary": "Créer une facture avec conditions immuables",
        "security": [
          {
            "merchantKey": []
          }
        ],
        "parameters": [
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string",
              "pattern": "^[A-Za-z0-9_-]{16,100}$"
            },
            "description": "Une clé par demande logique. Une répétition avec une entrée canonique identique renvoie l'enregistrement original ; une entrée différente avec la même clé renvoie 409."
          }
        ],
        "description": "Créer une facture immuable et recevoir immédiatement checkoutUrl ; cela ne débite pas l'acheteur. USD : carte/Alipay ; RUB : SBP ; USDT : crypto. L'USD crypto reste accepté pour compatibilité mais ne peut pas être payé sans facture USDT. Les parts client des frais et de la TVA sont des entiers indépendants de 0 à 100 ; la carte/Alipay live actuelle prend en charge TVA client=100 et frais client=0. Le total client n'est jamais modifié silencieusement. La dernière application du site Web doit être approuvée avant un nouveau débit live. La création de facture et son événement marchand sont validés atomiquement.",
        "requestBody": {
          "required": true,
          "description": "Objet JSON. Maximum 16 384 octets. Les champs inconnus sont rejetés.",
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreatePaymentLink"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Relecture idempotente ; replayed=true.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CreatePaymentLinkResponse"
                }
              }
            }
          },
          "201": {
            "description": "Facture créée ; replayed=false.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CreatePaymentLinkResponse"
                }
              }
            }
          },
          "400": {
            "description": "JSON invalide, champ inconnu ou valeur invalide.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "INVALID_JSON",
              "UNKNOWN_FIELD",
              "INVALID_INPUT",
              "INVALID_VAT_PAYER",
              "INVALID_NOTIFICATIONS",
              "INVALID_TICKET_STATUS",
              "INVALID_UPDATE",
              "INVALID_HISTORY",
              "INVALID_PAGE_CONTEXT",
              "INVALID_LOCALE",
              "INVALID_FILTER",
              "INVALID_NOTIFICATION_SELECTION",
              "INVALID_AMOUNT",
              "INVALID_CURRENCY",
              "INVALID_METHODS",
              "INVALID_EXPIRY",
              "INVALID_QUOTE",
              "INVALID_LIMIT",
              "INVALID_CURSOR",
              "INVALID_STATUS",
              "IDEMPOTENCY_KEY_REQUIRED",
              "METHOD_UNAVAILABLE"
            ]
          },
          "401": {
            "description": "Aucune session valide ; pour un webhook, un secret invalide.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "AUTH_REQUIRED",
              "SESSION_EXPIRED",
              "WEBHOOK_UNAUTHORIZED"
            ]
          },
          "403": {
            "description": "La validation d'origine, CSRF ou de liaison navigateur a échoué.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "INVALID_ORIGIN",
              "CSRF_REQUIRED",
              "BROWSER_PROOF_REQUIRED",
              "INVALID_CHALLENGE"
            ]
          },
          "409": {
            "description": "La demande de connexion est utilisée ou annulée, l'identité n'est pas confirmée, ou une demande d'assistance entre en conflit.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "CHALLENGE_UNAVAILABLE",
              "IDENTITY_INCOMPLETE",
              "TICKET_CONFLICT",
              "IDEMPOTENCY_CONFLICT",
              "LINK_CANCELLED",
              "LINK_EXPIRED",
              "LINK_UNAVAILABLE"
            ]
          },
          "413": {
            "description": "Le corps dépasse 16 384 octets ; la limite est de 65 536 octets pour /api/support/ask et le webhook Telegram.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "REQUEST_TOO_LARGE"
            ]
          },
          "415": {
            "description": "Content-Type : application/json est requis.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "JSON_REQUIRED"
            ]
          },
          "429": {
            "description": "Limite de débit dépassée.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              },
              "Retry-After": {
                "schema": {
                  "type": "string",
                  "const": "60"
                },
                "description": "Le serveur renvoie toujours 60 ; cela ne garantit pas qu'un quota horaire a expiré."
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "RATE_LIMITED"
            ]
          },
          "503": {
            "description": "Une intégration ou un stockage est indisponible, un événement est en cours de traitement, ou le service est indisponible.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "DATABASE_NOT_CONFIGURED",
              "ORIGIN_NOT_CONFIGURED",
              "TELEGRAM_NOT_CONFIGURED",
              "UPDATE_IN_PROGRESS",
              "SERVICE_UNAVAILABLE",
              "GATEWAY_NOT_CONNECTED"
            ]
          }
        },
        "x-required-scope": "invoices:write"
      }
    },
    "/api/v1/payments/{id}": {
      "parameters": [
        {
          "name": "id",
          "in": "path",
          "required": true,
          "schema": {
            "type": "string",
            "pattern": "^lnk_[0-9a-f-]{36}$"
          },
          "description": "Identifiant de lien possédé, pas le jeton public."
        }
      ],
      "get": {
        "tags": [
          "Merchant API"
        ],
        "operationId": "getOwnPaymentLinkViaPayments",
        "summary": "Lien possédé et calcul enregistré",
        "security": [
          {
            "merchantKey": []
          }
        ],
        "responses": {
          "200": {
            "description": "L'identifiant d'un autre utilisateur et un identifiant manquant renvoient tous deux 404.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PaymentLinkResponse"
                }
              }
            }
          },
          "401": {
            "description": "Aucune session valide ; pour un webhook, un secret invalide.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "AUTH_REQUIRED",
              "SESSION_EXPIRED",
              "WEBHOOK_UNAUTHORIZED"
            ]
          },
          "404": {
            "description": "L'enregistrement est manquant, le jeton public est invalide, ou l'enregistrement demandé appartient à un autre utilisateur.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "TICKET_NOT_FOUND",
              "LINK_NOT_FOUND"
            ]
          },
          "503": {
            "description": "Une intégration ou un stockage est indisponible, un événement est en cours de traitement, ou le service est indisponible.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "DATABASE_NOT_CONFIGURED",
              "ORIGIN_NOT_CONFIGURED",
              "TELEGRAM_NOT_CONFIGURED",
              "UPDATE_IN_PROGRESS",
              "SERVICE_UNAVAILABLE",
              "GATEWAY_NOT_CONNECTED"
            ]
          }
        },
        "parameters": [],
        "x-required-scope": "invoices:read"
      },
      "patch": {
        "tags": [
          "Merchant API"
        ],
        "operationId": "changePaymentLinkStatusViaPayments",
        "summary": "Suspendre, reprendre ou annuler un lien",
        "security": [
          {
            "merchantKey": []
          }
        ],
        "parameters": [],
        "description": "Modifie uniquement le statut. La mise à jour et l'événement facture.updated immuable sont validés atomiquement. Les modifications simultanées utilisent une vérification de version ; actualisez et réessayez en cas de LINK_CHANGED. cancelled est terminal, l'expiration n'est jamais prolongée, et le prix, les méthodes, les parts de coûts et la description restent figés. Répéter le statut enregistré renvoie la facture actuelle.",
        "requestBody": {
          "required": true,
          "description": "Objet JSON. Maximum 16 384 octets. Les champs inconnus sont rejetés.",
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "status": {
                    "type": "string",
                    "enum": [
                      "active",
                      "paused",
                      "cancelled"
                    ]
                  }
                },
                "required": [
                  "status"
                ],
                "additionalProperties": false
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "État actuel du lien possédé.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PaymentLinkResponse"
                }
              }
            }
          },
          "400": {
            "description": "JSON invalide, champ inconnu ou valeur invalide.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "INVALID_JSON",
              "UNKNOWN_FIELD",
              "INVALID_INPUT",
              "INVALID_VAT_PAYER",
              "INVALID_NOTIFICATIONS",
              "INVALID_TICKET_STATUS",
              "INVALID_UPDATE",
              "INVALID_HISTORY",
              "INVALID_PAGE_CONTEXT",
              "INVALID_LOCALE",
              "INVALID_FILTER",
              "INVALID_NOTIFICATION_SELECTION",
              "INVALID_AMOUNT",
              "INVALID_CURRENCY",
              "INVALID_METHODS",
              "INVALID_EXPIRY",
              "INVALID_QUOTE",
              "INVALID_LIMIT",
              "INVALID_CURSOR",
              "INVALID_STATUS",
              "IDEMPOTENCY_KEY_REQUIRED",
              "METHOD_UNAVAILABLE"
            ]
          },
          "401": {
            "description": "Aucune session valide ; pour un webhook, un secret invalide.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "AUTH_REQUIRED",
              "SESSION_EXPIRED",
              "WEBHOOK_UNAUTHORIZED"
            ]
          },
          "403": {
            "description": "La validation d'origine, CSRF ou de liaison navigateur a échoué.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "INVALID_ORIGIN",
              "CSRF_REQUIRED",
              "BROWSER_PROOF_REQUIRED",
              "INVALID_CHALLENGE"
            ]
          },
          "404": {
            "description": "L'enregistrement est manquant, le jeton public est invalide, ou l'enregistrement demandé appartient à un autre utilisateur.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "TICKET_NOT_FOUND",
              "LINK_NOT_FOUND"
            ]
          },
          "409": {
            "description": "La demande de connexion est utilisée ou annulée, l'identité n'est pas confirmée, ou une demande d'assistance entre en conflit.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "CHALLENGE_UNAVAILABLE",
              "IDENTITY_INCOMPLETE",
              "TICKET_CONFLICT",
              "IDEMPOTENCY_CONFLICT",
              "LINK_CANCELLED",
              "LINK_EXPIRED",
              "LINK_UNAVAILABLE"
            ]
          },
          "413": {
            "description": "Le corps dépasse 16 384 octets ; la limite est de 65 536 octets pour /api/support/ask et le webhook Telegram.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "REQUEST_TOO_LARGE"
            ]
          },
          "415": {
            "description": "Content-Type : application/json est requis.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "JSON_REQUIRED"
            ]
          },
          "503": {
            "description": "Une intégration ou un stockage est indisponible, un événement est en cours de traitement, ou le service est indisponible.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "DATABASE_NOT_CONFIGURED",
              "ORIGIN_NOT_CONFIGURED",
              "TELEGRAM_NOT_CONFIGURED",
              "UPDATE_IN_PROGRESS",
              "SERVICE_UNAVAILABLE",
              "GATEWAY_NOT_CONNECTED"
            ]
          }
        },
        "x-required-scope": "invoices:write"
      }
    },
    "/api/v1/payment-links": {
      "get": {
        "tags": [
          "Merchant API"
        ],
        "operationId": "listOwnPaymentLinksViaLinks",
        "summary": "Lister les liens de paiement possédés",
        "security": [
          {
            "merchantKey": []
          }
        ],
        "description": "Triés par createdAt DESC, id DESC. Les liens publics fonctionnent sur tous les appareils ; la liste nécessite un compte.",
        "parameters": [
          {
            "name": "limit",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 30
            }
          },
          {
            "name": "cursor",
            "in": "query",
            "schema": {
              "type": "string"
            },
            "description": "nextCursor de la réponse précédente. Le service utilise JSON base64 [createdAt,id] ; passez-le inchangé via URLSearchParams."
          }
        ],
        "responses": {
          "200": {
            "description": "Une page de liens de paiement possédés.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PaymentLinksPage"
                }
              }
            }
          },
          "400": {
            "description": "JSON invalide, champ inconnu ou valeur invalide.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "INVALID_JSON",
              "UNKNOWN_FIELD",
              "INVALID_INPUT",
              "INVALID_VAT_PAYER",
              "INVALID_NOTIFICATIONS",
              "INVALID_TICKET_STATUS",
              "INVALID_UPDATE",
              "INVALID_HISTORY",
              "INVALID_PAGE_CONTEXT",
              "INVALID_LOCALE",
              "INVALID_FILTER",
              "INVALID_NOTIFICATION_SELECTION",
              "INVALID_AMOUNT",
              "INVALID_CURRENCY",
              "INVALID_METHODS",
              "INVALID_EXPIRY",
              "INVALID_QUOTE",
              "INVALID_LIMIT",
              "INVALID_CURSOR",
              "INVALID_STATUS",
              "IDEMPOTENCY_KEY_REQUIRED",
              "METHOD_UNAVAILABLE"
            ]
          },
          "401": {
            "description": "Aucune session valide ; pour un webhook, un secret invalide.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "AUTH_REQUIRED",
              "SESSION_EXPIRED",
              "WEBHOOK_UNAUTHORIZED"
            ]
          },
          "503": {
            "description": "Une intégration ou un stockage est indisponible, un événement est en cours de traitement, ou le service est indisponible.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "DATABASE_NOT_CONFIGURED",
              "ORIGIN_NOT_CONFIGURED",
              "TELEGRAM_NOT_CONFIGURED",
              "UPDATE_IN_PROGRESS",
              "SERVICE_UNAVAILABLE",
              "GATEWAY_NOT_CONNECTED"
            ]
          }
        },
        "x-required-scope": "invoices:read"
      },
      "post": {
        "tags": [
          "Merchant API"
        ],
        "operationId": "createOwnPaymentLinkViaLinks",
        "summary": "Créer une facture avec conditions immuables",
        "security": [
          {
            "merchantKey": []
          }
        ],
        "parameters": [
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string",
              "pattern": "^[A-Za-z0-9_-]{16,100}$"
            },
            "description": "Une clé par demande logique. Une répétition avec une entrée canonique identique renvoie l'enregistrement original ; une entrée différente avec la même clé renvoie 409."
          }
        ],
        "description": "Créer une facture immuable et recevoir immédiatement checkoutUrl ; cela ne débite pas l'acheteur. USD : carte/Alipay ; RUB : SBP ; USDT : crypto. L'USD crypto reste accepté pour compatibilité mais ne peut pas être payé sans facture USDT. Les parts client des frais et de la TVA sont des entiers indépendants de 0 à 100 ; la carte/Alipay live actuelle prend en charge TVA client=100 et frais client=0. Le total client n'est jamais modifié silencieusement. La dernière application du site Web doit être approuvée avant un nouveau débit live. La création de facture et son événement marchand sont validés atomiquement.",
        "requestBody": {
          "required": true,
          "description": "Objet JSON. Maximum 16 384 octets. Les champs inconnus sont rejetés.",
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreatePaymentLink"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Relecture idempotente ; replayed=true.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CreatePaymentLinkResponse"
                }
              }
            }
          },
          "201": {
            "description": "Facture créée ; replayed=false.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CreatePaymentLinkResponse"
                }
              }
            }
          },
          "400": {
            "description": "JSON invalide, champ inconnu ou valeur invalide.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "INVALID_JSON",
              "UNKNOWN_FIELD",
              "INVALID_INPUT",
              "INVALID_VAT_PAYER",
              "INVALID_NOTIFICATIONS",
              "INVALID_TICKET_STATUS",
              "INVALID_UPDATE",
              "INVALID_HISTORY",
              "INVALID_PAGE_CONTEXT",
              "INVALID_LOCALE",
              "INVALID_FILTER",
              "INVALID_NOTIFICATION_SELECTION",
              "INVALID_AMOUNT",
              "INVALID_CURRENCY",
              "INVALID_METHODS",
              "INVALID_EXPIRY",
              "INVALID_QUOTE",
              "INVALID_LIMIT",
              "INVALID_CURSOR",
              "INVALID_STATUS",
              "IDEMPOTENCY_KEY_REQUIRED",
              "METHOD_UNAVAILABLE"
            ]
          },
          "401": {
            "description": "Aucune session valide ; pour un webhook, un secret invalide.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "AUTH_REQUIRED",
              "SESSION_EXPIRED",
              "WEBHOOK_UNAUTHORIZED"
            ]
          },
          "403": {
            "description": "La validation d'origine, CSRF ou de liaison navigateur a échoué.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "INVALID_ORIGIN",
              "CSRF_REQUIRED",
              "BROWSER_PROOF_REQUIRED",
              "INVALID_CHALLENGE"
            ]
          },
          "409": {
            "description": "La demande de connexion est utilisée ou annulée, l'identité n'est pas confirmée, ou une demande d'assistance entre en conflit.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "CHALLENGE_UNAVAILABLE",
              "IDENTITY_INCOMPLETE",
              "TICKET_CONFLICT",
              "IDEMPOTENCY_CONFLICT",
              "LINK_CANCELLED",
              "LINK_EXPIRED",
              "LINK_UNAVAILABLE"
            ]
          },
          "413": {
            "description": "Le corps dépasse 16 384 octets ; la limite est de 65 536 octets pour /api/support/ask et le webhook Telegram.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "REQUEST_TOO_LARGE"
            ]
          },
          "415": {
            "description": "Content-Type : application/json est requis.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "JSON_REQUIRED"
            ]
          },
          "429": {
            "description": "Limite de débit dépassée.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              },
              "Retry-After": {
                "schema": {
                  "type": "string",
                  "const": "60"
                },
                "description": "Le serveur renvoie toujours 60 ; cela ne garantit pas qu'un quota horaire a expiré."
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "RATE_LIMITED"
            ]
          },
          "503": {
            "description": "Une intégration ou un stockage est indisponible, un événement est en cours de traitement, ou le service est indisponible.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "DATABASE_NOT_CONFIGURED",
              "ORIGIN_NOT_CONFIGURED",
              "TELEGRAM_NOT_CONFIGURED",
              "UPDATE_IN_PROGRESS",
              "SERVICE_UNAVAILABLE",
              "GATEWAY_NOT_CONNECTED"
            ]
          }
        },
        "x-required-scope": "invoices:write"
      }
    },
    "/api/v1/payment-links/{id}": {
      "parameters": [
        {
          "name": "id",
          "in": "path",
          "required": true,
          "schema": {
            "type": "string",
            "pattern": "^lnk_[0-9a-f-]{36}$"
          },
          "description": "Identifiant de lien possédé, pas le jeton public."
        }
      ],
      "get": {
        "tags": [
          "Merchant API"
        ],
        "operationId": "getOwnPaymentLinkViaLinks",
        "summary": "Lien possédé et calcul enregistré",
        "security": [
          {
            "merchantKey": []
          }
        ],
        "responses": {
          "200": {
            "description": "L'identifiant d'un autre utilisateur et un identifiant manquant renvoient tous deux 404.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PaymentLinkResponse"
                }
              }
            }
          },
          "401": {
            "description": "Aucune session valide ; pour un webhook, un secret invalide.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "AUTH_REQUIRED",
              "SESSION_EXPIRED",
              "WEBHOOK_UNAUTHORIZED"
            ]
          },
          "404": {
            "description": "L'enregistrement est manquant, le jeton public est invalide, ou l'enregistrement demandé appartient à un autre utilisateur.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "TICKET_NOT_FOUND",
              "LINK_NOT_FOUND"
            ]
          },
          "503": {
            "description": "Une intégration ou un stockage est indisponible, un événement est en cours de traitement, ou le service est indisponible.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "DATABASE_NOT_CONFIGURED",
              "ORIGIN_NOT_CONFIGURED",
              "TELEGRAM_NOT_CONFIGURED",
              "UPDATE_IN_PROGRESS",
              "SERVICE_UNAVAILABLE",
              "GATEWAY_NOT_CONNECTED"
            ]
          }
        },
        "parameters": [],
        "x-required-scope": "invoices:read"
      },
      "patch": {
        "tags": [
          "Merchant API"
        ],
        "operationId": "changePaymentLinkStatusViaLinks",
        "summary": "Suspendre, reprendre ou annuler un lien",
        "security": [
          {
            "merchantKey": []
          }
        ],
        "parameters": [],
        "description": "Modifie uniquement le statut. La mise à jour et l'événement facture.updated immuable sont validés atomiquement. Les modifications simultanées utilisent une vérification de version ; actualisez et réessayez en cas de LINK_CHANGED. cancelled est terminal, l'expiration n'est jamais prolongée, et le prix, les méthodes, les parts de coûts et la description restent figés. Répéter le statut enregistré renvoie la facture actuelle.",
        "requestBody": {
          "required": true,
          "description": "Objet JSON. Maximum 16 384 octets. Les champs inconnus sont rejetés.",
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "status": {
                    "type": "string",
                    "enum": [
                      "active",
                      "paused",
                      "cancelled"
                    ]
                  }
                },
                "required": [
                  "status"
                ],
                "additionalProperties": false
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "État actuel du lien possédé.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PaymentLinkResponse"
                }
              }
            }
          },
          "400": {
            "description": "JSON invalide, champ inconnu ou valeur invalide.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "INVALID_JSON",
              "UNKNOWN_FIELD",
              "INVALID_INPUT",
              "INVALID_VAT_PAYER",
              "INVALID_NOTIFICATIONS",
              "INVALID_TICKET_STATUS",
              "INVALID_UPDATE",
              "INVALID_HISTORY",
              "INVALID_PAGE_CONTEXT",
              "INVALID_LOCALE",
              "INVALID_FILTER",
              "INVALID_NOTIFICATION_SELECTION",
              "INVALID_AMOUNT",
              "INVALID_CURRENCY",
              "INVALID_METHODS",
              "INVALID_EXPIRY",
              "INVALID_QUOTE",
              "INVALID_LIMIT",
              "INVALID_CURSOR",
              "INVALID_STATUS",
              "IDEMPOTENCY_KEY_REQUIRED",
              "METHOD_UNAVAILABLE"
            ]
          },
          "401": {
            "description": "Aucune session valide ; pour un webhook, un secret invalide.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "AUTH_REQUIRED",
              "SESSION_EXPIRED",
              "WEBHOOK_UNAUTHORIZED"
            ]
          },
          "403": {
            "description": "La validation d'origine, CSRF ou de liaison navigateur a échoué.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "INVALID_ORIGIN",
              "CSRF_REQUIRED",
              "BROWSER_PROOF_REQUIRED",
              "INVALID_CHALLENGE"
            ]
          },
          "404": {
            "description": "L'enregistrement est manquant, le jeton public est invalide, ou l'enregistrement demandé appartient à un autre utilisateur.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "TICKET_NOT_FOUND",
              "LINK_NOT_FOUND"
            ]
          },
          "409": {
            "description": "La demande de connexion est utilisée ou annulée, l'identité n'est pas confirmée, ou une demande d'assistance entre en conflit.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "CHALLENGE_UNAVAILABLE",
              "IDENTITY_INCOMPLETE",
              "TICKET_CONFLICT",
              "IDEMPOTENCY_CONFLICT",
              "LINK_CANCELLED",
              "LINK_EXPIRED",
              "LINK_UNAVAILABLE"
            ]
          },
          "413": {
            "description": "Le corps dépasse 16 384 octets ; la limite est de 65 536 octets pour /api/support/ask et le webhook Telegram.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "REQUEST_TOO_LARGE"
            ]
          },
          "415": {
            "description": "Content-Type : application/json est requis.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "JSON_REQUIRED"
            ]
          },
          "503": {
            "description": "Une intégration ou un stockage est indisponible, un événement est en cours de traitement, ou le service est indisponible.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "DATABASE_NOT_CONFIGURED",
              "ORIGIN_NOT_CONFIGURED",
              "TELEGRAM_NOT_CONFIGURED",
              "UPDATE_IN_PROGRESS",
              "SERVICE_UNAVAILABLE",
              "GATEWAY_NOT_CONNECTED"
            ]
          }
        },
        "x-required-scope": "invoices:write"
      }
    },
    "/api/api-keys": {
      "get": {
        "tags": [
          "Merchant API"
        ],
        "operationId": "listMerchantKeys",
        "summary": "Lister les métadonnées de ses propres clés",
        "security": [
          {
            "cookieSession": []
          }
        ],
        "responses": {
          "200": {
            "description": "Les secrets et les hachages de secrets sont omis.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "keys": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/MerchantKey"
                      }
                    }
                  },
                  "required": [
                    "keys"
                  ],
                  "additionalProperties": false
                }
              }
            }
          },
          "401": {
            "description": "Aucune session valide ; pour un webhook, un secret invalide.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "AUTH_REQUIRED",
              "SESSION_EXPIRED",
              "WEBHOOK_UNAUTHORIZED"
            ]
          },
          "503": {
            "description": "Une intégration ou un stockage est indisponible, un événement est en cours de traitement, ou le service est indisponible.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "DATABASE_NOT_CONFIGURED",
              "ORIGIN_NOT_CONFIGURED",
              "TELEGRAM_NOT_CONFIGURED",
              "UPDATE_IN_PROGRESS",
              "SERVICE_UNAVAILABLE",
              "GATEWAY_NOT_CONNECTED"
            ]
          }
        },
        "parameters": [
          {
            "name": "X-Kevmi-Store",
            "in": "header",
            "required": false,
            "schema": {
              "type": "string",
              "pattern": "^sto_[A-Za-z0-9_-]+$"
            },
            "description": "Sélectionnez une boutique possédée pour une session de navigateur. Omett pour utiliser la première boutique. Les clés API ignorent cet en-tête et utilisent toujours leur liaison de boutique immuable. La valeur all est en lecture seule et n'est prise en charge que par le tableau de bord et les points de terminaison de liste de factures."
          }
        ]
      },
      "post": {
        "tags": [
          "Merchant API"
        ],
        "operationId": "createMerchantKey",
        "summary": "Créer une clé à portée limitée ; révéler son secret une seule fois",
        "security": [
          {
            "cookieSession": [],
            "csrfToken": []
          }
        ],
        "parameters": [
          {
            "name": "Origin",
            "in": "header",
            "required": true,
            "description": "Doit correspondre exactement à APP_ORIGIN. Sec-Fetch-Site : cross-site est rejeté. Le serveur ne fournit pas d'accès CORS.",
            "schema": {
              "type": "string",
              "format": "uri"
            }
          },
          {
            "name": "X-CSRF-Token",
            "in": "header",
            "required": true,
            "description": "Jeton de session actuel depuis GET /api/auth/me ou achèvement réussi de la connexion.",
            "schema": {
              "type": "string",
              "pattern": "^[A-Za-z0-9_-]{43}$"
            }
          },
          {
            "name": "X-Kevmi-Store",
            "in": "header",
            "required": false,
            "schema": {
              "type": "string",
              "pattern": "^sto_[A-Za-z0-9_-]+$"
            },
            "description": "Sélectionnez une boutique possédée pour une session de navigateur. Omett pour utiliser la première boutique. Les clés API ignorent cet en-tête et utilisent toujours leur liaison de boutique immuable. La valeur all est en lecture seule et n'est prise en charge que par le tableau de bord et les points de terminaison de liste de factures."
          }
        ],
        "description": "Maximum 10 clés actives. Au plus 5 créations/heure/utilisateur. Les clés expirent, peuvent être révoquées et sont limitées à 90 requêtes API/minute/clé.",
        "requestBody": {
          "required": true,
          "description": "Objet JSON. Maximum 16 384 octets. Les champs inconnus sont rejetés.",
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "name": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 60
                  },
                  "scopes": {
                    "type": "array",
                    "minItems": 1,
                    "maxItems": 3,
                    "uniqueItems": true,
                    "items": {
                      "type": "string",
                      "enum": [
                        "invoices:read",
                        "invoices:write",
                        "events:read"
                      ]
                    }
                  },
                  "expiresInDays": {
                    "type": "integer",
                    "minimum": 1,
                    "maximum": 365,
                    "default": 90
                  }
                },
                "required": [
                  "name"
                ],
                "additionalProperties": false
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Stockez le secret de manière sécurisée sur votre serveur.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "key": {
                      "$ref": "#/components/schemas/MerchantKey"
                    },
                    "secret": {
                      "type": "string",
                      "pattern": "^kv_live_[A-Za-z0-9_-]{43}$"
                    }
                  },
                  "required": [
                    "key",
                    "secret"
                  ],
                  "additionalProperties": false
                }
              }
            }
          },
          "400": {
            "description": "JSON invalide, champ inconnu ou valeur invalide.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "INVALID_JSON",
              "UNKNOWN_FIELD",
              "INVALID_INPUT",
              "INVALID_VAT_PAYER",
              "INVALID_NOTIFICATIONS",
              "INVALID_TICKET_STATUS",
              "INVALID_UPDATE",
              "INVALID_HISTORY",
              "INVALID_PAGE_CONTEXT",
              "INVALID_LOCALE",
              "INVALID_FILTER",
              "INVALID_NOTIFICATION_SELECTION",
              "INVALID_AMOUNT",
              "INVALID_CURRENCY",
              "INVALID_METHODS",
              "INVALID_EXPIRY",
              "INVALID_QUOTE",
              "INVALID_LIMIT",
              "INVALID_CURSOR",
              "INVALID_STATUS",
              "IDEMPOTENCY_KEY_REQUIRED",
              "METHOD_UNAVAILABLE"
            ]
          },
          "401": {
            "description": "Aucune session valide ; pour un webhook, un secret invalide.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "AUTH_REQUIRED",
              "SESSION_EXPIRED",
              "WEBHOOK_UNAUTHORIZED"
            ]
          },
          "403": {
            "description": "La validation d'origine, CSRF ou de liaison navigateur a échoué.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "INVALID_ORIGIN",
              "CSRF_REQUIRED",
              "BROWSER_PROOF_REQUIRED",
              "INVALID_CHALLENGE"
            ]
          },
          "409": {
            "description": "La demande de connexion est utilisée ou annulée, l'identité n'est pas confirmée, ou une demande d'assistance entre en conflit.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "CHALLENGE_UNAVAILABLE",
              "IDENTITY_INCOMPLETE",
              "TICKET_CONFLICT",
              "IDEMPOTENCY_CONFLICT",
              "LINK_CANCELLED",
              "LINK_EXPIRED",
              "LINK_UNAVAILABLE"
            ]
          },
          "429": {
            "description": "Limite de débit dépassée.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              },
              "Retry-After": {
                "schema": {
                  "type": "string",
                  "const": "60"
                },
                "description": "Le serveur renvoie toujours 60 ; cela ne garantit pas qu'un quota horaire a expiré."
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "RATE_LIMITED"
            ]
          },
          "503": {
            "description": "Une intégration ou un stockage est indisponible, un événement est en cours de traitement, ou le service est indisponible.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "DATABASE_NOT_CONFIGURED",
              "ORIGIN_NOT_CONFIGURED",
              "TELEGRAM_NOT_CONFIGURED",
              "UPDATE_IN_PROGRESS",
              "SERVICE_UNAVAILABLE",
              "GATEWAY_NOT_CONNECTED"
            ]
          }
        }
      }
    },
    "/api/api-keys/{id}": {
      "parameters": [
        {
          "name": "id",
          "in": "path",
          "required": true,
          "schema": {
            "type": "string",
            "pattern": "^key_[0-9a-f-]{36}$"
          }
        }
      ],
      "delete": {
        "tags": [
          "Merchant API"
        ],
        "operationId": "revokeMerchantKey",
        "summary": "Révoquer sa propre clé",
        "security": [
          {
            "cookieSession": [],
            "csrfToken": []
          }
        ],
        "parameters": [
          {
            "name": "Origin",
            "in": "header",
            "required": true,
            "description": "Doit correspondre exactement à APP_ORIGIN. Sec-Fetch-Site : cross-site est rejeté. Le serveur ne fournit pas d'accès CORS.",
            "schema": {
              "type": "string",
              "format": "uri"
            }
          },
          {
            "name": "X-CSRF-Token",
            "in": "header",
            "required": true,
            "description": "Jeton de session actuel depuis GET /api/auth/me ou achèvement réussi de la connexion.",
            "schema": {
              "type": "string",
              "pattern": "^[A-Za-z0-9_-]{43}$"
            }
          },
          {
            "name": "X-Kevmi-Store",
            "in": "header",
            "required": false,
            "schema": {
              "type": "string",
              "pattern": "^sto_[A-Za-z0-9_-]+$"
            },
            "description": "Sélectionnez une boutique possédée pour une session de navigateur. Omett pour utiliser la première boutique. Les clés API ignorent cet en-tête et utilisent toujours leur liaison de boutique immuable. La valeur all est en lecture seule et n'est prise en charge que par le tableau de bord et les points de terminaison de liste de factures."
          }
        ],
        "responses": {
          "200": {
            "description": "Les utilisations futures de la clé sont rejetées.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "revoked": {
                      "const": true
                    }
                  },
                  "required": [
                    "revoked"
                  ],
                  "additionalProperties": false
                }
              }
            }
          },
          "401": {
            "description": "Aucune session valide ; pour un webhook, un secret invalide.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "AUTH_REQUIRED",
              "SESSION_EXPIRED",
              "WEBHOOK_UNAUTHORIZED"
            ]
          },
          "403": {
            "description": "La validation d'origine, CSRF ou de liaison navigateur a échoué.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "INVALID_ORIGIN",
              "CSRF_REQUIRED",
              "BROWSER_PROOF_REQUIRED",
              "INVALID_CHALLENGE"
            ]
          },
          "404": {
            "description": "L'enregistrement est manquant, le jeton public est invalide, ou l'enregistrement demandé appartient à un autre utilisateur.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "TICKET_NOT_FOUND",
              "LINK_NOT_FOUND"
            ]
          },
          "503": {
            "description": "Une intégration ou un stockage est indisponible, un événement est en cours de traitement, ou le service est indisponible.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "DATABASE_NOT_CONFIGURED",
              "ORIGIN_NOT_CONFIGURED",
              "TELEGRAM_NOT_CONFIGURED",
              "UPDATE_IN_PROGRESS",
              "SERVICE_UNAVAILABLE",
              "GATEWAY_NOT_CONNECTED"
            ]
          }
        }
      }
    },
    "/api/events": {
      "get": {
        "tags": [
          "Merchant API"
        ],
        "operationId": "listOwnEvents",
        "summary": "Lire son propre historique d'événements immuable",
        "security": [
          {
            "cookieSession": []
          }
        ],
        "parameters": [
          {
            "name": "before",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 0,
              "description": "Horodatage Unix en millisecondes, UTC."
            },
            "description": "Limite d'horodatage exclusive héritée. Ne peut pas être combinée avec cursor. Utilisez nextCursor pour une pagination sans perte."
          },
          {
            "name": "cursor",
            "in": "query",
            "schema": {
              "type": "string"
            },
            "description": "nextCursor opaque renvoyé par la page précédente. Encode [createdAt,id] et préserve les événements partageant un même horodatage."
          },
          {
            "name": "X-Kevmi-Store",
            "in": "header",
            "required": false,
            "schema": {
              "type": "string",
              "pattern": "^sto_[A-Za-z0-9_-]+$"
            },
            "description": "Sélectionnez une boutique possédée pour une session de navigateur. Omett pour utiliser la première boutique. Les clés API ignorent cet en-tête et utilisent toujours leur liaison de boutique immuable. La valeur all est en lecture seule et n'est prise en charge que par le tableau de bord et les points de terminaison de liste de factures."
          }
        ],
        "responses": {
          "200": {
            "description": "Plus récents en premier ; jusqu'à 50 événements. Suivez nextCursor jusqu'à null.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "events": {
                      "type": "array",
                      "maxItems": 50,
                      "items": {
                        "$ref": "#/components/schemas/MerchantEvent"
                      }
                    },
                    "nextCursor": {
                      "type": [
                        "string",
                        "null"
                      ]
                    }
                  },
                  "required": [
                    "events",
                    "nextCursor"
                  ],
                  "additionalProperties": false
                }
              }
            }
          },
          "400": {
            "description": "JSON invalide, champ inconnu ou valeur invalide.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "INVALID_JSON",
              "UNKNOWN_FIELD",
              "INVALID_INPUT",
              "INVALID_VAT_PAYER",
              "INVALID_NOTIFICATIONS",
              "INVALID_TICKET_STATUS",
              "INVALID_UPDATE",
              "INVALID_HISTORY",
              "INVALID_PAGE_CONTEXT",
              "INVALID_LOCALE",
              "INVALID_FILTER",
              "INVALID_NOTIFICATION_SELECTION",
              "INVALID_AMOUNT",
              "INVALID_CURRENCY",
              "INVALID_METHODS",
              "INVALID_EXPIRY",
              "INVALID_QUOTE",
              "INVALID_LIMIT",
              "INVALID_CURSOR",
              "INVALID_STATUS",
              "IDEMPOTENCY_KEY_REQUIRED",
              "METHOD_UNAVAILABLE"
            ]
          },
          "401": {
            "description": "Aucune session valide ; pour un webhook, un secret invalide.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "AUTH_REQUIRED",
              "SESSION_EXPIRED",
              "WEBHOOK_UNAUTHORIZED"
            ]
          },
          "503": {
            "description": "Une intégration ou un stockage est indisponible, un événement est en cours de traitement, ou le service est indisponible.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "DATABASE_NOT_CONFIGURED",
              "ORIGIN_NOT_CONFIGURED",
              "TELEGRAM_NOT_CONFIGURED",
              "UPDATE_IN_PROGRESS",
              "SERVICE_UNAVAILABLE",
              "GATEWAY_NOT_CONNECTED"
            ]
          }
        }
      }
    },
    "/api/v1/events": {
      "get": {
        "tags": [
          "Merchant API"
        ],
        "operationId": "listMerchantEventsWithKey",
        "summary": "Lire son propre historique d'événements immuable",
        "security": [
          {
            "merchantKey": []
          }
        ],
        "parameters": [
          {
            "name": "before",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 0,
              "description": "Horodatage Unix en millisecondes, UTC."
            },
            "description": "Limite d'horodatage exclusive héritée. Ne peut pas être combinée avec cursor. Utilisez nextCursor pour une pagination sans perte."
          },
          {
            "name": "cursor",
            "in": "query",
            "schema": {
              "type": "string"
            },
            "description": "nextCursor opaque renvoyé par la page précédente. Encode [createdAt,id] et préserve les événements partageant un même horodatage."
          }
        ],
        "responses": {
          "200": {
            "description": "Plus récents en premier ; jusqu'à 50 événements. Suivez nextCursor jusqu'à null.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "events": {
                      "type": "array",
                      "maxItems": 50,
                      "items": {
                        "$ref": "#/components/schemas/MerchantEvent"
                      }
                    },
                    "nextCursor": {
                      "type": [
                        "string",
                        "null"
                      ]
                    }
                  },
                  "required": [
                    "events",
                    "nextCursor"
                  ],
                  "additionalProperties": false
                }
              }
            }
          },
          "400": {
            "description": "JSON invalide, champ inconnu ou valeur invalide.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "INVALID_JSON",
              "UNKNOWN_FIELD",
              "INVALID_INPUT",
              "INVALID_VAT_PAYER",
              "INVALID_NOTIFICATIONS",
              "INVALID_TICKET_STATUS",
              "INVALID_UPDATE",
              "INVALID_HISTORY",
              "INVALID_PAGE_CONTEXT",
              "INVALID_LOCALE",
              "INVALID_FILTER",
              "INVALID_NOTIFICATION_SELECTION",
              "INVALID_AMOUNT",
              "INVALID_CURRENCY",
              "INVALID_METHODS",
              "INVALID_EXPIRY",
              "INVALID_QUOTE",
              "INVALID_LIMIT",
              "INVALID_CURSOR",
              "INVALID_STATUS",
              "IDEMPOTENCY_KEY_REQUIRED",
              "METHOD_UNAVAILABLE"
            ]
          },
          "401": {
            "description": "Aucune session valide ; pour un webhook, un secret invalide.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "AUTH_REQUIRED",
              "SESSION_EXPIRED",
              "WEBHOOK_UNAUTHORIZED"
            ]
          },
          "503": {
            "description": "Une intégration ou un stockage est indisponible, un événement est en cours de traitement, ou le service est indisponible.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "DATABASE_NOT_CONFIGURED",
              "ORIGIN_NOT_CONFIGURED",
              "TELEGRAM_NOT_CONFIGURED",
              "UPDATE_IN_PROGRESS",
              "SERVICE_UNAVAILABLE",
              "GATEWAY_NOT_CONNECTED"
            ]
          }
        },
        "x-required-scope": "events:read"
      }
    },
    "/api/webhooks": {
      "get": {
        "tags": [
          "Webhooks"
        ],
        "operationId": "listWebhookEndpoints",
        "summary": "Lister les endpoints et les 30 dernières livraisons",
        "security": [
          {
            "cookieSession": []
          }
        ],
        "responses": {
          "200": {
            "description": "Aucun secret de signature n'est renvoyé.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "endpoints": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/WebhookEndpoint"
                      }
                    },
                    "deliveries": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/WebhookDelivery"
                      }
                    },
                    "available": {
                      "type": "boolean"
                    },
                    "eventTypes": {
                      "type": "array",
                      "items": {
                        "type": "string",
                        "enum": [
                          "invoice.created",
                          "invoice.updated",
                          "payment.succeeded",
                          "payment.failed",
                          "payment.refunded",
                          "payment.partially_refunded",
                          "payment.disputed",
                          "payment.review_required"
                        ]
                      }
                    }
                  },
                  "required": [
                    "endpoints",
                    "deliveries",
                    "available",
                    "eventTypes"
                  ],
                  "additionalProperties": false
                }
              }
            }
          },
          "401": {
            "description": "Aucune session valide ; pour un webhook, un secret invalide.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "AUTH_REQUIRED",
              "SESSION_EXPIRED",
              "WEBHOOK_UNAUTHORIZED"
            ]
          },
          "404": {
            "description": "L'enregistrement est manquant, le jeton public est invalide, ou l'enregistrement demandé appartient à un autre utilisateur.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "TICKET_NOT_FOUND",
              "LINK_NOT_FOUND"
            ]
          },
          "503": {
            "description": "Une intégration ou un stockage est indisponible, un événement est en cours de traitement, ou le service est indisponible.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "DATABASE_NOT_CONFIGURED",
              "ORIGIN_NOT_CONFIGURED",
              "TELEGRAM_NOT_CONFIGURED",
              "UPDATE_IN_PROGRESS",
              "SERVICE_UNAVAILABLE",
              "GATEWAY_NOT_CONNECTED"
            ]
          }
        },
        "parameters": [
          {
            "name": "X-Kevmi-Store",
            "in": "header",
            "required": false,
            "schema": {
              "type": "string",
              "pattern": "^sto_[A-Za-z0-9_-]+$"
            },
            "description": "Sélectionnez une boutique possédée pour une session de navigateur. Omett pour utiliser la première boutique. Les clés API ignorent cet en-tête et utilisent toujours leur liaison de boutique immuable. La valeur all est en lecture seule et n'est prise en charge que par le tableau de bord et les points de terminaison de liste de factures."
          }
        ]
      },
      "post": {
        "tags": [
          "Webhooks"
        ],
        "operationId": "createWebhookEndpoint",
        "summary": "Enregistrer une destination HTTPS publique",
        "security": [
          {
            "cookieSession": [],
            "csrfToken": []
          }
        ],
        "parameters": [
          {
            "name": "Origin",
            "in": "header",
            "required": true,
            "description": "Doit correspondre exactement à APP_ORIGIN. Sec-Fetch-Site : cross-site est rejeté. Le serveur ne fournit pas d'accès CORS.",
            "schema": {
              "type": "string",
              "format": "uri"
            }
          },
          {
            "name": "X-CSRF-Token",
            "in": "header",
            "required": true,
            "description": "Jeton de session actuel depuis GET /api/auth/me ou achèvement réussi de la connexion.",
            "schema": {
              "type": "string",
              "pattern": "^[A-Za-z0-9_-]{43}$"
            }
          },
          {
            "name": "X-Kevmi-Store",
            "in": "header",
            "required": false,
            "schema": {
              "type": "string",
              "pattern": "^sto_[A-Za-z0-9_-]+$"
            },
            "description": "Sélectionnez une boutique possédée pour une session de navigateur. Omett pour utiliser la première boutique. Les clés API ignorent cet en-tête et utilisent toujours leur liaison de boutique immuable. La valeur all est en lecture seule et n'est prise en charge que par le tableau de bord et les points de terminaison de liste de factures."
          }
        ],
        "description": "Jusqu'à 5 endpoints et 6 créations/heure/utilisateur. Domaines HTTPS publics uniquement ; aucun identifiant, littéral IP ni port personnalisé. Un endpoint nouvellement créé ne reçoit aucune livraison en direct tant que son défi n'a pas réussi.",
        "requestBody": {
          "required": true,
          "description": "Objet JSON. Maximum 16 384 octets. Les champs inconnus sont rejetés.",
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "url": {
                    "type": "string",
                    "format": "uri",
                    "maxLength": 2048
                  },
                  "description": {
                    "type": "string",
                    "maxLength": 120
                  },
                  "events": {
                    "type": "array",
                    "minItems": 1,
                    "maxItems": 8,
                    "uniqueItems": true,
                    "items": {
                      "type": "string",
                      "enum": [
                        "invoice.created",
                        "invoice.updated",
                        "payment.succeeded",
                        "payment.failed",
                        "payment.refunded",
                        "payment.partially_refunded",
                        "payment.disputed",
                        "payment.review_required"
                      ]
                    }
                  }
                },
                "required": [
                  "url"
                ],
                "additionalProperties": false
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Enregistrez le secret de signature à usage unique.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "endpoint": {
                      "$ref": "#/components/schemas/WebhookEndpoint"
                    },
                    "secret": {
                      "type": "string",
                      "pattern": "^whsec_[A-Za-z0-9_-]{43}$",
                      "description": "Affiché uniquement lors de la création/rotation. Stocké chiffré côté serveur."
                    }
                  },
                  "required": [
                    "endpoint",
                    "secret"
                  ],
                  "additionalProperties": false
                }
              }
            }
          },
          "400": {
            "description": "JSON invalide, champ inconnu ou valeur invalide.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "INVALID_JSON",
              "UNKNOWN_FIELD",
              "INVALID_INPUT",
              "INVALID_VAT_PAYER",
              "INVALID_NOTIFICATIONS",
              "INVALID_TICKET_STATUS",
              "INVALID_UPDATE",
              "INVALID_HISTORY",
              "INVALID_PAGE_CONTEXT",
              "INVALID_LOCALE",
              "INVALID_FILTER",
              "INVALID_NOTIFICATION_SELECTION",
              "INVALID_AMOUNT",
              "INVALID_CURRENCY",
              "INVALID_METHODS",
              "INVALID_EXPIRY",
              "INVALID_QUOTE",
              "INVALID_LIMIT",
              "INVALID_CURSOR",
              "INVALID_STATUS",
              "IDEMPOTENCY_KEY_REQUIRED",
              "METHOD_UNAVAILABLE"
            ]
          },
          "401": {
            "description": "Aucune session valide ; pour un webhook, un secret invalide.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "AUTH_REQUIRED",
              "SESSION_EXPIRED",
              "WEBHOOK_UNAUTHORIZED"
            ]
          },
          "403": {
            "description": "La validation d'origine, CSRF ou de liaison navigateur a échoué.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "INVALID_ORIGIN",
              "CSRF_REQUIRED",
              "BROWSER_PROOF_REQUIRED",
              "INVALID_CHALLENGE"
            ]
          },
          "409": {
            "description": "La demande de connexion est utilisée ou annulée, l'identité n'est pas confirmée, ou une demande d'assistance entre en conflit.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "CHALLENGE_UNAVAILABLE",
              "IDENTITY_INCOMPLETE",
              "TICKET_CONFLICT",
              "IDEMPOTENCY_CONFLICT",
              "LINK_CANCELLED",
              "LINK_EXPIRED",
              "LINK_UNAVAILABLE"
            ]
          },
          "429": {
            "description": "Limite de débit dépassée.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              },
              "Retry-After": {
                "schema": {
                  "type": "string",
                  "const": "60"
                },
                "description": "Le serveur renvoie toujours 60 ; cela ne garantit pas qu'un quota horaire a expiré."
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "RATE_LIMITED"
            ]
          },
          "503": {
            "description": "Une intégration ou un stockage est indisponible, un événement est en cours de traitement, ou le service est indisponible.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "DATABASE_NOT_CONFIGURED",
              "ORIGIN_NOT_CONFIGURED",
              "TELEGRAM_NOT_CONFIGURED",
              "UPDATE_IN_PROGRESS",
              "SERVICE_UNAVAILABLE",
              "GATEWAY_NOT_CONNECTED"
            ]
          }
        }
      }
    },
    "/api/webhooks/{id}": {
      "parameters": [
        {
          "name": "id",
          "in": "path",
          "required": true,
          "schema": {
            "type": "string",
            "pattern": "^wh_[0-9a-f-]{36}$"
          }
        }
      ],
      "get": {
        "tags": [
          "Webhooks"
        ],
        "operationId": "getWebhookEndpoint",
        "summary": "Lister les endpoints et les 30 dernières livraisons",
        "security": [
          {
            "cookieSession": []
          }
        ],
        "responses": {
          "200": {
            "description": "Aucun secret de signature n'est renvoyé.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "endpoints": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/WebhookEndpoint"
                      }
                    },
                    "deliveries": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/WebhookDelivery"
                      }
                    },
                    "available": {
                      "type": "boolean"
                    },
                    "eventTypes": {
                      "type": "array",
                      "items": {
                        "type": "string",
                        "enum": [
                          "invoice.created",
                          "invoice.updated",
                          "payment.succeeded",
                          "payment.failed",
                          "payment.refunded",
                          "payment.partially_refunded",
                          "payment.disputed",
                          "payment.review_required"
                        ]
                      }
                    }
                  },
                  "required": [
                    "endpoints",
                    "deliveries",
                    "available",
                    "eventTypes"
                  ],
                  "additionalProperties": false
                }
              }
            }
          },
          "401": {
            "description": "Aucune session valide ; pour un webhook, un secret invalide.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "AUTH_REQUIRED",
              "SESSION_EXPIRED",
              "WEBHOOK_UNAUTHORIZED"
            ]
          },
          "404": {
            "description": "L'enregistrement est manquant, le jeton public est invalide, ou l'enregistrement demandé appartient à un autre utilisateur.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "TICKET_NOT_FOUND",
              "LINK_NOT_FOUND"
            ]
          },
          "503": {
            "description": "Une intégration ou un stockage est indisponible, un événement est en cours de traitement, ou le service est indisponible.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "DATABASE_NOT_CONFIGURED",
              "ORIGIN_NOT_CONFIGURED",
              "TELEGRAM_NOT_CONFIGURED",
              "UPDATE_IN_PROGRESS",
              "SERVICE_UNAVAILABLE",
              "GATEWAY_NOT_CONNECTED"
            ]
          }
        },
        "parameters": [
          {
            "name": "X-Kevmi-Store",
            "in": "header",
            "required": false,
            "schema": {
              "type": "string",
              "pattern": "^sto_[A-Za-z0-9_-]+$"
            },
            "description": "Sélectionnez une boutique possédée pour une session de navigateur. Omett pour utiliser la première boutique. Les clés API ignorent cet en-tête et utilisent toujours leur liaison de boutique immuable. La valeur all est en lecture seule et n'est prise en charge que par le tableau de bord et les points de terminaison de liste de factures."
          }
        ]
      },
      "patch": {
        "tags": [
          "Webhooks"
        ],
        "operationId": "setWebhookStatus",
        "summary": "Mettre en pause ou reprendre un endpoint vérifié",
        "description": "Les endpoints vérifiés en pause conservent les événements éligibles dans la file d'attente ; la reprise traite les livraisons dues.",
        "security": [
          {
            "cookieSession": [],
            "csrfToken": []
          }
        ],
        "parameters": [
          {
            "name": "Origin",
            "in": "header",
            "required": true,
            "description": "Doit correspondre exactement à APP_ORIGIN. Sec-Fetch-Site : cross-site est rejeté. Le serveur ne fournit pas d'accès CORS.",
            "schema": {
              "type": "string",
              "format": "uri"
            }
          },
          {
            "name": "X-CSRF-Token",
            "in": "header",
            "required": true,
            "description": "Jeton de session actuel depuis GET /api/auth/me ou achèvement réussi de la connexion.",
            "schema": {
              "type": "string",
              "pattern": "^[A-Za-z0-9_-]{43}$"
            }
          },
          {
            "name": "X-Kevmi-Store",
            "in": "header",
            "required": false,
            "schema": {
              "type": "string",
              "pattern": "^sto_[A-Za-z0-9_-]+$"
            },
            "description": "Sélectionnez une boutique possédée pour une session de navigateur. Omett pour utiliser la première boutique. Les clés API ignorent cet en-tête et utilisent toujours leur liaison de boutique immuable. La valeur all est en lecture seule et n'est prise en charge que par le tableau de bord et les points de terminaison de liste de factures."
          }
        ],
        "requestBody": {
          "required": true,
          "description": "Objet JSON. Maximum 16 384 octets. Les champs inconnus sont rejetés.",
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "status": {
                    "type": "string",
                    "enum": [
                      "active",
                      "paused"
                    ]
                  }
                },
                "required": [
                  "status"
                ],
                "additionalProperties": false
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Statut enregistré.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "updated": {
                      "const": true
                    }
                  },
                  "required": [
                    "updated"
                  ],
                  "additionalProperties": false
                }
              }
            }
          },
          "400": {
            "description": "JSON invalide, champ inconnu ou valeur invalide.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "INVALID_JSON",
              "UNKNOWN_FIELD",
              "INVALID_INPUT",
              "INVALID_VAT_PAYER",
              "INVALID_NOTIFICATIONS",
              "INVALID_TICKET_STATUS",
              "INVALID_UPDATE",
              "INVALID_HISTORY",
              "INVALID_PAGE_CONTEXT",
              "INVALID_LOCALE",
              "INVALID_FILTER",
              "INVALID_NOTIFICATION_SELECTION",
              "INVALID_AMOUNT",
              "INVALID_CURRENCY",
              "INVALID_METHODS",
              "INVALID_EXPIRY",
              "INVALID_QUOTE",
              "INVALID_LIMIT",
              "INVALID_CURSOR",
              "INVALID_STATUS",
              "IDEMPOTENCY_KEY_REQUIRED",
              "METHOD_UNAVAILABLE"
            ]
          },
          "401": {
            "description": "Aucune session valide ; pour un webhook, un secret invalide.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "AUTH_REQUIRED",
              "SESSION_EXPIRED",
              "WEBHOOK_UNAUTHORIZED"
            ]
          },
          "403": {
            "description": "La validation d'origine, CSRF ou de liaison navigateur a échoué.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "INVALID_ORIGIN",
              "CSRF_REQUIRED",
              "BROWSER_PROOF_REQUIRED",
              "INVALID_CHALLENGE"
            ]
          },
          "404": {
            "description": "L'enregistrement est manquant, le jeton public est invalide, ou l'enregistrement demandé appartient à un autre utilisateur.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "TICKET_NOT_FOUND",
              "LINK_NOT_FOUND"
            ]
          },
          "409": {
            "description": "La demande de connexion est utilisée ou annulée, l'identité n'est pas confirmée, ou une demande d'assistance entre en conflit.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "CHALLENGE_UNAVAILABLE",
              "IDENTITY_INCOMPLETE",
              "TICKET_CONFLICT",
              "IDEMPOTENCY_CONFLICT",
              "LINK_CANCELLED",
              "LINK_EXPIRED",
              "LINK_UNAVAILABLE"
            ]
          },
          "503": {
            "description": "Une intégration ou un stockage est indisponible, un événement est en cours de traitement, ou le service est indisponible.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "DATABASE_NOT_CONFIGURED",
              "ORIGIN_NOT_CONFIGURED",
              "TELEGRAM_NOT_CONFIGURED",
              "UPDATE_IN_PROGRESS",
              "SERVICE_UNAVAILABLE",
              "GATEWAY_NOT_CONNECTED"
            ]
          }
        }
      },
      "delete": {
        "tags": [
          "Webhooks"
        ],
        "operationId": "deleteWebhookEndpoint",
        "summary": "Supprimer l'endpoint et annuler les tâches non livrées",
        "description": "Annule les tâches en attente, échouées et louées. Une requête déjà envoyée sur le réseau ne peut pas être rappelée.",
        "security": [
          {
            "cookieSession": [],
            "csrfToken": []
          }
        ],
        "parameters": [
          {
            "name": "Origin",
            "in": "header",
            "required": true,
            "description": "Doit correspondre exactement à APP_ORIGIN. Sec-Fetch-Site : cross-site est rejeté. Le serveur ne fournit pas d'accès CORS.",
            "schema": {
              "type": "string",
              "format": "uri"
            }
          },
          {
            "name": "X-CSRF-Token",
            "in": "header",
            "required": true,
            "description": "Jeton de session actuel depuis GET /api/auth/me ou achèvement réussi de la connexion.",
            "schema": {
              "type": "string",
              "pattern": "^[A-Za-z0-9_-]{43}$"
            }
          },
          {
            "name": "X-Kevmi-Store",
            "in": "header",
            "required": false,
            "schema": {
              "type": "string",
              "pattern": "^sto_[A-Za-z0-9_-]+$"
            },
            "description": "Sélectionnez une boutique possédée pour une session de navigateur. Omett pour utiliser la première boutique. Les clés API ignorent cet en-tête et utilisent toujours leur liaison de boutique immuable. La valeur all est en lecture seule et n'est prise en charge que par le tableau de bord et les points de terminaison de liste de factures."
          }
        ],
        "responses": {
          "200": {
            "description": "Endpoint supprimé.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "deleted": {
                      "const": true
                    }
                  },
                  "required": [
                    "deleted"
                  ],
                  "additionalProperties": false
                }
              }
            }
          },
          "401": {
            "description": "Aucune session valide ; pour un webhook, un secret invalide.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "AUTH_REQUIRED",
              "SESSION_EXPIRED",
              "WEBHOOK_UNAUTHORIZED"
            ]
          },
          "403": {
            "description": "La validation d'origine, CSRF ou de liaison navigateur a échoué.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "INVALID_ORIGIN",
              "CSRF_REQUIRED",
              "BROWSER_PROOF_REQUIRED",
              "INVALID_CHALLENGE"
            ]
          },
          "404": {
            "description": "L'enregistrement est manquant, le jeton public est invalide, ou l'enregistrement demandé appartient à un autre utilisateur.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "TICKET_NOT_FOUND",
              "LINK_NOT_FOUND"
            ]
          },
          "503": {
            "description": "Une intégration ou un stockage est indisponible, un événement est en cours de traitement, ou le service est indisponible.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "DATABASE_NOT_CONFIGURED",
              "ORIGIN_NOT_CONFIGURED",
              "TELEGRAM_NOT_CONFIGURED",
              "UPDATE_IN_PROGRESS",
              "SERVICE_UNAVAILABLE",
              "GATEWAY_NOT_CONNECTED"
            ]
          }
        }
      }
    },
    "/api/webhooks/{id}/verify": {
      "parameters": [
        {
          "name": "id",
          "in": "path",
          "required": true,
          "schema": {
            "type": "string",
            "pattern": "^wh_[0-9a-f-]{36}$"
          }
        }
      ],
      "post": {
        "tags": [
          "Webhooks"
        ],
        "operationId": "verifyWebhookEndpoint",
        "summary": "Envoyer un défi de vérification d'endpoint signé",
        "description": "Maximum 5 tentatives de vérification/heure/utilisateur. Votre serveur doit valider Kevmi-Signature et renvoyer un JSON 2xx {\"challenge\":\"<data.challenge>\"}. Délai de requête : 8 secondes ; limite de réponse : 8192 octets. Les redirections ne sont pas suivies. La vérification active l'endpoint.",
        "security": [
          {
            "cookieSession": [],
            "csrfToken": []
          }
        ],
        "parameters": [
          {
            "name": "Origin",
            "in": "header",
            "required": true,
            "description": "Doit correspondre exactement à APP_ORIGIN. Sec-Fetch-Site : cross-site est rejeté. Le serveur ne fournit pas d'accès CORS.",
            "schema": {
              "type": "string",
              "format": "uri"
            }
          },
          {
            "name": "X-CSRF-Token",
            "in": "header",
            "required": true,
            "description": "Jeton de session actuel depuis GET /api/auth/me ou achèvement réussi de la connexion.",
            "schema": {
              "type": "string",
              "pattern": "^[A-Za-z0-9_-]{43}$"
            }
          },
          {
            "name": "X-Kevmi-Store",
            "in": "header",
            "required": false,
            "schema": {
              "type": "string",
              "pattern": "^sto_[A-Za-z0-9_-]+$"
            },
            "description": "Sélectionnez une boutique possédée pour une session de navigateur. Omett pour utiliser la première boutique. Les clés API ignorent cet en-tête et utilisent toujours leur liaison de boutique immuable. La valeur all est en lecture seule et n'est prise en charge que par le tableau de bord et les points de terminaison de liste de factures."
          }
        ],
        "requestBody": {
          "required": true,
          "description": "Objet JSON. Maximum 16 384 octets. Les champs inconnus sont rejetés.",
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/EmptyObject"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Vérifié et actif.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "verified": {
                      "const": true
                    },
                    "endpoint": {
                      "$ref": "#/components/schemas/WebhookEndpoint"
                    }
                  },
                  "required": [
                    "verified",
                    "endpoint"
                  ],
                  "additionalProperties": false
                }
              }
            }
          },
          "400": {
            "description": "JSON invalide, champ inconnu ou valeur invalide.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "INVALID_JSON",
              "UNKNOWN_FIELD",
              "INVALID_INPUT",
              "INVALID_VAT_PAYER",
              "INVALID_NOTIFICATIONS",
              "INVALID_TICKET_STATUS",
              "INVALID_UPDATE",
              "INVALID_HISTORY",
              "INVALID_PAGE_CONTEXT",
              "INVALID_LOCALE",
              "INVALID_FILTER",
              "INVALID_NOTIFICATION_SELECTION",
              "INVALID_AMOUNT",
              "INVALID_CURRENCY",
              "INVALID_METHODS",
              "INVALID_EXPIRY",
              "INVALID_QUOTE",
              "INVALID_LIMIT",
              "INVALID_CURSOR",
              "INVALID_STATUS",
              "IDEMPOTENCY_KEY_REQUIRED",
              "METHOD_UNAVAILABLE"
            ]
          },
          "401": {
            "description": "Aucune session valide ; pour un webhook, un secret invalide.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "AUTH_REQUIRED",
              "SESSION_EXPIRED",
              "WEBHOOK_UNAUTHORIZED"
            ]
          },
          "403": {
            "description": "La validation d'origine, CSRF ou de liaison navigateur a échoué.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "INVALID_ORIGIN",
              "CSRF_REQUIRED",
              "BROWSER_PROOF_REQUIRED",
              "INVALID_CHALLENGE"
            ]
          },
          "404": {
            "description": "L'enregistrement est manquant, le jeton public est invalide, ou l'enregistrement demandé appartient à un autre utilisateur.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "TICKET_NOT_FOUND",
              "LINK_NOT_FOUND"
            ]
          },
          "409": {
            "description": "La demande de connexion est utilisée ou annulée, l'identité n'est pas confirmée, ou une demande d'assistance entre en conflit.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "CHALLENGE_UNAVAILABLE",
              "IDENTITY_INCOMPLETE",
              "TICKET_CONFLICT",
              "IDEMPOTENCY_CONFLICT",
              "LINK_CANCELLED",
              "LINK_EXPIRED",
              "LINK_UNAVAILABLE"
            ]
          },
          "422": {
            "description": "La destination n'a pas pu être vérifiée.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "WEBHOOK_UNREACHABLE",
              "WEBHOOK_VERIFICATION_FAILED"
            ]
          },
          "429": {
            "description": "Limite de débit dépassée.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              },
              "Retry-After": {
                "schema": {
                  "type": "string",
                  "const": "60"
                },
                "description": "Le serveur renvoie toujours 60 ; cela ne garantit pas qu'un quota horaire a expiré."
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "RATE_LIMITED"
            ]
          },
          "503": {
            "description": "Une intégration ou un stockage est indisponible, un événement est en cours de traitement, ou le service est indisponible.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "DATABASE_NOT_CONFIGURED",
              "ORIGIN_NOT_CONFIGURED",
              "TELEGRAM_NOT_CONFIGURED",
              "UPDATE_IN_PROGRESS",
              "SERVICE_UNAVAILABLE",
              "GATEWAY_NOT_CONNECTED"
            ]
          }
        }
      }
    },
    "/api/webhooks/{id}/rotate": {
      "parameters": [
        {
          "name": "id",
          "in": "path",
          "required": true,
          "schema": {
            "type": "string",
            "pattern": "^wh_[0-9a-f-]{36}$"
          }
        }
      ],
      "post": {
        "tags": [
          "Webhooks"
        ],
        "operationId": "rotateWebhookSecret",
        "summary": "Remplacer le secret de signature et exiger une nouvelle vérification",
        "description": "Maximum 3 rotations/heure/utilisateur. Mettez à jour votre serveur et vérifiez à nouveau l'endpoint. Une requête déjà en vol peut encore porter la signature précédente.",
        "security": [
          {
            "cookieSession": [],
            "csrfToken": []
          }
        ],
        "parameters": [
          {
            "name": "Origin",
            "in": "header",
            "required": true,
            "description": "Doit correspondre exactement à APP_ORIGIN. Sec-Fetch-Site : cross-site est rejeté. Le serveur ne fournit pas d'accès CORS.",
            "schema": {
              "type": "string",
              "format": "uri"
            }
          },
          {
            "name": "X-CSRF-Token",
            "in": "header",
            "required": true,
            "description": "Jeton de session actuel depuis GET /api/auth/me ou achèvement réussi de la connexion.",
            "schema": {
              "type": "string",
              "pattern": "^[A-Za-z0-9_-]{43}$"
            }
          },
          {
            "name": "X-Kevmi-Store",
            "in": "header",
            "required": false,
            "schema": {
              "type": "string",
              "pattern": "^sto_[A-Za-z0-9_-]+$"
            },
            "description": "Sélectionnez une boutique possédée pour une session de navigateur. Omett pour utiliser la première boutique. Les clés API ignorent cet en-tête et utilisent toujours leur liaison de boutique immuable. La valeur all est en lecture seule et n'est prise en charge que par le tableau de bord et les points de terminaison de liste de factures."
          }
        ],
        "requestBody": {
          "required": true,
          "description": "Objet JSON. Maximum 16 384 octets. Les champs inconnus sont rejetés.",
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/EmptyObject"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Nouveau secret affiché une fois.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "secret": {
                      "type": "string",
                      "pattern": "^whsec_[A-Za-z0-9_-]{43}$",
                      "description": "Affiché uniquement lors de la création/rotation. Stocké chiffré côté serveur."
                    },
                    "requiresVerification": {
                      "const": true
                    }
                  },
                  "required": [
                    "secret",
                    "requiresVerification"
                  ],
                  "additionalProperties": false
                }
              }
            }
          },
          "400": {
            "description": "JSON invalide, champ inconnu ou valeur invalide.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "INVALID_JSON",
              "UNKNOWN_FIELD",
              "INVALID_INPUT",
              "INVALID_VAT_PAYER",
              "INVALID_NOTIFICATIONS",
              "INVALID_TICKET_STATUS",
              "INVALID_UPDATE",
              "INVALID_HISTORY",
              "INVALID_PAGE_CONTEXT",
              "INVALID_LOCALE",
              "INVALID_FILTER",
              "INVALID_NOTIFICATION_SELECTION",
              "INVALID_AMOUNT",
              "INVALID_CURRENCY",
              "INVALID_METHODS",
              "INVALID_EXPIRY",
              "INVALID_QUOTE",
              "INVALID_LIMIT",
              "INVALID_CURSOR",
              "INVALID_STATUS",
              "IDEMPOTENCY_KEY_REQUIRED",
              "METHOD_UNAVAILABLE"
            ]
          },
          "401": {
            "description": "Aucune session valide ; pour un webhook, un secret invalide.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "AUTH_REQUIRED",
              "SESSION_EXPIRED",
              "WEBHOOK_UNAUTHORIZED"
            ]
          },
          "403": {
            "description": "La validation d'origine, CSRF ou de liaison navigateur a échoué.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "INVALID_ORIGIN",
              "CSRF_REQUIRED",
              "BROWSER_PROOF_REQUIRED",
              "INVALID_CHALLENGE"
            ]
          },
          "404": {
            "description": "L'enregistrement est manquant, le jeton public est invalide, ou l'enregistrement demandé appartient à un autre utilisateur.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "TICKET_NOT_FOUND",
              "LINK_NOT_FOUND"
            ]
          },
          "409": {
            "description": "La demande de connexion est utilisée ou annulée, l'identité n'est pas confirmée, ou une demande d'assistance entre en conflit.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "CHALLENGE_UNAVAILABLE",
              "IDENTITY_INCOMPLETE",
              "TICKET_CONFLICT",
              "IDEMPOTENCY_CONFLICT",
              "LINK_CANCELLED",
              "LINK_EXPIRED",
              "LINK_UNAVAILABLE"
            ]
          },
          "429": {
            "description": "Limite de débit dépassée.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              },
              "Retry-After": {
                "schema": {
                  "type": "string",
                  "const": "60"
                },
                "description": "Le serveur renvoie toujours 60 ; cela ne garantit pas qu'un quota horaire a expiré."
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "RATE_LIMITED"
            ]
          },
          "503": {
            "description": "Une intégration ou un stockage est indisponible, un événement est en cours de traitement, ou le service est indisponible.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "DATABASE_NOT_CONFIGURED",
              "ORIGIN_NOT_CONFIGURED",
              "TELEGRAM_NOT_CONFIGURED",
              "UPDATE_IN_PROGRESS",
              "SERVICE_UNAVAILABLE",
              "GATEWAY_NOT_CONNECTED"
            ]
          }
        }
      }
    },
    "/api/webhooks/{id}/deliveries/{deliveryId}/retry": {
      "parameters": [
        {
          "name": "id",
          "in": "path",
          "required": true,
          "schema": {
            "type": "string",
            "pattern": "^wh_[0-9a-f-]{36}$"
          }
        },
        {
          "name": "deliveryId",
          "in": "path",
          "required": true,
          "schema": {
            "type": "string",
            "pattern": "^del_[a-f0-9]{40}$"
          }
        }
      ],
      "post": {
        "tags": [
          "Webhooks"
        ],
        "operationId": "retryWebhookDelivery",
        "summary": "Réessayer une livraison échouée vers un endpoint actif",
        "description": "Conserve l'ID d'événement et la charge utile ; génère un horodatage et une signature frais. Chaque cycle de livraison automatique effectue au plus 6 tentatives, y compris les baux expirés, avec des délais de 1m, 5m, 30m, 2h et 12h. La livraison est au moins une fois ; dédupliquez les ID d'événements sur votre serveur.",
        "security": [
          {
            "cookieSession": [],
            "csrfToken": []
          }
        ],
        "parameters": [
          {
            "name": "Origin",
            "in": "header",
            "required": true,
            "description": "Doit correspondre exactement à APP_ORIGIN. Sec-Fetch-Site : cross-site est rejeté. Le serveur ne fournit pas d'accès CORS.",
            "schema": {
              "type": "string",
              "format": "uri"
            }
          },
          {
            "name": "X-CSRF-Token",
            "in": "header",
            "required": true,
            "description": "Jeton de session actuel depuis GET /api/auth/me ou achèvement réussi de la connexion.",
            "schema": {
              "type": "string",
              "pattern": "^[A-Za-z0-9_-]{43}$"
            }
          },
          {
            "name": "X-Kevmi-Store",
            "in": "header",
            "required": false,
            "schema": {
              "type": "string",
              "pattern": "^sto_[A-Za-z0-9_-]+$"
            },
            "description": "Sélectionnez une boutique possédée pour une session de navigateur. Omett pour utiliser la première boutique. Les clés API ignorent cet en-tête et utilisent toujours leur liaison de boutique immuable. La valeur all est en lecture seule et n'est prise en charge que par le tableau de bord et les points de terminaison de liste de factures."
          }
        ],
        "requestBody": {
          "required": true,
          "description": "Objet JSON. Maximum 16 384 octets. Les champs inconnus sont rejetés.",
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/EmptyObject"
              }
            }
          }
        },
        "responses": {
          "202": {
            "description": "Réessai planifié.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "queued": {
                      "const": true
                    }
                  },
                  "required": [
                    "queued"
                  ],
                  "additionalProperties": false
                }
              }
            }
          },
          "400": {
            "description": "JSON invalide, champ inconnu ou valeur invalide.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "INVALID_JSON",
              "UNKNOWN_FIELD",
              "INVALID_INPUT",
              "INVALID_VAT_PAYER",
              "INVALID_NOTIFICATIONS",
              "INVALID_TICKET_STATUS",
              "INVALID_UPDATE",
              "INVALID_HISTORY",
              "INVALID_PAGE_CONTEXT",
              "INVALID_LOCALE",
              "INVALID_FILTER",
              "INVALID_NOTIFICATION_SELECTION",
              "INVALID_AMOUNT",
              "INVALID_CURRENCY",
              "INVALID_METHODS",
              "INVALID_EXPIRY",
              "INVALID_QUOTE",
              "INVALID_LIMIT",
              "INVALID_CURSOR",
              "INVALID_STATUS",
              "IDEMPOTENCY_KEY_REQUIRED",
              "METHOD_UNAVAILABLE"
            ]
          },
          "401": {
            "description": "Aucune session valide ; pour un webhook, un secret invalide.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "AUTH_REQUIRED",
              "SESSION_EXPIRED",
              "WEBHOOK_UNAUTHORIZED"
            ]
          },
          "403": {
            "description": "La validation d'origine, CSRF ou de liaison navigateur a échoué.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "INVALID_ORIGIN",
              "CSRF_REQUIRED",
              "BROWSER_PROOF_REQUIRED",
              "INVALID_CHALLENGE"
            ]
          },
          "404": {
            "description": "L'enregistrement est manquant, le jeton public est invalide, ou l'enregistrement demandé appartient à un autre utilisateur.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "TICKET_NOT_FOUND",
              "LINK_NOT_FOUND"
            ]
          },
          "409": {
            "description": "La demande de connexion est utilisée ou annulée, l'identité n'est pas confirmée, ou une demande d'assistance entre en conflit.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "CHALLENGE_UNAVAILABLE",
              "IDENTITY_INCOMPLETE",
              "TICKET_CONFLICT",
              "IDEMPOTENCY_CONFLICT",
              "LINK_CANCELLED",
              "LINK_EXPIRED",
              "LINK_UNAVAILABLE"
            ]
          },
          "429": {
            "description": "Limite de débit dépassée.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              },
              "Retry-After": {
                "schema": {
                  "type": "string",
                  "const": "60"
                },
                "description": "Le serveur renvoie toujours 60 ; cela ne garantit pas qu'un quota horaire a expiré."
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "RATE_LIMITED"
            ]
          },
          "503": {
            "description": "Une intégration ou un stockage est indisponible, un événement est en cours de traitement, ou le service est indisponible.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "DATABASE_NOT_CONFIGURED",
              "ORIGIN_NOT_CONFIGURED",
              "TELEGRAM_NOT_CONFIGURED",
              "UPDATE_IN_PROGRESS",
              "SERVICE_UNAVAILABLE",
              "GATEWAY_NOT_CONNECTED"
            ]
          }
        }
      }
    },
    "/api/applications": {
      "get": {
        "tags": [
          "Applications"
        ],
        "operationId": "listOwnApplications",
        "summary": "Lister jusqu'à 30 de ses propres applications",
        "security": [
          {
            "cookieSession": []
          }
        ],
        "parameters": [
          {
            "name": "X-Kevmi-Locale",
            "in": "header",
            "required": false,
            "schema": {
              "type": "string",
              "enum": [
                "ru",
                "en",
                "es",
                "de",
                "fr",
                "pt",
                "tr",
                "kk"
              ]
            },
            "description": "URL de suivi et langue des messages système. Sans valeur prise en charge, le référent de même origine et la langue du navigateur sont pris en compte ; le repli est l'anglais."
          },
          {
            "name": "X-Kevmi-Store",
            "in": "header",
            "required": false,
            "schema": {
              "type": "string",
              "pattern": "^sto_[A-Za-z0-9_-]+$"
            },
            "description": "Sélectionnez une boutique possédée pour une session de navigateur. Omett pour utiliser la première boutique. Les clés API ignorent cet en-tête et utilisent toujours leur liaison de boutique immuable. La valeur all est en lecture seule et n'est prise en charge que par le tableau de bord et les points de terminaison de liste de factures."
          }
        ],
        "responses": {
          "200": {
            "description": "Plus récents en premier.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "applications": {
                      "type": "array",
                      "maxItems": 30,
                      "items": {
                        "$ref": "#/components/schemas/Application"
                      }
                    }
                  },
                  "required": [
                    "applications"
                  ],
                  "additionalProperties": false
                }
              }
            }
          },
          "401": {
            "description": "Aucune session valide ; pour un webhook, un secret invalide.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "AUTH_REQUIRED",
              "SESSION_EXPIRED",
              "WEBHOOK_UNAUTHORIZED"
            ]
          },
          "503": {
            "description": "Une intégration ou un stockage est indisponible, un événement est en cours de traitement, ou le service est indisponible.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "DATABASE_NOT_CONFIGURED",
              "ORIGIN_NOT_CONFIGURED",
              "TELEGRAM_NOT_CONFIGURED",
              "UPDATE_IN_PROGRESS",
              "SERVICE_UNAVAILABLE",
              "GATEWAY_NOT_CONNECTED"
            ]
          }
        }
      },
      "post": {
        "tags": [
          "Applications"
        ],
        "operationId": "submitWebsiteApplication",
        "summary": "Soumettre les informations du site Web et recevoir une URL de suivi",
        "description": "Nécessite une connexion Telegram confirmée. Jusqu'à 5 nouvelles soumissions/jour/utilisateur. Une relecture idempotente renvoie 200. Le serveur fait avancer les étapes persistées réelles ; les résultats automatiques indisponibles ou risqués nécessitent un examen humain. L'approbation ne configure pas la passerelle de paiement. La propriété et l'état ne peuvent pas être fournis par le demandeur.",
        "security": [
          {
            "cookieSession": [],
            "csrfToken": []
          }
        ],
        "parameters": [
          {
            "name": "Origin",
            "in": "header",
            "required": true,
            "description": "Doit correspondre exactement à APP_ORIGIN. Sec-Fetch-Site : cross-site est rejeté. Le serveur ne fournit pas d'accès CORS.",
            "schema": {
              "type": "string",
              "format": "uri"
            }
          },
          {
            "name": "X-CSRF-Token",
            "in": "header",
            "required": true,
            "description": "Jeton de session actuel depuis GET /api/auth/me ou achèvement réussi de la connexion.",
            "schema": {
              "type": "string",
              "pattern": "^[A-Za-z0-9_-]{43}$"
            }
          },
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string",
              "pattern": "^[A-Za-z0-9_-]{16,100}$"
            },
            "description": "Une clé par demande logique. Une répétition avec une entrée canonique identique renvoie l'enregistrement original ; une entrée différente avec la même clé renvoie 409."
          },
          {
            "name": "X-Kevmi-Locale",
            "in": "header",
            "required": false,
            "schema": {
              "type": "string",
              "enum": [
                "ru",
                "en",
                "es",
                "de",
                "fr",
                "pt",
                "tr",
                "kk"
              ]
            },
            "description": "URL de suivi et langue des messages système. Sans valeur prise en charge, le référent de même origine et la langue du navigateur sont pris en compte ; le repli est l'anglais."
          },
          {
            "name": "X-Kevmi-Store",
            "in": "header",
            "required": false,
            "schema": {
              "type": "string",
              "pattern": "^sto_[A-Za-z0-9_-]+$"
            },
            "description": "Sélectionnez une boutique possédée pour une session de navigateur. Omett pour utiliser la première boutique. Les clés API ignorent cet en-tête et utilisent toujours leur liaison de boutique immuable. La valeur all est en lecture seule et n'est prise en charge que par le tableau de bord et les points de terminaison de liste de factures."
          }
        ],
        "requestBody": {
          "required": true,
          "description": "Objet JSON. Maximum 16 384 octets. Les champs inconnus sont rejetés.",
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "websiteUrl": {
                    "type": "string",
                    "format": "uri",
                    "maxLength": 2048,
                    "description": "Domaine HTTPS public sans identifiants, paramètres de requête, littéraux IP ni ports personnalisés. Un fragment est supprimé lors de la normalisation."
                  },
                  "businessName": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 100
                  },
                  "businessType": {
                    "type": "string",
                    "enum": [
                      "digital_goods",
                      "software",
                      "services",
                      "physical_goods",
                      "education",
                      "other"
                    ]
                  },
                  "description": {
                    "type": "string",
                    "minLength": 40,
                    "maxLength": 3000
                  },
                  "contactTelegram": {
                    "type": "string",
                    "description": "Un nom d'utilisateur Telegram de 5 à 32 caractères commençant par une lettre, puis lettres/chiffres/tirets bas. Préfixe @ ou https://t.me/ facultatif ; les espaces environnants sont supprimés. Stocké comme @username."
                  },
                  "consent": {
                    "type": "boolean",
                    "const": true
                  }
                },
                "required": [
                  "websiteUrl",
                  "businessName",
                  "businessType",
                  "description",
                  "contactTelegram",
                  "consent"
                ],
                "additionalProperties": false
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Soumission existante.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "application": {
                      "$ref": "#/components/schemas/Application"
                    },
                    "url": {
                      "type": "string",
                      "format": "uri"
                    },
                    "replayed": {
                      "type": "boolean"
                    }
                  },
                  "required": [
                    "application",
                    "url",
                    "replayed"
                  ],
                  "additionalProperties": false
                }
              }
            }
          },
          "202": {
            "description": "Accepté ; le traitement continue sur le serveur.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "application": {
                      "$ref": "#/components/schemas/Application"
                    },
                    "url": {
                      "type": "string",
                      "format": "uri"
                    },
                    "replayed": {
                      "type": "boolean"
                    }
                  },
                  "required": [
                    "application",
                    "url",
                    "replayed"
                  ],
                  "additionalProperties": false
                }
              }
            }
          },
          "400": {
            "description": "JSON invalide, champ inconnu ou valeur invalide.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "INVALID_JSON",
              "UNKNOWN_FIELD",
              "INVALID_INPUT",
              "INVALID_VAT_PAYER",
              "INVALID_NOTIFICATIONS",
              "INVALID_TICKET_STATUS",
              "INVALID_UPDATE",
              "INVALID_HISTORY",
              "INVALID_PAGE_CONTEXT",
              "INVALID_LOCALE",
              "INVALID_FILTER",
              "INVALID_NOTIFICATION_SELECTION",
              "INVALID_AMOUNT",
              "INVALID_CURRENCY",
              "INVALID_METHODS",
              "INVALID_EXPIRY",
              "INVALID_QUOTE",
              "INVALID_LIMIT",
              "INVALID_CURSOR",
              "INVALID_STATUS",
              "IDEMPOTENCY_KEY_REQUIRED",
              "METHOD_UNAVAILABLE"
            ]
          },
          "401": {
            "description": "Aucune session valide ; pour un webhook, un secret invalide.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "AUTH_REQUIRED",
              "SESSION_EXPIRED",
              "WEBHOOK_UNAUTHORIZED"
            ]
          },
          "403": {
            "description": "La validation d'origine, CSRF ou de liaison navigateur a échoué.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "INVALID_ORIGIN",
              "CSRF_REQUIRED",
              "BROWSER_PROOF_REQUIRED",
              "INVALID_CHALLENGE"
            ]
          },
          "409": {
            "description": "La demande de connexion est utilisée ou annulée, l'identité n'est pas confirmée, ou une demande d'assistance entre en conflit.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "CHALLENGE_UNAVAILABLE",
              "IDENTITY_INCOMPLETE",
              "TICKET_CONFLICT",
              "IDEMPOTENCY_CONFLICT",
              "LINK_CANCELLED",
              "LINK_EXPIRED",
              "LINK_UNAVAILABLE"
            ]
          },
          "429": {
            "description": "Limite de débit dépassée.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              },
              "Retry-After": {
                "schema": {
                  "type": "string",
                  "const": "60"
                },
                "description": "Le serveur renvoie toujours 60 ; cela ne garantit pas qu'un quota horaire a expiré."
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "RATE_LIMITED"
            ]
          },
          "503": {
            "description": "Une intégration ou un stockage est indisponible, un événement est en cours de traitement, ou le service est indisponible.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "DATABASE_NOT_CONFIGURED",
              "ORIGIN_NOT_CONFIGURED",
              "TELEGRAM_NOT_CONFIGURED",
              "UPDATE_IN_PROGRESS",
              "SERVICE_UNAVAILABLE",
              "GATEWAY_NOT_CONNECTED"
            ]
          }
        }
      }
    },
    "/api/applications/{id}": {
      "parameters": [
        {
          "name": "id",
          "in": "path",
          "required": true,
          "schema": {
            "type": "string",
            "pattern": "^app_[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$"
          }
        }
      ],
      "get": {
        "tags": [
          "Applications"
        ],
        "operationId": "getOwnApplication",
        "summary": "Lire ses propres étapes d'examen et son historique d'événements",
        "security": [
          {
            "cookieSession": []
          }
        ],
        "parameters": [
          {
            "name": "X-Kevmi-Locale",
            "in": "header",
            "required": false,
            "schema": {
              "type": "string",
              "enum": [
                "ru",
                "en",
                "es",
                "de",
                "fr",
                "pt",
                "tr",
                "kk"
              ]
            },
            "description": "URL de suivi et langue des messages système. Sans valeur prise en charge, le référent de même origine et la langue du navigateur sont pris en compte ; le repli est l'anglais."
          },
          {
            "name": "X-Kevmi-Store",
            "in": "header",
            "required": false,
            "schema": {
              "type": "string",
              "pattern": "^sto_[A-Za-z0-9_-]+$"
            },
            "description": "Sélectionnez une boutique possédée pour une session de navigateur. Omett pour utiliser la première boutique. Les clés API ignorent cet en-tête et utilisent toujours leur liaison de boutique immuable. La valeur all est en lecture seule et n'est prise en charge que par le tableau de bord et les points de terminaison de liste de factures."
          }
        ],
        "responses": {
          "200": {
            "description": "Projection privée du demandeur. Les preuves et la sortie du modèle ne sont pas exposées.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "application": {
                      "$ref": "#/components/schemas/Application"
                    },
                    "events": {
                      "type": "array",
                      "maxItems": 40,
                      "items": {
                        "$ref": "#/components/schemas/ApplicationEvent"
                      }
                    }
                  },
                  "required": [
                    "application",
                    "events"
                  ],
                  "additionalProperties": false
                }
              }
            }
          },
          "401": {
            "description": "Aucune session valide ; pour un webhook, un secret invalide.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "AUTH_REQUIRED",
              "SESSION_EXPIRED",
              "WEBHOOK_UNAUTHORIZED"
            ]
          },
          "404": {
            "description": "L'enregistrement est manquant, le jeton public est invalide, ou l'enregistrement demandé appartient à un autre utilisateur.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "TICKET_NOT_FOUND",
              "LINK_NOT_FOUND"
            ]
          },
          "503": {
            "description": "Une intégration ou un stockage est indisponible, un événement est en cours de traitement, ou le service est indisponible.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "DATABASE_NOT_CONFIGURED",
              "ORIGIN_NOT_CONFIGURED",
              "TELEGRAM_NOT_CONFIGURED",
              "UPDATE_IN_PROGRESS",
              "SERVICE_UNAVAILABLE",
              "GATEWAY_NOT_CONNECTED"
            ]
          }
        }
      }
    },
    "/api/applications/{id}/resume": {
      "parameters": [
        {
          "name": "id",
          "in": "path",
          "required": true,
          "schema": {
            "type": "string",
            "pattern": "^app_[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$"
          }
        }
      ],
      "post": {
        "tags": [
          "Applications"
        ],
        "operationId": "resumeApplicationJob",
        "summary": "Reprendre une étape de fond éligible",
        "description": "Ne peut pas sauter l'examen ni approuver une étape. Un bail serveur empêche le travail en double. Les applications nécessitant un examen et finales restent inchangées. Au plus 30 requêtes de reprise de traitement/minute/utilisateur.",
        "security": [
          {
            "cookieSession": [],
            "csrfToken": []
          }
        ],
        "parameters": [
          {
            "name": "Origin",
            "in": "header",
            "required": true,
            "description": "Doit correspondre exactement à APP_ORIGIN. Sec-Fetch-Site : cross-site est rejeté. Le serveur ne fournit pas d'accès CORS.",
            "schema": {
              "type": "string",
              "format": "uri"
            }
          },
          {
            "name": "X-CSRF-Token",
            "in": "header",
            "required": true,
            "description": "Jeton de session actuel depuis GET /api/auth/me ou achèvement réussi de la connexion.",
            "schema": {
              "type": "string",
              "pattern": "^[A-Za-z0-9_-]{43}$"
            }
          },
          {
            "name": "X-Kevmi-Locale",
            "in": "header",
            "required": false,
            "schema": {
              "type": "string",
              "enum": [
                "ru",
                "en",
                "es",
                "de",
                "fr",
                "pt",
                "tr",
                "kk"
              ]
            },
            "description": "URL de suivi et langue des messages système. Sans valeur prise en charge, le référent de même origine et la langue du navigateur sont pris en compte ; le repli est l'anglais."
          },
          {
            "name": "X-Kevmi-Store",
            "in": "header",
            "required": false,
            "schema": {
              "type": "string",
              "pattern": "^sto_[A-Za-z0-9_-]+$"
            },
            "description": "Sélectionnez une boutique possédée pour une session de navigateur. Omett pour utiliser la première boutique. Les clés API ignorent cet en-tête et utilisent toujours leur liaison de boutique immuable. La valeur all est en lecture seule et n'est prise en charge que par le tableau de bord et les points de terminaison de liste de factures."
          }
        ],
        "requestBody": {
          "required": true,
          "description": "Objet JSON. Maximum 16 384 octets. Les champs inconnus sont rejetés.",
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/EmptyObject"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Aucun traitement nécessaire.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "application": {
                      "$ref": "#/components/schemas/Application"
                    }
                  },
                  "required": [
                    "application"
                  ],
                  "additionalProperties": false
                }
              }
            }
          },
          "202": {
            "description": "Traitement planifié.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "application": {
                      "$ref": "#/components/schemas/Application"
                    },
                    "scheduled": {
                      "type": "boolean",
                      "const": true
                    }
                  },
                  "required": [
                    "application",
                    "scheduled"
                  ],
                  "additionalProperties": false
                }
              }
            }
          },
          "400": {
            "description": "JSON invalide, champ inconnu ou valeur invalide.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "INVALID_JSON",
              "UNKNOWN_FIELD",
              "INVALID_INPUT",
              "INVALID_VAT_PAYER",
              "INVALID_NOTIFICATIONS",
              "INVALID_TICKET_STATUS",
              "INVALID_UPDATE",
              "INVALID_HISTORY",
              "INVALID_PAGE_CONTEXT",
              "INVALID_LOCALE",
              "INVALID_FILTER",
              "INVALID_NOTIFICATION_SELECTION",
              "INVALID_AMOUNT",
              "INVALID_CURRENCY",
              "INVALID_METHODS",
              "INVALID_EXPIRY",
              "INVALID_QUOTE",
              "INVALID_LIMIT",
              "INVALID_CURSOR",
              "INVALID_STATUS",
              "IDEMPOTENCY_KEY_REQUIRED",
              "METHOD_UNAVAILABLE"
            ]
          },
          "401": {
            "description": "Aucune session valide ; pour un webhook, un secret invalide.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "AUTH_REQUIRED",
              "SESSION_EXPIRED",
              "WEBHOOK_UNAUTHORIZED"
            ]
          },
          "403": {
            "description": "La validation d'origine, CSRF ou de liaison navigateur a échoué.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "INVALID_ORIGIN",
              "CSRF_REQUIRED",
              "BROWSER_PROOF_REQUIRED",
              "INVALID_CHALLENGE"
            ]
          },
          "404": {
            "description": "L'enregistrement est manquant, le jeton public est invalide, ou l'enregistrement demandé appartient à un autre utilisateur.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "TICKET_NOT_FOUND",
              "LINK_NOT_FOUND"
            ]
          },
          "429": {
            "description": "Limite de débit dépassée.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              },
              "Retry-After": {
                "schema": {
                  "type": "string",
                  "const": "60"
                },
                "description": "Le serveur renvoie toujours 60 ; cela ne garantit pas qu'un quota horaire a expiré."
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "RATE_LIMITED"
            ]
          },
          "503": {
            "description": "Une intégration ou un stockage est indisponible, un événement est en cours de traitement, ou le service est indisponible.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "DATABASE_NOT_CONFIGURED",
              "ORIGIN_NOT_CONFIGURED",
              "TELEGRAM_NOT_CONFIGURED",
              "UPDATE_IN_PROGRESS",
              "SERVICE_UNAVAILABLE",
              "GATEWAY_NOT_CONNECTED"
            ]
          }
        }
      }
    },
    "/api/moderation/applications": {
      "get": {
        "tags": [
          "Moderation"
        ],
        "operationId": "listModerationApplications",
        "summary": "Lister jusqu'à 100 applications pour modération",
        "description": "Nécessite que l'identité Telegram authentifiée soit dans MODERATOR_TELEGRAM_IDS. Inclut tous les états, ordonnés needs_review d'abord, processing ensuite, puis états finaux ; mises à jour les plus récentes d'abord au sein d'un groupe.",
        "security": [
          {
            "cookieSession": []
          }
        ],
        "responses": {
          "200": {
            "description": "File d'attente du modérateur.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "applications": {
                      "type": "array",
                      "maxItems": 100,
                      "items": {
                        "$ref": "#/components/schemas/Application"
                      }
                    }
                  },
                  "required": [
                    "applications"
                  ],
                  "additionalProperties": false
                }
              }
            }
          },
          "401": {
            "description": "Aucune session valide ; pour un webhook, un secret invalide.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "AUTH_REQUIRED",
              "SESSION_EXPIRED",
              "WEBHOOK_UNAUTHORIZED"
            ]
          },
          "403": {
            "description": "La validation d'origine, CSRF ou de liaison navigateur a échoué.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "INVALID_ORIGIN",
              "CSRF_REQUIRED",
              "BROWSER_PROOF_REQUIRED",
              "INVALID_CHALLENGE"
            ]
          },
          "503": {
            "description": "Une intégration ou un stockage est indisponible, un événement est en cours de traitement, ou le service est indisponible.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "DATABASE_NOT_CONFIGURED",
              "ORIGIN_NOT_CONFIGURED",
              "TELEGRAM_NOT_CONFIGURED",
              "UPDATE_IN_PROGRESS",
              "SERVICE_UNAVAILABLE",
              "GATEWAY_NOT_CONNECTED"
            ]
          }
        }
      }
    },
    "/api/moderation/applications/{id}": {
      "parameters": [
        {
          "name": "id",
          "in": "path",
          "required": true,
          "schema": {
            "type": "string",
            "pattern": "^app_[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$"
          }
        }
      ],
      "get": {
        "tags": [
          "Moderation"
        ],
        "operationId": "getModerationApplication",
        "summary": "Lire les preuves d'examen et l'historique des étapes",
        "security": [
          {
            "cookieSession": []
          }
        ],
        "responses": {
          "200": {
            "description": "Preuves privées du modérateur.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "application": {
                      "$ref": "#/components/schemas/Application"
                    },
                    "events": {
                      "type": "array",
                      "maxItems": 40,
                      "items": {
                        "$ref": "#/components/schemas/ApplicationEvent"
                      }
                    },
                    "evidence": {
                      "type": "object",
                      "additionalProperties": true
                    }
                  },
                  "required": [
                    "application",
                    "events",
                    "evidence"
                  ],
                  "additionalProperties": false
                }
              }
            }
          },
          "401": {
            "description": "Aucune session valide ; pour un webhook, un secret invalide.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "AUTH_REQUIRED",
              "SESSION_EXPIRED",
              "WEBHOOK_UNAUTHORIZED"
            ]
          },
          "403": {
            "description": "La validation d'origine, CSRF ou de liaison navigateur a échoué.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "INVALID_ORIGIN",
              "CSRF_REQUIRED",
              "BROWSER_PROOF_REQUIRED",
              "INVALID_CHALLENGE"
            ]
          },
          "404": {
            "description": "L'enregistrement est manquant, le jeton public est invalide, ou l'enregistrement demandé appartient à un autre utilisateur.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "TICKET_NOT_FOUND",
              "LINK_NOT_FOUND"
            ]
          },
          "503": {
            "description": "Une intégration ou un stockage est indisponible, un événement est en cours de traitement, ou le service est indisponible.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "DATABASE_NOT_CONFIGURED",
              "ORIGIN_NOT_CONFIGURED",
              "TELEGRAM_NOT_CONFIGURED",
              "UPDATE_IN_PROGRESS",
              "SERVICE_UNAVAILABLE",
              "GATEWAY_NOT_CONNECTED"
            ]
          }
        }
      }
    },
    "/api/moderation/applications/{id}/decision": {
      "parameters": [
        {
          "name": "id",
          "in": "path",
          "required": true,
          "schema": {
            "type": "string",
            "pattern": "^app_[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$"
          }
        }
      ],
      "post": {
        "tags": [
          "Moderation"
        ],
        "operationId": "decideApplicationStage",
        "summary": "Approuver l'étape d'examen actuelle ou refuser avec une raison publique",
        "description": "Nécessite un modérateur configuré ; au plus 30 décisions/minute/utilisateur. expectedVersion est comparé atomiquement. Seule l'étape actuelle needs_review peut être approuvée ; les candidatures finales ne peuvent pas être rouvertes silencieusement. Un rejet exige une raison publique modifiable. La décision, l'historique des étapes et la notification activée au candidat sont enregistrés ensemble.",
        "security": [
          {
            "cookieSession": [],
            "csrfToken": []
          }
        ],
        "parameters": [
          {
            "name": "Origin",
            "in": "header",
            "required": true,
            "description": "Doit correspondre exactement à APP_ORIGIN. Sec-Fetch-Site : cross-site est rejeté. Le serveur ne fournit pas d'accès CORS.",
            "schema": {
              "type": "string",
              "format": "uri"
            }
          },
          {
            "name": "X-CSRF-Token",
            "in": "header",
            "required": true,
            "description": "Jeton de session actuel depuis GET /api/auth/me ou achèvement réussi de la connexion.",
            "schema": {
              "type": "string",
              "pattern": "^[A-Za-z0-9_-]{43}$"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "description": "Objet JSON. Maximum 16 384 octets. Les champs inconnus sont rejetés.",
          "content": {
            "application/json": {
              "schema": {
                "oneOf": [
                  {
                    "type": "object",
                    "properties": {
                      "expectedVersion": {
                        "type": "integer",
                        "minimum": 1
                      },
                      "action": {
                        "type": "string",
                        "const": "approve_step"
                      },
                      "reason": {
                        "type": "string",
                        "maxLength": 0
                      }
                    },
                    "required": [
                      "expectedVersion",
                      "action"
                    ],
                    "additionalProperties": false
                  },
                  {
                    "type": "object",
                    "properties": {
                      "expectedVersion": {
                        "type": "integer",
                        "minimum": 1
                      },
                      "action": {
                        "type": "string",
                        "const": "reject"
                      },
                      "reason": {
                        "type": "string",
                        "minLength": 10,
                        "maxLength": 1500
                      }
                    },
                    "required": [
                      "expectedVersion",
                      "action",
                      "reason"
                    ],
                    "additionalProperties": false
                  }
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Décision enregistrée.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "application": {
                      "$ref": "#/components/schemas/Application"
                    }
                  },
                  "required": [
                    "application"
                  ],
                  "additionalProperties": false
                }
              }
            }
          },
          "400": {
            "description": "JSON invalide, champ inconnu ou valeur invalide.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "INVALID_JSON",
              "UNKNOWN_FIELD",
              "INVALID_INPUT",
              "INVALID_VAT_PAYER",
              "INVALID_NOTIFICATIONS",
              "INVALID_TICKET_STATUS",
              "INVALID_UPDATE",
              "INVALID_HISTORY",
              "INVALID_PAGE_CONTEXT",
              "INVALID_LOCALE",
              "INVALID_FILTER",
              "INVALID_NOTIFICATION_SELECTION",
              "INVALID_AMOUNT",
              "INVALID_CURRENCY",
              "INVALID_METHODS",
              "INVALID_EXPIRY",
              "INVALID_QUOTE",
              "INVALID_LIMIT",
              "INVALID_CURSOR",
              "INVALID_STATUS",
              "IDEMPOTENCY_KEY_REQUIRED",
              "METHOD_UNAVAILABLE"
            ]
          },
          "401": {
            "description": "Aucune session valide ; pour un webhook, un secret invalide.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "AUTH_REQUIRED",
              "SESSION_EXPIRED",
              "WEBHOOK_UNAUTHORIZED"
            ]
          },
          "403": {
            "description": "La validation d'origine, CSRF ou de liaison navigateur a échoué.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "INVALID_ORIGIN",
              "CSRF_REQUIRED",
              "BROWSER_PROOF_REQUIRED",
              "INVALID_CHALLENGE"
            ]
          },
          "404": {
            "description": "L'enregistrement est manquant, le jeton public est invalide, ou l'enregistrement demandé appartient à un autre utilisateur.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "TICKET_NOT_FOUND",
              "LINK_NOT_FOUND"
            ]
          },
          "409": {
            "description": "La demande de connexion est utilisée ou annulée, l'identité n'est pas confirmée, ou une demande d'assistance entre en conflit.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "CHALLENGE_UNAVAILABLE",
              "IDENTITY_INCOMPLETE",
              "TICKET_CONFLICT",
              "IDEMPOTENCY_CONFLICT",
              "LINK_CANCELLED",
              "LINK_EXPIRED",
              "LINK_UNAVAILABLE"
            ]
          },
          "429": {
            "description": "Limite de débit dépassée.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              },
              "Retry-After": {
                "schema": {
                  "type": "string",
                  "const": "60"
                },
                "description": "Le serveur renvoie toujours 60 ; cela ne garantit pas qu'un quota horaire a expiré."
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "RATE_LIMITED"
            ]
          },
          "503": {
            "description": "Une intégration ou un stockage est indisponible, un événement est en cours de traitement, ou le service est indisponible.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "DATABASE_NOT_CONFIGURED",
              "ORIGIN_NOT_CONFIGURED",
              "TELEGRAM_NOT_CONFIGURED",
              "UPDATE_IN_PROGRESS",
              "SERVICE_UNAVAILABLE",
              "GATEWAY_NOT_CONNECTED"
            ]
          }
        }
      }
    },
    "/api/gateway/webhook": {
      "post": {
        "tags": [
          "Gateway events"
        ],
        "operationId": "receiveGatewayEvent",
        "summary": "Recevoir des événements de passerelle signés d'origine native",
        "description": "Natif v3 : fournir X-Gateway-Timestamp (secondes Unix), X-Gateway-Signature et X-Gateway-Event-Id correspondant. L'écart de horodatage maximal est de 300 secondes. Après vérification de la signature, Kevmi récupère le paiement via l'API de passerelle authentifiée, vérifie la commande enregistrée, la devise/montant exacts, la méthode et l'état confirmé par le fournisseur, puis stocke atomiquement le reçu et l'événement de boîte de sortie marchand. Le texte de webhook et les observations du navigateur seuls ne confirment jamais un paiement. Les identifiants répétés sont dédupliqués ; un corps modifié pour un identifiant existant est rejeté. Les événements retardés utilisent un état faisant autorité frais. Les remboursements partiels, les remboursements complets, les litiges et les états d'examen sont pris en charge. Les commandes non liées sont accusées réception et ignorées. Les événements normalisés hérités utilisant Kevmi-Signature restent pris en charge séparément ; seules leurs transitions succeeded/failed/full-refunded s'appliquent.",
        "security": [
          {
            "nativeGatewaySignature": []
          },
          {
            "gatewaySignature": []
          }
        ],
        "parameters": [
          {
            "name": "X-Gateway-Timestamp",
            "in": "header",
            "required": false,
            "schema": {
              "type": "string",
              "pattern": "^[0-9]{10}$"
            },
            "description": "Requis pour les événements natifs v3."
          },
          {
            "name": "X-Gateway-Event-Id",
            "in": "header",
            "required": false,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Requis pour le natif v3 ; doit correspondre à body.id."
          }
        ],
        "requestBody": {
          "required": true,
          "description": "Corps JSON signé exact ; limite native v3 : 524288 octets. Ne pas analyser et resérialiser avant la vérification de la signature.",
          "content": {
            "application/json": {
              "schema": {
                "oneOf": [
                  {
                    "$ref": "#/components/schemas/NativeGatewayEvent"
                  },
                  {
                    "$ref": "#/components/schemas/LegacyGatewayEvent"
                  }
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Reçu, dédupliqué ou ignoré en toute sécurité.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "received": {
                      "type": "boolean",
                      "const": true
                    },
                    "replayed": {
                      "type": "boolean"
                    },
                    "ignored": {
                      "type": "boolean"
                    },
                    "applied": {
                      "type": "boolean"
                    }
                  },
                  "required": [
                    "received"
                  ],
                  "additionalProperties": false
                }
              }
            }
          },
          "400": {
            "description": "JSON invalide, champ inconnu ou valeur invalide.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "INVALID_JSON",
              "UNKNOWN_FIELD",
              "INVALID_INPUT",
              "INVALID_VAT_PAYER",
              "INVALID_NOTIFICATIONS",
              "INVALID_TICKET_STATUS",
              "INVALID_UPDATE",
              "INVALID_HISTORY",
              "INVALID_PAGE_CONTEXT",
              "INVALID_LOCALE",
              "INVALID_FILTER",
              "INVALID_NOTIFICATION_SELECTION",
              "INVALID_AMOUNT",
              "INVALID_CURRENCY",
              "INVALID_METHODS",
              "INVALID_EXPIRY",
              "INVALID_QUOTE",
              "INVALID_LIMIT",
              "INVALID_CURSOR",
              "INVALID_STATUS",
              "IDEMPOTENCY_KEY_REQUIRED",
              "METHOD_UNAVAILABLE"
            ]
          },
          "401": {
            "description": "La signature de la passerelle est manquante, invalide ou hors de la fenêtre temporelle acceptée.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "INVALID_GATEWAY_SIGNATURE",
              "INVALID_SIGNATURE",
              "WEBHOOK_UNAUTHORIZED"
            ]
          },
          "404": {
            "description": "L'enregistrement est manquant, le jeton public est invalide, ou l'enregistrement demandé appartient à un autre utilisateur.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "TICKET_NOT_FOUND",
              "LINK_NOT_FOUND"
            ]
          },
          "409": {
            "description": "Un identifiant d'événement a été réutilisé avec un contenu modifié ou un identifiant de paiement entre en conflit avec la liaison de facture enregistrée.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "GATEWAY_EVENT_CONFLICT",
              "PAYMENT_ID_CONFLICT"
            ]
          },
          "413": {
            "description": "Le corps dépasse 16 384 octets ; la limite est de 65 536 octets pour /api/support/ask et le webhook Telegram.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "REQUEST_TOO_LARGE"
            ]
          },
          "415": {
            "description": "Content-Type : application/json est requis.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "JSON_REQUIRED"
            ]
          },
          "429": {
            "description": "La passerelle est occupée ; réessayer la livraison du webhook avec le même identifiant d'événement.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "GATEWAY_BUSY"
            ]
          },
          "502": {
            "description": "Réponse de la passerelle indisponible, malformée ou non vérifiable. Lisez le statut de paiement existant avant de tenter toute nouvelle tentative.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "GATEWAY_REQUEST_UNCERTAIN",
              "GATEWAY_INVALID_RESPONSE"
            ]
          },
          "503": {
            "description": "La configuration de la passerelle ou le secret de signature est manquant.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "GATEWAY_NOT_CONNECTED",
              "DATABASE_NOT_CONFIGURED"
            ]
          }
        }
      }
    },
    "/api/jobs/dispatch": {
      "post": {
        "tags": [
          "Service"
        ],
        "operationId": "dispatchBackgroundJobs",
        "summary": "Exécuter les tâches de livraison et d'application dues",
        "description": "Point d'entrée du planificateur serveur. Nécessite JOBS_SECRET, pas une session navigateur ni une clé marchande. N'accepte pas d'identifiants de tâche ni d'URL sortantes arbitraires.",
        "security": [
          {
            "jobSecret": []
          }
        ],
        "responses": {
          "200": {
            "description": "Terminé sans contexte d'arrière-plan.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": true
                }
              }
            }
          },
          "202": {
            "description": "Accepté dans un contexte d'exécution en arrière-plan.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "accepted": {
                      "const": true
                    }
                  },
                  "required": [
                    "accepted"
                  ],
                  "additionalProperties": false
                }
              }
            }
          },
          "401": {
            "description": "Aucune session valide ; pour un webhook, un secret invalide.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "AUTH_REQUIRED",
              "SESSION_EXPIRED",
              "WEBHOOK_UNAUTHORIZED"
            ]
          },
          "503": {
            "description": "Une intégration ou un stockage est indisponible, un événement est en cours de traitement, ou le service est indisponible.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "DATABASE_NOT_CONFIGURED",
              "ORIGIN_NOT_CONFIGURED",
              "TELEGRAM_NOT_CONFIGURED",
              "UPDATE_IN_PROGRESS",
              "SERVICE_UNAVAILABLE",
              "GATEWAY_NOT_CONNECTED"
            ]
          }
        }
      }
    },
    "/api/checkout/{token}/status": {
      "parameters": [
        {
          "name": "token",
          "in": "path",
          "required": true,
          "schema": {
            "type": "string",
            "pattern": "^[A-Za-z0-9_-]{43}$"
          },
          "description": "Identifiant public aléatoire de l'URL de paiement. Accorde l'accès en lecture uniquement à cette facture."
        }
      ],
      "get": {
        "tags": [
          "Checkout"
        ],
        "operationId": "getCheckoutPaymentStatus",
        "summary": "Lire et actualiser le même paiement",
        "description": "Aucun compte n'est requis. Actualise une opération existante via un transport de passerelle authentifié, avec limitation côté serveur. Aucun nouveau paiement n'est créé. Renvoie null si rien n'a été envoyé. Confirmez une commande uniquement lorsque fulfillmentReady est vrai.",
        "responses": {
          "200": {
            "description": "État de paiement assaini actuel.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "payment": {
                      "oneOf": [
                        {
                          "$ref": "#/components/schemas/CheckoutPayment"
                        },
                        {
                          "type": "null"
                        }
                      ]
                    },
                    "replayed": {
                      "type": "boolean"
                    }
                  },
                  "required": [
                    "payment"
                  ],
                  "additionalProperties": false
                }
              }
            }
          },
          "400": {
            "description": "JSON invalide, champ inconnu ou valeur invalide.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "INVALID_JSON",
              "UNKNOWN_FIELD",
              "INVALID_INPUT",
              "INVALID_VAT_PAYER",
              "INVALID_NOTIFICATIONS",
              "INVALID_TICKET_STATUS",
              "INVALID_UPDATE",
              "INVALID_HISTORY",
              "INVALID_PAGE_CONTEXT",
              "INVALID_LOCALE",
              "INVALID_FILTER",
              "INVALID_NOTIFICATION_SELECTION",
              "INVALID_AMOUNT",
              "INVALID_CURRENCY",
              "INVALID_METHODS",
              "INVALID_EXPIRY",
              "INVALID_QUOTE",
              "INVALID_LIMIT",
              "INVALID_CURSOR",
              "INVALID_STATUS",
              "IDEMPOTENCY_KEY_REQUIRED",
              "METHOD_UNAVAILABLE"
            ]
          },
          "404": {
            "description": "L'enregistrement est manquant, le jeton public est invalide, ou l'enregistrement demandé appartient à un autre utilisateur.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "TICKET_NOT_FOUND",
              "LINK_NOT_FOUND"
            ]
          },
          "429": {
            "description": "Limite de débit dépassée.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              },
              "Retry-After": {
                "schema": {
                  "type": "string",
                  "const": "60"
                },
                "description": "Le serveur renvoie toujours 60 ; cela ne garantit pas qu'un quota horaire a expiré."
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "RATE_LIMITED"
            ]
          },
          "502": {
            "description": "Réponse de la passerelle indisponible, malformée ou non vérifiable. Lisez le statut de paiement existant avant de tenter toute nouvelle tentative.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "GATEWAY_REQUEST_UNCERTAIN",
              "GATEWAY_INVALID_RESPONSE"
            ]
          },
          "503": {
            "description": "Une intégration ou un stockage est indisponible, un événement est en cours de traitement, ou le service est indisponible.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "DATABASE_NOT_CONFIGURED",
              "ORIGIN_NOT_CONFIGURED",
              "TELEGRAM_NOT_CONFIGURED",
              "UPDATE_IN_PROGRESS",
              "SERVICE_UNAVAILABLE",
              "GATEWAY_NOT_CONNECTED"
            ]
          }
        }
      }
    },
    "/api/checkout/{token}/reconcile": {
      "parameters": [
        {
          "name": "token",
          "in": "path",
          "required": true,
          "schema": {
            "type": "string",
            "pattern": "^[A-Za-z0-9_-]{43}$"
          },
          "description": "Identifiant public aléatoire de l'URL de paiement. Accorde l'accès en lecture uniquement à cette facture."
        }
      ],
      "post": {
        "tags": [
          "Checkout"
        ],
        "operationId": "reconcileCheckoutPayment",
        "summary": "Demander un réconciliation d'état faisant autorité",
        "description": "Lit l'opération existante et demande à la passerelle de réconcilier son état de fournisseur. Ne crée pas de paiement. Au plus 3 requêtes par facture par minute. Disponible pour les paiements déjà envoyés même si la facture n'est plus active.",
        "parameters": [
          {
            "name": "Origin",
            "in": "header",
            "required": true,
            "description": "Doit correspondre exactement à APP_ORIGIN. Sec-Fetch-Site : cross-site est rejeté. Le serveur ne fournit pas d'accès CORS.",
            "schema": {
              "type": "string",
              "format": "uri"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "description": "Objet JSON. Maximum 16 384 octets. Les champs inconnus sont rejetés.",
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/EmptyObject"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Paiement réconcilié ou null.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "payment": {
                      "oneOf": [
                        {
                          "$ref": "#/components/schemas/CheckoutPayment"
                        },
                        {
                          "type": "null"
                        }
                      ]
                    },
                    "replayed": {
                      "type": "boolean"
                    }
                  },
                  "required": [
                    "payment"
                  ],
                  "additionalProperties": false
                }
              }
            }
          },
          "400": {
            "description": "JSON invalide, champ inconnu ou valeur invalide.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "INVALID_JSON",
              "UNKNOWN_FIELD",
              "INVALID_INPUT",
              "INVALID_VAT_PAYER",
              "INVALID_NOTIFICATIONS",
              "INVALID_TICKET_STATUS",
              "INVALID_UPDATE",
              "INVALID_HISTORY",
              "INVALID_PAGE_CONTEXT",
              "INVALID_LOCALE",
              "INVALID_FILTER",
              "INVALID_NOTIFICATION_SELECTION",
              "INVALID_AMOUNT",
              "INVALID_CURRENCY",
              "INVALID_METHODS",
              "INVALID_EXPIRY",
              "INVALID_QUOTE",
              "INVALID_LIMIT",
              "INVALID_CURSOR",
              "INVALID_STATUS",
              "IDEMPOTENCY_KEY_REQUIRED",
              "METHOD_UNAVAILABLE"
            ]
          },
          "403": {
            "description": "La validation d'origine, CSRF ou de liaison navigateur a échoué.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "INVALID_ORIGIN",
              "CSRF_REQUIRED",
              "BROWSER_PROOF_REQUIRED",
              "INVALID_CHALLENGE"
            ]
          },
          "404": {
            "description": "L'enregistrement est manquant, le jeton public est invalide, ou l'enregistrement demandé appartient à un autre utilisateur.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "TICKET_NOT_FOUND",
              "LINK_NOT_FOUND"
            ]
          },
          "409": {
            "description": "La demande de connexion est utilisée ou annulée, l'identité n'est pas confirmée, ou une demande d'assistance entre en conflit.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "CHALLENGE_UNAVAILABLE",
              "IDENTITY_INCOMPLETE",
              "TICKET_CONFLICT",
              "IDEMPOTENCY_CONFLICT",
              "LINK_CANCELLED",
              "LINK_EXPIRED",
              "LINK_UNAVAILABLE"
            ]
          },
          "410": {
            "description": "L'intention de chiffrement de la passerelle ou le défi bancaire a expiré. Obtenez une nouvelle intention pour la même opération ; ne créez pas de paiement de remplacement.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "CARD_SESSION_INVALID"
            ]
          },
          "413": {
            "description": "Le corps dépasse 16 384 octets ; la limite est de 65 536 octets pour /api/support/ask et le webhook Telegram.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "REQUEST_TOO_LARGE"
            ]
          },
          "415": {
            "description": "Content-Type : application/json est requis.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "JSON_REQUIRED"
            ]
          },
          "429": {
            "description": "Limite de débit dépassée.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              },
              "Retry-After": {
                "schema": {
                  "type": "string",
                  "const": "60"
                },
                "description": "Le serveur renvoie toujours 60 ; cela ne garantit pas qu'un quota horaire a expiré."
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "RATE_LIMITED"
            ]
          },
          "502": {
            "description": "Réponse de la passerelle indisponible, malformée ou non vérifiable. Lisez le statut de paiement existant avant de tenter toute nouvelle tentative.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "GATEWAY_REQUEST_UNCERTAIN",
              "GATEWAY_INVALID_RESPONSE"
            ]
          },
          "503": {
            "description": "Une intégration ou un stockage est indisponible, un événement est en cours de traitement, ou le service est indisponible.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "DATABASE_NOT_CONFIGURED",
              "ORIGIN_NOT_CONFIGURED",
              "TELEGRAM_NOT_CONFIGURED",
              "UPDATE_IN_PROGRESS",
              "SERVICE_UNAVAILABLE",
              "GATEWAY_NOT_CONNECTED"
            ]
          }
        }
      }
    },
    "/api/checkout/{token}/card-intent": {
      "parameters": [
        {
          "name": "token",
          "in": "path",
          "required": true,
          "schema": {
            "type": "string",
            "pattern": "^[A-Za-z0-9_-]{43}$"
          },
          "description": "Identifiant public aléatoire de l'URL de paiement. Accorde l'accès en lecture uniquement à cette facture."
        }
      ],
      "post": {
        "tags": [
          "Checkout"
        ],
        "operationId": "createCardEncryptionIntent",
        "summary": "Obtenir une clé publique de passerelle et une intention de chiffrement liée",
        "description": "La passerelle seule possède la clé privée. Une intention expire dans les cinq minutes et lie la commande marchande, la devise, les montants de base/total, la clé d'idempotence de l'opération et l'objet. Les intentions de paiement exigent une facture active et un marchand approuvé ; les intentions de code exigent un défi bancaire existant et actuel. Aucun champ PAN, CVV, code bancaire ou JWK privé n'est accepté par ce point de terminaison. Au plus 8 intentions par facture et 12 par client par minute. Le texte en clair chiffré est {intent,name,country,email?,card:{number,expiration,cvv,holder}} pour le paiement ou {intent,code} pour le code. Le texte en clair est assemblé uniquement dans le navigateur avant le chiffrement JWE.",
        "parameters": [
          {
            "name": "Origin",
            "in": "header",
            "required": true,
            "description": "Doit correspondre exactement à APP_ORIGIN. Sec-Fetch-Site : cross-site est rejeté. Le serveur ne fournit pas d'accès CORS.",
            "schema": {
              "type": "string",
              "format": "uri"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "description": "Objet JSON, au plus 2048 octets.",
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "purpose": {
                    "type": "string",
                    "enum": [
                      "payment",
                      "code"
                    ],
                    "default": "payment"
                  }
                },
                "required": [],
                "additionalProperties": false
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Intention de courte durée et clé publique RSA uniquement.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CardIntent"
                }
              }
            }
          },
          "400": {
            "description": "JSON invalide, champ inconnu ou valeur invalide.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "INVALID_JSON",
              "UNKNOWN_FIELD",
              "INVALID_INPUT",
              "INVALID_VAT_PAYER",
              "INVALID_NOTIFICATIONS",
              "INVALID_TICKET_STATUS",
              "INVALID_UPDATE",
              "INVALID_HISTORY",
              "INVALID_PAGE_CONTEXT",
              "INVALID_LOCALE",
              "INVALID_FILTER",
              "INVALID_NOTIFICATION_SELECTION",
              "INVALID_AMOUNT",
              "INVALID_CURRENCY",
              "INVALID_METHODS",
              "INVALID_EXPIRY",
              "INVALID_QUOTE",
              "INVALID_LIMIT",
              "INVALID_CURSOR",
              "INVALID_STATUS",
              "IDEMPOTENCY_KEY_REQUIRED",
              "METHOD_UNAVAILABLE"
            ]
          },
          "403": {
            "description": "La validation d'origine, CSRF ou de liaison navigateur a échoué.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "INVALID_ORIGIN",
              "CSRF_REQUIRED",
              "BROWSER_PROOF_REQUIRED",
              "INVALID_CHALLENGE"
            ]
          },
          "404": {
            "description": "L'enregistrement est manquant, le jeton public est invalide, ou l'enregistrement demandé appartient à un autre utilisateur.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "TICKET_NOT_FOUND",
              "LINK_NOT_FOUND"
            ]
          },
          "409": {
            "description": "La demande de connexion est utilisée ou annulée, l'identité n'est pas confirmée, ou une demande d'assistance entre en conflit.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "CHALLENGE_UNAVAILABLE",
              "IDENTITY_INCOMPLETE",
              "TICKET_CONFLICT",
              "IDEMPOTENCY_CONFLICT",
              "LINK_CANCELLED",
              "LINK_EXPIRED",
              "LINK_UNAVAILABLE"
            ]
          },
          "410": {
            "description": "L'intention de chiffrement de la passerelle ou le défi bancaire a expiré. Obtenez une nouvelle intention pour la même opération ; ne créez pas de paiement de remplacement.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "CARD_SESSION_INVALID"
            ]
          },
          "413": {
            "description": "Le corps dépasse 16 384 octets ; la limite est de 65 536 octets pour /api/support/ask et le webhook Telegram.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "REQUEST_TOO_LARGE"
            ]
          },
          "415": {
            "description": "Content-Type : application/json est requis.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "JSON_REQUIRED"
            ]
          },
          "429": {
            "description": "Limite de débit dépassée.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              },
              "Retry-After": {
                "schema": {
                  "type": "string",
                  "const": "60"
                },
                "description": "Le serveur renvoie toujours 60 ; cela ne garantit pas qu'un quota horaire a expiré."
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "RATE_LIMITED"
            ]
          },
          "502": {
            "description": "Réponse de la passerelle indisponible, malformée ou non vérifiable. Lisez le statut de paiement existant avant de tenter toute nouvelle tentative.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "GATEWAY_REQUEST_UNCERTAIN",
              "GATEWAY_INVALID_RESPONSE"
            ]
          },
          "503": {
            "description": "Une intégration ou un stockage est indisponible, un événement est en cours de traitement, ou le service est indisponible.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "DATABASE_NOT_CONFIGURED",
              "ORIGIN_NOT_CONFIGURED",
              "TELEGRAM_NOT_CONFIGURED",
              "UPDATE_IN_PROGRESS",
              "SERVICE_UNAVAILABLE",
              "GATEWAY_NOT_CONNECTED"
            ]
          }
        }
      }
    },
    "/api/checkout/{token}/code": {
      "parameters": [
        {
          "name": "token",
          "in": "path",
          "required": true,
          "schema": {
            "type": "string",
            "pattern": "^[A-Za-z0-9_-]{43}$"
          },
          "description": "Identifiant public aléatoire de l'URL de paiement. Accorde l'accès en lecture uniquement à cette facture."
        }
      ],
      "post": {
        "tags": [
          "Checkout"
        ],
        "operationId": "submitEncryptedBankCode",
        "summary": "Relayer un code chiffré pour le défi bancaire actuel",
        "description": "Obtenir une intention purpose=code et chiffrer {intent,code} dans le navigateur. La passerelle valide le défi actuel et consomme l'intention scellée une fois. Kevmi ne reçoit jamais le code en clair. Au plus 5 soumissions par facture par minute ; actualiser l'état après une réponse incertaine.",
        "parameters": [
          {
            "name": "Origin",
            "in": "header",
            "required": true,
            "description": "Doit correspondre exactement à APP_ORIGIN. Sec-Fetch-Site : cross-site est rejeté. Le serveur ne fournit pas d'accès CORS.",
            "schema": {
              "type": "string",
              "format": "uri"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "description": "Objet JSON. Maximum 16 384 octets. Les champs inconnus sont rejetés.",
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "sealed": {
                    "$ref": "#/components/schemas/SealedCardData"
                  }
                },
                "required": [
                  "sealed"
                ],
                "additionalProperties": false
              }
            }
          }
        },
        "responses": {
          "202": {
            "description": "Code envoyé ; état de paiement actuel.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "payment": {
                      "oneOf": [
                        {
                          "$ref": "#/components/schemas/CheckoutPayment"
                        },
                        {
                          "type": "null"
                        }
                      ]
                    },
                    "replayed": {
                      "type": "boolean"
                    }
                  },
                  "required": [
                    "payment"
                  ],
                  "additionalProperties": false
                }
              }
            }
          },
          "400": {
            "description": "JSON invalide, champ inconnu ou valeur invalide.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "INVALID_JSON",
              "UNKNOWN_FIELD",
              "INVALID_INPUT",
              "INVALID_VAT_PAYER",
              "INVALID_NOTIFICATIONS",
              "INVALID_TICKET_STATUS",
              "INVALID_UPDATE",
              "INVALID_HISTORY",
              "INVALID_PAGE_CONTEXT",
              "INVALID_LOCALE",
              "INVALID_FILTER",
              "INVALID_NOTIFICATION_SELECTION",
              "INVALID_AMOUNT",
              "INVALID_CURRENCY",
              "INVALID_METHODS",
              "INVALID_EXPIRY",
              "INVALID_QUOTE",
              "INVALID_LIMIT",
              "INVALID_CURSOR",
              "INVALID_STATUS",
              "IDEMPOTENCY_KEY_REQUIRED",
              "METHOD_UNAVAILABLE"
            ]
          },
          "403": {
            "description": "La validation d'origine, CSRF ou de liaison navigateur a échoué.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "INVALID_ORIGIN",
              "CSRF_REQUIRED",
              "BROWSER_PROOF_REQUIRED",
              "INVALID_CHALLENGE"
            ]
          },
          "404": {
            "description": "L'enregistrement est manquant, le jeton public est invalide, ou l'enregistrement demandé appartient à un autre utilisateur.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "TICKET_NOT_FOUND",
              "LINK_NOT_FOUND"
            ]
          },
          "409": {
            "description": "La demande de connexion est utilisée ou annulée, l'identité n'est pas confirmée, ou une demande d'assistance entre en conflit.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "CHALLENGE_UNAVAILABLE",
              "IDENTITY_INCOMPLETE",
              "TICKET_CONFLICT",
              "IDEMPOTENCY_CONFLICT",
              "LINK_CANCELLED",
              "LINK_EXPIRED",
              "LINK_UNAVAILABLE"
            ]
          },
          "410": {
            "description": "L'intention de chiffrement de la passerelle ou le défi bancaire a expiré. Obtenez une nouvelle intention pour la même opération ; ne créez pas de paiement de remplacement.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "CARD_SESSION_INVALID"
            ]
          },
          "413": {
            "description": "Le corps dépasse 16 384 octets ; la limite est de 65 536 octets pour /api/support/ask et le webhook Telegram.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "REQUEST_TOO_LARGE"
            ]
          },
          "415": {
            "description": "Content-Type : application/json est requis.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "JSON_REQUIRED"
            ]
          },
          "429": {
            "description": "Limite de débit dépassée.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              },
              "Retry-After": {
                "schema": {
                  "type": "string",
                  "const": "60"
                },
                "description": "Le serveur renvoie toujours 60 ; cela ne garantit pas qu'un quota horaire a expiré."
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "RATE_LIMITED"
            ]
          },
          "502": {
            "description": "Réponse de la passerelle indisponible, malformée ou non vérifiable. Lisez le statut de paiement existant avant de tenter toute nouvelle tentative.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "GATEWAY_REQUEST_UNCERTAIN",
              "GATEWAY_INVALID_RESPONSE"
            ]
          },
          "503": {
            "description": "Une intégration ou un stockage est indisponible, un événement est en cours de traitement, ou le service est indisponible.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "DATABASE_NOT_CONFIGURED",
              "ORIGIN_NOT_CONFIGURED",
              "TELEGRAM_NOT_CONFIGURED",
              "UPDATE_IN_PROGRESS",
              "SERVICE_UNAVAILABLE",
              "GATEWAY_NOT_CONNECTED"
            ]
          }
        }
      }
    },
    "/api/checkout/{token}/crypto-services": {
      "parameters": [
        {
          "name": "token",
          "in": "path",
          "required": true,
          "schema": {
            "type": "string",
            "pattern": "^[A-Za-z0-9_-]{43}$"
          },
          "description": "Identifiant public aléatoire de l'URL de paiement. Accorde l'accès en lecture uniquement à cette facture."
        }
      ],
      "get": {
        "tags": [
          "Checkout"
        ],
        "operationId": "getCheckoutCryptoServices",
        "summary": "Lire le catalogue actuel des pièces et des réseaux",
        "description": "Uniquement pour une facture USDT qui autorise la crypto. La facture reste libellée en USDT ; la passerelle cite la pièce de payeur choisie. Ne jamais supposer que USD équivaut à USDT ni convertir le montant du transfert en nombre à virgule flottante. Au plus 10 lectures de catalogue par facture par minute.",
        "responses": {
          "200": {
            "description": "Catalogue fourni par la passerelle, avec disponibilité actuelle et limites de transfert.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "services": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "currency": {
                            "type": "string"
                          },
                          "network": {
                            "type": "string"
                          },
                          "networkLabel": {
                            "type": "string"
                          },
                          "available": {
                            "type": "boolean"
                          },
                          "minAmount": {
                            "type": "string",
                            "pattern": "^[0-9]+(?:\\.[0-9]+)?$",
                            "description": "Valeur décimale exacte dans la pièce de transfert sélectionnée."
                          },
                          "maxAmount": {
                            "type": "string",
                            "pattern": "^[0-9]+(?:\\.[0-9]+)?$",
                            "description": "Valeur décimale exacte dans la pièce de transfert sélectionnée."
                          },
                          "confirmations": {
                            "type": [
                              "integer",
                              "null"
                            ],
                            "minimum": 0
                          }
                        },
                        "required": [
                          "currency",
                          "network",
                          "networkLabel",
                          "available",
                          "minAmount",
                          "maxAmount",
                          "confirmations"
                        ],
                        "additionalProperties": false
                      }
                    },
                    "fetchedAt": {
                      "type": "integer",
                      "minimum": 0,
                      "description": "Horodatage Unix en millisecondes, UTC."
                    }
                  },
                  "required": [
                    "services",
                    "fetchedAt"
                  ],
                  "additionalProperties": false
                }
              }
            }
          },
          "400": {
            "description": "JSON invalide, champ inconnu ou valeur invalide.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "INVALID_JSON",
              "UNKNOWN_FIELD",
              "INVALID_INPUT",
              "INVALID_VAT_PAYER",
              "INVALID_NOTIFICATIONS",
              "INVALID_TICKET_STATUS",
              "INVALID_UPDATE",
              "INVALID_HISTORY",
              "INVALID_PAGE_CONTEXT",
              "INVALID_LOCALE",
              "INVALID_FILTER",
              "INVALID_NOTIFICATION_SELECTION",
              "INVALID_AMOUNT",
              "INVALID_CURRENCY",
              "INVALID_METHODS",
              "INVALID_EXPIRY",
              "INVALID_QUOTE",
              "INVALID_LIMIT",
              "INVALID_CURSOR",
              "INVALID_STATUS",
              "IDEMPOTENCY_KEY_REQUIRED",
              "METHOD_UNAVAILABLE"
            ]
          },
          "404": {
            "description": "L'enregistrement est manquant, le jeton public est invalide, ou l'enregistrement demandé appartient à un autre utilisateur.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "TICKET_NOT_FOUND",
              "LINK_NOT_FOUND"
            ]
          },
          "429": {
            "description": "Limite de débit dépassée.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              },
              "Retry-After": {
                "schema": {
                  "type": "string",
                  "const": "60"
                },
                "description": "Le serveur renvoie toujours 60 ; cela ne garantit pas qu'un quota horaire a expiré."
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "RATE_LIMITED"
            ]
          },
          "502": {
            "description": "Réponse de la passerelle indisponible, malformée ou non vérifiable. Lisez le statut de paiement existant avant de tenter toute nouvelle tentative.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "GATEWAY_REQUEST_UNCERTAIN",
              "GATEWAY_INVALID_RESPONSE"
            ]
          },
          "503": {
            "description": "Une intégration ou un stockage est indisponible, un événement est en cours de traitement, ou le service est indisponible.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "DATABASE_NOT_CONFIGURED",
              "ORIGIN_NOT_CONFIGURED",
              "TELEGRAM_NOT_CONFIGURED",
              "UPDATE_IN_PROGRESS",
              "SERVICE_UNAVAILABLE",
              "GATEWAY_NOT_CONNECTED"
            ]
          }
        }
      }
    },
    "/api/stores": {
      "get": {
        "tags": [
          "Account"
        ],
        "operationId": "listStores",
        "summary": "Répertorier toutes les boutiques possédées par le compte actuel",
        "security": [
          {
            "cookieSession": []
          }
        ],
        "responses": {
          "200": {
            "description": "Les identifiants de boutique sont distincts des identifiants de compte. Aucun secret n'est exposé.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "stores": {
                      "type": "array",
                      "maxItems": 100,
                      "items": {
                        "$ref": "#/components/schemas/Store"
                      }
                    },
                    "defaultStoreId": {
                      "type": "string",
                      "pattern": "^sto_[A-Za-z0-9_-]+$"
                    }
                  },
                  "required": [
                    "stores",
                    "defaultStoreId"
                  ],
                  "additionalProperties": false
                }
              }
            }
          },
          "401": {
            "description": "Aucune session valide ; pour un webhook, un secret invalide.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "AUTH_REQUIRED",
              "SESSION_EXPIRED",
              "WEBHOOK_UNAUTHORIZED"
            ]
          },
          "503": {
            "description": "Une intégration ou un stockage est indisponible, un événement est en cours de traitement, ou le service est indisponible.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "DATABASE_NOT_CONFIGURED",
              "ORIGIN_NOT_CONFIGURED",
              "TELEGRAM_NOT_CONFIGURED",
              "UPDATE_IN_PROGRESS",
              "SERVICE_UNAVAILABLE",
              "GATEWAY_NOT_CONNECTED"
            ]
          }
        }
      },
      "post": {
        "tags": [
          "Account"
        ],
        "operationId": "createStore",
        "summary": "Créer une boutique séparée",
        "description": "Jusqu'à 100 boutiques/compte et 10 créations/heure/compte. Une nouvelle boutique nécessite sa propre approbation de site Web avant de démarrer un paiement.",
        "security": [
          {
            "cookieSession": [],
            "csrfToken": []
          }
        ],
        "parameters": [
          {
            "name": "Origin",
            "in": "header",
            "required": true,
            "description": "Doit correspondre exactement à APP_ORIGIN. Sec-Fetch-Site : cross-site est rejeté. Le serveur ne fournit pas d'accès CORS.",
            "schema": {
              "type": "string",
              "format": "uri"
            }
          },
          {
            "name": "X-CSRF-Token",
            "in": "header",
            "required": true,
            "description": "Jeton de session actuel depuis GET /api/auth/me ou achèvement réussi de la connexion.",
            "schema": {
              "type": "string",
              "pattern": "^[A-Za-z0-9_-]{43}$"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "description": "Objet JSON. Maximum 16 384 octets. Les champs inconnus sont rejetés.",
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "name": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 80
                  }
                },
                "required": [
                  "name"
                ],
                "additionalProperties": false
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Créé.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "store": {
                      "$ref": "#/components/schemas/Store"
                    }
                  },
                  "required": [
                    "store"
                  ],
                  "additionalProperties": false
                }
              }
            }
          },
          "400": {
            "description": "JSON invalide, champ inconnu ou valeur invalide.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "INVALID_JSON",
              "UNKNOWN_FIELD",
              "INVALID_INPUT",
              "INVALID_VAT_PAYER",
              "INVALID_NOTIFICATIONS",
              "INVALID_TICKET_STATUS",
              "INVALID_UPDATE",
              "INVALID_HISTORY",
              "INVALID_PAGE_CONTEXT",
              "INVALID_LOCALE",
              "INVALID_FILTER",
              "INVALID_NOTIFICATION_SELECTION",
              "INVALID_AMOUNT",
              "INVALID_CURRENCY",
              "INVALID_METHODS",
              "INVALID_EXPIRY",
              "INVALID_QUOTE",
              "INVALID_LIMIT",
              "INVALID_CURSOR",
              "INVALID_STATUS",
              "IDEMPOTENCY_KEY_REQUIRED",
              "METHOD_UNAVAILABLE"
            ]
          },
          "401": {
            "description": "Aucune session valide ; pour un webhook, un secret invalide.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "AUTH_REQUIRED",
              "SESSION_EXPIRED",
              "WEBHOOK_UNAUTHORIZED"
            ]
          },
          "403": {
            "description": "La validation d'origine, CSRF ou de liaison navigateur a échoué.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "INVALID_ORIGIN",
              "CSRF_REQUIRED",
              "BROWSER_PROOF_REQUIRED",
              "INVALID_CHALLENGE"
            ]
          },
          "409": {
            "description": "La demande de connexion est utilisée ou annulée, l'identité n'est pas confirmée, ou une demande d'assistance entre en conflit.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "CHALLENGE_UNAVAILABLE",
              "IDENTITY_INCOMPLETE",
              "TICKET_CONFLICT",
              "IDEMPOTENCY_CONFLICT",
              "LINK_CANCELLED",
              "LINK_EXPIRED",
              "LINK_UNAVAILABLE"
            ]
          },
          "429": {
            "description": "Limite de débit dépassée.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              },
              "Retry-After": {
                "schema": {
                  "type": "string",
                  "const": "60"
                },
                "description": "Le serveur renvoie toujours 60 ; cela ne garantit pas qu'un quota horaire a expiré."
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "RATE_LIMITED"
            ]
          },
          "503": {
            "description": "Une intégration ou un stockage est indisponible, un événement est en cours de traitement, ou le service est indisponible.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "DATABASE_NOT_CONFIGURED",
              "ORIGIN_NOT_CONFIGURED",
              "TELEGRAM_NOT_CONFIGURED",
              "UPDATE_IN_PROGRESS",
              "SERVICE_UNAVAILABLE",
              "GATEWAY_NOT_CONNECTED"
            ]
          }
        }
      }
    },
    "/api/stores/{id}": {
      "parameters": [
        {
          "name": "id",
          "in": "path",
          "required": true,
          "schema": {
            "type": "string",
            "pattern": "^sto_[A-Za-z0-9_-]+$"
          }
        }
      ],
      "patch": {
        "tags": [
          "Account"
        ],
        "operationId": "updateStore",
        "summary": "Renommer une boutique possédée ou modifier sa TVA par défaut",
        "security": [
          {
            "cookieSession": [],
            "csrfToken": []
          }
        ],
        "parameters": [
          {
            "name": "Origin",
            "in": "header",
            "required": true,
            "description": "Doit correspondre exactement à APP_ORIGIN. Sec-Fetch-Site : cross-site est rejeté. Le serveur ne fournit pas d'accès CORS.",
            "schema": {
              "type": "string",
              "format": "uri"
            }
          },
          {
            "name": "X-CSRF-Token",
            "in": "header",
            "required": true,
            "description": "Jeton de session actuel depuis GET /api/auth/me ou achèvement réussi de la connexion.",
            "schema": {
              "type": "string",
              "pattern": "^[A-Za-z0-9_-]{43}$"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "description": "Objet JSON. Maximum 16 384 octets. Les champs inconnus sont rejetés.",
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "name": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 80
                  },
                  "vatPayer": {
                    "type": "string",
                    "enum": [
                      "customer",
                      "merchant"
                    ]
                  }
                },
                "required": [],
                "additionalProperties": false
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Les cotations de facture existantes sont inchangées.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "store": {
                      "$ref": "#/components/schemas/Store"
                    }
                  },
                  "required": [
                    "store"
                  ],
                  "additionalProperties": false
                }
              }
            }
          },
          "400": {
            "description": "JSON invalide, champ inconnu ou valeur invalide.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "INVALID_JSON",
              "UNKNOWN_FIELD",
              "INVALID_INPUT",
              "INVALID_VAT_PAYER",
              "INVALID_NOTIFICATIONS",
              "INVALID_TICKET_STATUS",
              "INVALID_UPDATE",
              "INVALID_HISTORY",
              "INVALID_PAGE_CONTEXT",
              "INVALID_LOCALE",
              "INVALID_FILTER",
              "INVALID_NOTIFICATION_SELECTION",
              "INVALID_AMOUNT",
              "INVALID_CURRENCY",
              "INVALID_METHODS",
              "INVALID_EXPIRY",
              "INVALID_QUOTE",
              "INVALID_LIMIT",
              "INVALID_CURSOR",
              "INVALID_STATUS",
              "IDEMPOTENCY_KEY_REQUIRED",
              "METHOD_UNAVAILABLE"
            ]
          },
          "401": {
            "description": "Aucune session valide ; pour un webhook, un secret invalide.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "AUTH_REQUIRED",
              "SESSION_EXPIRED",
              "WEBHOOK_UNAUTHORIZED"
            ]
          },
          "403": {
            "description": "La validation d'origine, CSRF ou de liaison navigateur a échoué.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "INVALID_ORIGIN",
              "CSRF_REQUIRED",
              "BROWSER_PROOF_REQUIRED",
              "INVALID_CHALLENGE"
            ]
          },
          "404": {
            "description": "L'enregistrement est manquant, le jeton public est invalide, ou l'enregistrement demandé appartient à un autre utilisateur.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "TICKET_NOT_FOUND",
              "LINK_NOT_FOUND"
            ]
          },
          "503": {
            "description": "Une intégration ou un stockage est indisponible, un événement est en cours de traitement, ou le service est indisponible.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "DATABASE_NOT_CONFIGURED",
              "ORIGIN_NOT_CONFIGURED",
              "TELEGRAM_NOT_CONFIGURED",
              "UPDATE_IN_PROGRESS",
              "SERVICE_UNAVAILABLE",
              "GATEWAY_NOT_CONNECTED"
            ]
          }
        }
      }
    },
    "/api/exchange-rates": {
      "get": {
        "tags": [
          "Payments"
        ],
        "operationId": "getReferenceExchangeRate",
        "summary": "Obtenir le taux de référence actuel USD/RUB",
        "description": "Mis en cache côté serveur pendant une heure. Le taux numérique utilise des millionièmes de RUB par USD. Aucune cotation de secours n'est inventée. Utilisé à la création de facture ; une facture existante conserve son propre taux.",
        "security": [],
        "responses": {
          "200": {
            "description": "Source officielle, date de publication et heure de récupération.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "rate": {
                      "$ref": "#/components/schemas/ExchangeRate"
                    }
                  },
                  "required": [
                    "rate"
                  ],
                  "additionalProperties": false
                }
              }
            }
          },
          "503": {
            "description": "Une intégration ou un stockage est indisponible, un événement est en cours de traitement, ou le service est indisponible.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "DATABASE_NOT_CONFIGURED",
              "ORIGIN_NOT_CONFIGURED",
              "TELEGRAM_NOT_CONFIGURED",
              "UPDATE_IN_PROGRESS",
              "SERVICE_UNAVAILABLE",
              "GATEWAY_NOT_CONNECTED"
            ]
          }
        }
      }
    },
    "/api/account/dashboard": {
      "get": {
        "tags": [
          "Account"
        ],
        "operationId": "getAccountDashboard",
        "summary": "Lire les reçus vérifiés pour une boutique ou toutes les boutiques possédées",
        "description": "Les reçus nets ne constituent pas un solde retirable. Les monnaies héritées restent séparées. Les totaux sont de tous les temps ; les répartitions quotidiennes/par méthode utilisent la période UTC demandée.",
        "security": [
          {
            "cookieSession": []
          }
        ],
        "parameters": [
          {
            "name": "X-Kevmi-Store",
            "in": "header",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Sélectionnez une boutique possédée pour une session de navigateur. Omett pour utiliser la première boutique. Les clés API ignorent cet en-tête et utilisent toujours leur liaison de boutique immuable. La valeur all est en lecture seule et n'est prise en charge que par le tableau de bord et les points de terminaison de liste de factures."
          },
          {
            "name": "days",
            "in": "query",
            "schema": {
              "type": "integer",
              "enum": [
                7,
                30
              ],
              "default": 7
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Résumé comptable en lecture seule.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "storeId": {
                      "type": [
                        "string",
                        "null"
                      ]
                    },
                    "stores": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "id": {
                            "type": "string",
                            "pattern": "^sto_[A-Za-z0-9_-]+$"
                          },
                          "name": {
                            "type": "string"
                          },
                          "currency": {
                            "type": [
                              "string",
                              "null"
                            ]
                          },
                          "invoices": {
                            "type": "integer"
                          },
                          "netMinor": {
                            "type": "integer",
                            "minimum": 0,
                            "maximum": 9007199254740991,
                            "description": "Montant entier en unités monétaires mineures."
                          }
                        },
                        "required": [
                          "id",
                          "name",
                          "currency",
                          "invoices",
                          "netMinor"
                        ],
                        "additionalProperties": false
                      }
                    },
                    "totals": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "currency": {
                            "type": "string"
                          },
                          "invoices": {
                            "type": "integer"
                          },
                          "succeeded": {
                            "type": "integer"
                          },
                          "grossMinor": {
                            "type": "integer",
                            "minimum": 0,
                            "maximum": 9007199254740991,
                            "description": "Montant entier en unités monétaires mineures."
                          },
                          "netMinor": {
                            "type": "integer",
                            "minimum": 0,
                            "maximum": 9007199254740991,
                            "description": "Montant entier en unités monétaires mineures."
                          }
                        },
                        "required": [
                          "currency",
                          "invoices",
                          "succeeded",
                          "grossMinor",
                          "netMinor"
                        ],
                        "additionalProperties": false
                      }
                    },
                    "daily": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "currency": {
                            "type": "string"
                          },
                          "day": {
                            "type": "string"
                          },
                          "count": {
                            "type": "integer"
                          },
                          "grossMinor": {
                            "type": "integer",
                            "minimum": 0,
                            "maximum": 9007199254740991,
                            "description": "Montant entier en unités monétaires mineures."
                          }
                        },
                        "required": [
                          "currency",
                          "day",
                          "count",
                          "grossMinor"
                        ],
                        "additionalProperties": false
                      }
                    },
                    "methods": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "currency": {
                            "type": "string"
                          },
                          "method": {
                            "type": "string"
                          },
                          "count": {
                            "type": "integer"
                          },
                          "grossMinor": {
                            "type": "integer",
                            "minimum": 0,
                            "maximum": 9007199254740991,
                            "description": "Montant entier en unités monétaires mineures."
                          }
                        },
                        "required": [
                          "currency",
                          "method",
                          "count",
                          "grossMinor"
                        ],
                        "additionalProperties": false
                      }
                    },
                    "days": {
                      "type": "integer",
                      "enum": [
                        7,
                        30
                      ]
                    },
                    "from": {
                      "type": "integer",
                      "minimum": 0,
                      "description": "Horodatage Unix en millisecondes, UTC."
                    },
                    "generatedAt": {
                      "type": "integer",
                      "minimum": 0,
                      "description": "Horodatage Unix en millisecondes, UTC."
                    },
                    "accountingBasis": {
                      "type": "string",
                      "const": "verified_receipts"
                    },
                    "withdrawableBalance": {
                      "type": "null"
                    },
                    "payoutCreationAvailable": {
                      "type": "boolean",
                      "const": false
                    }
                  },
                  "required": [
                    "storeId",
                    "stores",
                    "totals",
                    "daily",
                    "methods",
                    "days",
                    "from",
                    "generatedAt",
                    "accountingBasis",
                    "withdrawableBalance",
                    "payoutCreationAvailable"
                  ],
                  "additionalProperties": false
                }
              }
            }
          },
          "400": {
            "description": "JSON invalide, champ inconnu ou valeur invalide.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "INVALID_JSON",
              "UNKNOWN_FIELD",
              "INVALID_INPUT",
              "INVALID_VAT_PAYER",
              "INVALID_NOTIFICATIONS",
              "INVALID_TICKET_STATUS",
              "INVALID_UPDATE",
              "INVALID_HISTORY",
              "INVALID_PAGE_CONTEXT",
              "INVALID_LOCALE",
              "INVALID_FILTER",
              "INVALID_NOTIFICATION_SELECTION",
              "INVALID_AMOUNT",
              "INVALID_CURRENCY",
              "INVALID_METHODS",
              "INVALID_EXPIRY",
              "INVALID_QUOTE",
              "INVALID_LIMIT",
              "INVALID_CURSOR",
              "INVALID_STATUS",
              "IDEMPOTENCY_KEY_REQUIRED",
              "METHOD_UNAVAILABLE"
            ]
          },
          "401": {
            "description": "Aucune session valide ; pour un webhook, un secret invalide.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "AUTH_REQUIRED",
              "SESSION_EXPIRED",
              "WEBHOOK_UNAUTHORIZED"
            ]
          },
          "404": {
            "description": "L'enregistrement est manquant, le jeton public est invalide, ou l'enregistrement demandé appartient à un autre utilisateur.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "TICKET_NOT_FOUND",
              "LINK_NOT_FOUND"
            ]
          },
          "503": {
            "description": "Une intégration ou un stockage est indisponible, un événement est en cours de traitement, ou le service est indisponible.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "DATABASE_NOT_CONFIGURED",
              "ORIGIN_NOT_CONFIGURED",
              "TELEGRAM_NOT_CONFIGURED",
              "UPDATE_IN_PROGRESS",
              "SERVICE_UNAVAILABLE",
              "GATEWAY_NOT_CONNECTED"
            ]
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "cookieSession": {
        "type": "apiKey",
        "in": "cookie",
        "name": "__Host-kevmi-session",
        "description": "Jeton de session aléatoire, Secure/HttpOnly/SameSite=Lax, valide 24 heures. Un hachage est stocké dans la base de données. Émis uniquement après une connexion Telegram vérifiée."
      },
      "browserProof": {
        "type": "apiKey",
        "in": "cookie",
        "name": "__Host-kevmi-login",
        "description": "Cookie HttpOnly dès le départ : challengeId.proof, valide 300 secondes. Exclu du lien profond Telegram."
      },
      "csrfToken": {
        "type": "apiKey",
        "in": "header",
        "name": "X-CSRF-Token",
        "description": "Jeton CSRF de session émis par /api/auth/me ou /api/auth/telegram/complete. Les mutations exigent également l'Origin exact."
      },
      "telegramWebhookSecret": {
        "type": "apiKey",
        "in": "header",
        "name": "X-Telegram-Bot-Api-Secret-Token",
        "description": "Secret de webhook séparé configuré côté serveur. Sa valeur est privée. Ce n'est pas le jeton du bot."
      },
      "merchantKey": {
        "type": "http",
        "scheme": "bearer",
        "bearerFormat": "kv_live_…",
        "description": "Clé API marchande à portée limitée. Seul son hachage est stocké. Utilisation serveur-à-serveur uniquement ; ne jamais la placer dans un widget ou un bundle navigateur."
      },
      "gatewaySignature": {
        "type": "apiKey",
        "in": "header",
        "name": "Kevmi-Signature",
        "description": "t=<secondes Unix>,v1=<hex HMAC-SHA256(secret, timestamp + point + corps de requête UTF-8 exact)>. Écart de horodatage maximal : 300 secondes."
      },
      "jobSecret": {
        "type": "http",
        "scheme": "bearer",
        "description": "JOBS_SECRET séparé réservé au serveur. Ce n'est pas une clé API marchande."
      },
      "nativeGatewaySignature": {
        "type": "apiKey",
        "in": "header",
        "name": "X-Gateway-Signature",
        "description": "v1=<hex HMAC-SHA256(GATEWAY_WEBHOOK_SECRET, timestamp + \".\" + corps UTF-8 brut exact)> ; séparé de la Kevmi-Signature marchande."
      }
    },
    "schemas": {
      "Error": {
        "type": "object",
        "properties": {
          "error": {
            "type": "object",
            "properties": {
              "code": {
                "type": "string"
              },
              "message": {
                "type": "string"
              }
            },
            "required": [
              "code",
              "message"
            ],
            "additionalProperties": false
          }
        },
        "required": [
          "error"
        ],
        "additionalProperties": false
      },
      "EmptyObject": {
        "type": "object",
        "properties": {},
        "required": [],
        "additionalProperties": false
      },
      "Config": {
        "type": "object",
        "properties": {
          "telegram": {
            "type": "object",
            "properties": {
              "enabled": {
                "type": "boolean"
              },
              "botUsername": {
                "type": [
                  "string",
                  "null"
                ]
              }
            },
            "required": [
              "enabled",
              "botUsername"
            ],
            "additionalProperties": false
          },
          "support": {
            "type": "object",
            "properties": {
              "mode": {
                "type": "string",
                "enum": [
                  "search",
                  "ai"
                ]
              },
              "available": {
                "type": "boolean",
                "const": true
              }
            },
            "required": [
              "mode",
              "available"
            ],
            "additionalProperties": false
          },
          "gateway": {
            "type": "object",
            "properties": {
              "configured": {
                "type": "boolean"
              },
              "connected": {
                "type": "boolean",
                "const": false,
                "description": "Le point de terminaison de configuration publique ne sonde pas la santé de la passerelle. La configuration n'est pas une vérification de connexion active réussie."
              },
              "status": {
                "type": "string",
                "enum": [
                  "configured",
                  "not_configured"
                ]
              }
            },
            "required": [
              "configured",
              "connected",
              "status"
            ],
            "additionalProperties": false
          },
          "capabilities": {
            "type": "object",
            "properties": {
              "server": {
                "type": "boolean",
                "const": true
              },
              "account": {
                "type": "boolean"
              },
              "notifications": {
                "type": "boolean"
              },
              "tickets": {
                "type": "boolean"
              },
              "directPaymentsOnly": {
                "type": "boolean",
                "const": true
              },
              "paymentLinks": {
                "type": "boolean",
                "description": "D1 est configuré pour les factures serveur. Cela n'indique pas la disponibilité du paiement."
              },
              "apiKeys": {
                "type": "boolean"
              },
              "webhooks": {
                "type": "boolean"
              },
              "widgets": {
                "type": "boolean"
              },
              "applications": {
                "type": "boolean"
              },
              "applicationNotifications": {
                "type": "boolean"
              }
            },
            "required": [
              "server",
              "account",
              "notifications",
              "tickets",
              "directPaymentsOnly",
              "paymentLinks",
              "apiKeys",
              "webhooks",
              "widgets",
              "applications",
              "applicationNotifications"
            ],
            "additionalProperties": false
          }
        },
        "required": [
          "telegram",
          "support",
          "gateway",
          "capabilities"
        ],
        "additionalProperties": false
      },
      "Health": {
        "type": "object",
        "properties": {
          "telegram": {
            "type": "object",
            "properties": {
              "enabled": {
                "type": "boolean"
              },
              "botUsername": {
                "type": [
                  "string",
                  "null"
                ]
              }
            },
            "required": [
              "enabled",
              "botUsername"
            ],
            "additionalProperties": false
          },
          "support": {
            "type": "object",
            "properties": {
              "mode": {
                "type": "string",
                "enum": [
                  "search",
                  "ai"
                ]
              },
              "available": {
                "type": "boolean",
                "const": true
              }
            },
            "required": [
              "mode",
              "available"
            ],
            "additionalProperties": false
          },
          "gateway": {
            "type": "object",
            "properties": {
              "configured": {
                "type": "boolean"
              },
              "connected": {
                "type": "boolean",
                "const": false,
                "description": "Le point de terminaison de configuration publique ne sonde pas la santé de la passerelle. La configuration n'est pas une vérification de connexion active réussie."
              },
              "status": {
                "type": "string",
                "enum": [
                  "configured",
                  "not_configured"
                ]
              }
            },
            "required": [
              "configured",
              "connected",
              "status"
            ],
            "additionalProperties": false
          },
          "capabilities": {
            "type": "object",
            "properties": {
              "server": {
                "type": "boolean",
                "const": true
              },
              "account": {
                "type": "boolean"
              },
              "notifications": {
                "type": "boolean"
              },
              "tickets": {
                "type": "boolean"
              },
              "directPaymentsOnly": {
                "type": "boolean",
                "const": true
              },
              "paymentLinks": {
                "type": "boolean",
                "description": "D1 est configuré pour les factures serveur. Cela n'indique pas la disponibilité du paiement."
              },
              "apiKeys": {
                "type": "boolean"
              },
              "webhooks": {
                "type": "boolean"
              },
              "widgets": {
                "type": "boolean"
              },
              "applications": {
                "type": "boolean"
              },
              "applicationNotifications": {
                "type": "boolean"
              }
            },
            "required": [
              "server",
              "account",
              "notifications",
              "tickets",
              "directPaymentsOnly",
              "paymentLinks",
              "apiKeys",
              "webhooks",
              "widgets",
              "applications",
              "applicationNotifications"
            ],
            "additionalProperties": false
          },
          "status": {
            "type": "string",
            "enum": [
              "ok",
              "degraded"
            ]
          },
          "storage": {
            "type": "string",
            "enum": [
              "ready",
              "not_configured",
              "unavailable"
            ]
          }
        },
        "required": [
          "telegram",
          "support",
          "gateway",
          "capabilities",
          "status",
          "storage"
        ],
        "additionalProperties": false
      },
      "NotificationPreferences": {
        "type": "object",
        "properties": {
          "payments": {
            "type": "boolean"
          },
          "payouts": {
            "type": "boolean"
          },
          "support": {
            "type": "boolean"
          },
          "security": {
            "type": "boolean"
          }
        },
        "required": [
          "payments",
          "payouts",
          "support",
          "security"
        ],
        "additionalProperties": false
      },
      "SettingsPatch": {
        "type": "object",
        "properties": {
          "storeName": {
            "type": "string",
            "minLength": 1,
            "x-trimmedMaxLength": 80,
            "x-trimmedMinLength": 1,
            "description": "Les espaces environnants sont supprimés. Longueur après suppression : 1–80 caractères. Les caractères de contrôle sauf les tabulations et les sauts de ligne sont interdits."
          },
          "vatPayer": {
            "type": "string",
            "enum": [
              "customer",
              "merchant"
            ]
          },
          "notifications": {
            "type": "object",
            "properties": {
              "payments": {
                "type": "boolean"
              },
              "payouts": {
                "type": "boolean"
              },
              "support": {
                "type": "boolean"
              },
              "security": {
                "type": "boolean"
              }
            },
            "required": [],
            "additionalProperties": false
          }
        },
        "required": [],
        "additionalProperties": false
      },
      "User": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "displayName": {
            "type": "string"
          },
          "username": {
            "type": [
              "string",
              "null"
            ]
          },
          "storeName": {
            "type": "string"
          },
          "vatPayer": {
            "type": "string",
            "enum": [
              "customer",
              "merchant"
            ]
          },
          "notifications": {
            "$ref": "#/components/schemas/NotificationPreferences"
          },
          "isModerator": {
            "type": "boolean",
            "description": "Indique si cette identité Telegram appartient à la liste d'autorisation des modérateurs configurée."
          }
        },
        "required": [
          "id",
          "displayName",
          "username",
          "storeName",
          "vatPayer",
          "notifications",
          "isModerator"
        ],
        "additionalProperties": false
      },
      "UserResponse": {
        "type": "object",
        "properties": {
          "user": {
            "$ref": "#/components/schemas/User"
          }
        },
        "required": [
          "user"
        ],
        "additionalProperties": false
      },
      "Anonymous": {
        "type": "object",
        "properties": {
          "authenticated": {
            "type": "boolean",
            "const": false
          }
        },
        "required": [
          "authenticated"
        ],
        "additionalProperties": false
      },
      "CurrentSession": {
        "type": "object",
        "properties": {
          "authenticated": {
            "type": "boolean",
            "const": true
          },
          "user": {
            "$ref": "#/components/schemas/User"
          },
          "csrfToken": {
            "type": "string",
            "pattern": "^[A-Za-z0-9_-]{43}$"
          },
          "expiresAt": {
            "type": "integer",
            "minimum": 0,
            "description": "Horodatage Unix en millisecondes, UTC."
          }
        },
        "required": [
          "authenticated",
          "user",
          "csrfToken",
          "expiresAt"
        ],
        "additionalProperties": false
      },
      "LoginChallenge": {
        "type": "object",
        "properties": {
          "challengeId": {
            "type": "string",
            "pattern": "^[A-Za-z0-9_-]{43}$"
          },
          "code": {
            "type": "string",
            "pattern": "^[0-9]{6}$"
          },
          "expiresAt": {
            "type": "integer",
            "minimum": 0,
            "description": "Horodatage Unix en millisecondes, UTC."
          },
          "telegramUrl": {
            "type": "string",
            "format": "uri",
            "pattern": "^https://t\\.me/"
          }
        },
        "required": [
          "challengeId",
          "code",
          "expiresAt",
          "telegramUrl"
        ],
        "additionalProperties": false
      },
      "CompletedLogin": {
        "type": "object",
        "properties": {
          "status": {
            "type": "string",
            "const": "authenticated"
          },
          "user": {
            "$ref": "#/components/schemas/User"
          },
          "csrfToken": {
            "type": "string",
            "pattern": "^[A-Za-z0-9_-]{43}$"
          },
          "expiresAt": {
            "type": "integer",
            "minimum": 0,
            "description": "Horodatage Unix en millisecondes, UTC."
          }
        },
        "required": [
          "status",
          "user",
          "csrfToken",
          "expiresAt"
        ],
        "additionalProperties": false
      },
      "SupportQuestion": {
        "type": "object",
        "properties": {
          "question": {
            "type": "string",
            "minLength": 2,
            "x-trimmedMaxLength": 2000,
            "x-trimmedMinLength": 2,
            "description": "Les espaces environnants sont supprimés. Longueur après suppression : 2–2 000 caractères. Les caractères de contrôle sauf les tabulations et les sauts de ligne sont interdits."
          },
          "locale": {
            "type": "string",
            "enum": [
              "ru",
              "en",
              "es",
              "de",
              "fr",
              "pt",
              "tr",
              "kk"
            ],
            "default": "en",
            "description": "Langue de réponse. Si omise, les préférences Accept-Language prises en charge sont utilisées ; sinon anglais. La documentation est disponible dans chaque langue d'interface prise en charge."
          },
          "history": {
            "type": "array",
            "maxItems": 6,
            "description": "Jusqu'aux trois dernières paires question-réponse. Optionnel. Contexte non fiable, pas des messages de rôle système du fournisseur.",
            "items": {
              "oneOf": [
                {
                  "type": "object",
                  "properties": {
                    "role": {
                      "type": "string",
                      "const": "user"
                    },
                    "content": {
                      "type": "string",
                      "minLength": 1,
                      "x-trimmedMaxLength": 1800,
                      "x-trimmedMinLength": 1,
                      "description": "Les espaces environnants sont supprimés. Longueur après suppression : 1–1 800 caractères. Les caractères de contrôle sauf les tabulations et les sauts de ligne sont interdits."
                    }
                  },
                  "required": [
                    "role",
                    "content"
                  ],
                  "additionalProperties": false
                },
                {
                  "type": "object",
                  "properties": {
                    "role": {
                      "type": "string",
                      "const": "assistant"
                    },
                    "content": {
                      "type": "string",
                      "minLength": 1,
                      "x-trimmedMaxLength": 3000,
                      "x-trimmedMinLength": 1,
                      "description": "Les espaces environnants sont supprimés. Longueur après suppression : 1–3 000 caractères. Les caractères de contrôle sauf les tabulations et les sauts de ligne sont interdits."
                    }
                  },
                  "required": [
                    "role",
                    "content"
                  ],
                  "additionalProperties": false
                }
              ]
            }
          },
          "pageContext": {
            "type": "string",
            "minLength": 1,
            "x-trimmedMaxLength": 80,
            "x-trimmedMinLength": 1,
            "description": "Libellé de section court après suppression, 1–80 caractères. Autorise les lettres, chiffres, espaces et ·.,—–-:(). Les sauts de ligne, URLs, paramètres de requête et HTML sont interdits. Ne pas inclure de jetons ni de secrets.",
            "pattern": "^[\\p{L}\\p{N}\\s·.,—–\\-:()]+$",
            "not": {
              "pattern": "[\\r\\n]"
            }
          }
        },
        "required": [
          "question"
        ],
        "additionalProperties": false
      },
      "SupportSource": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "title": {
            "type": "string"
          },
          "url": {
            "type": "string",
            "pattern": "^/(?:ru/|en/)?docs/[^/]+$",
            "description": "Lien relatif vers un article de connaissance publié."
          }
        },
        "required": [
          "id",
          "title",
          "url"
        ],
        "additionalProperties": false
      },
      "SearchSupportAnswer": {
        "type": "object",
        "properties": {
          "mode": {
            "type": "string",
            "const": "search"
          },
          "answer": {
            "type": "string"
          },
          "sources": {
            "type": "array",
            "maxItems": 3,
            "items": {
              "$ref": "#/components/schemas/SupportSource"
            }
          },
          "knowledgeVersion": {
            "type": [
              "string",
              "null"
            ]
          },
          "needsHuman": {
            "type": "boolean"
          },
          "aiUnavailable": {
            "type": "boolean",
            "const": true
          }
        },
        "required": [
          "mode",
          "answer",
          "sources",
          "knowledgeVersion",
          "needsHuman"
        ],
        "additionalProperties": false
      },
      "AISupportAnswer": {
        "type": "object",
        "properties": {
          "mode": {
            "type": "string",
            "const": "ai"
          },
          "answer": {
            "type": "string",
            "minLength": 1,
            "maxLength": 6000,
            "description": "Texte brut uniquement ; les URLs et HTML sont rejetés. Les liens sont renvoyés uniquement dans les sources."
          },
          "sources": {
            "type": "array",
            "maxItems": 25,
            "uniqueItems": true,
            "items": {
              "$ref": "#/components/schemas/SupportSource"
            }
          },
          "knowledgeVersion": {
            "type": "string"
          },
          "needsHuman": {
            "type": "boolean"
          }
        },
        "required": [
          "mode",
          "answer",
          "sources",
          "knowledgeVersion",
          "needsHuman"
        ],
        "additionalProperties": false,
        "allOf": [
          {
            "if": {
              "properties": {
                "needsHuman": {
                  "const": false
                }
              }
            },
            "then": {
              "properties": {
                "sources": {
                  "minItems": 1
                }
              }
            }
          }
        ]
      },
      "SupportAnswer": {
        "oneOf": [
          {
            "$ref": "#/components/schemas/SearchSupportAnswer"
          },
          {
            "$ref": "#/components/schemas/AISupportAnswer"
          }
        ]
      },
      "Ticket": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "pattern": "^tkt_[a-f0-9-]{36}$"
          },
          "subject": {
            "type": "string"
          },
          "message": {
            "type": "string"
          },
          "status": {
            "type": "string",
            "enum": [
              "open",
              "answered",
              "closed"
            ],
            "description": "answered désigne une demande répondue. L'API publique permet à un utilisateur de fermer uniquement sa propre demande."
          },
          "source": {
            "type": "string",
            "enum": [
              "web",
              "telegram"
            ]
          },
          "createdAt": {
            "type": "integer",
            "minimum": 0,
            "description": "Horodatage Unix en millisecondes, UTC."
          },
          "updatedAt": {
            "type": "integer",
            "minimum": 0,
            "description": "Horodatage Unix en millisecondes, UTC."
          }
        },
        "required": [
          "id",
          "subject",
          "message",
          "status",
          "source",
          "createdAt",
          "updatedAt"
        ],
        "additionalProperties": false
      },
      "TicketResponse": {
        "type": "object",
        "properties": {
          "ticket": {
            "$ref": "#/components/schemas/Ticket"
          }
        },
        "required": [
          "ticket"
        ],
        "additionalProperties": false
      },
      "Notification": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "pattern": "^ntf_[A-Za-z0-9-]{1,80}$"
          },
          "kind": {
            "type": "string",
            "enum": [
              "payments",
              "payouts",
              "support",
              "security",
              "test"
            ]
          },
          "message": {
            "type": "string"
          },
          "status": {
            "type": "string",
            "enum": [
              "pending",
              "sending",
              "sent",
              "failed"
            ],
            "description": "État de livraison Telegram, indépendant de l'état de lecture sur le site web."
          },
          "createdAt": {
            "type": "integer",
            "minimum": 0,
            "description": "Horodatage Unix en millisecondes, UTC."
          },
          "sentAt": {
            "type": [
              "integer",
              "null"
            ],
            "minimum": 0,
            "description": "Horodatage Unix en millisecondes, UTC."
          },
          "readAt": {
            "type": [
              "integer",
              "null"
            ],
            "minimum": 0,
            "description": "Première lecture sur le site web ; null signifie non lu. Cela ne confirme pas qu'il a été lu dans Telegram."
          }
        },
        "required": [
          "id",
          "kind",
          "message",
          "status",
          "createdAt",
          "sentAt",
          "readAt"
        ],
        "additionalProperties": false
      },
      "NotificationsPage": {
        "type": "object",
        "properties": {
          "notifications": {
            "type": "array",
            "maxItems": 30,
            "items": {
              "$ref": "#/components/schemas/Notification"
            }
          },
          "unreadCount": {
            "type": "integer",
            "minimum": 0
          },
          "nextCursor": {
            "type": [
              "string",
              "null"
            ],
            "description": "Curseur de page suivante ; null si aucun autre enregistrement ne subsiste."
          }
        },
        "required": [
          "notifications",
          "unreadCount",
          "nextCursor"
        ],
        "additionalProperties": false
      },
      "ReadNotifications": {
        "oneOf": [
          {
            "type": "object",
            "properties": {
              "ids": {
                "type": "array",
                "minItems": 1,
                "maxItems": 100,
                "description": "Les doublons sont autorisés et comptés une fois.",
                "items": {
                  "type": "string",
                  "pattern": "^ntf_[A-Za-z0-9-]{1,80}$"
                }
              }
            },
            "required": [
              "ids"
            ],
            "additionalProperties": false
          },
          {
            "type": "object",
            "properties": {
              "all": {
                "type": "boolean",
                "const": true
              },
              "before": {
                "type": "integer",
                "minimum": 0,
                "description": "createdAt du dernier événement affiché. Le serveur exige également avant <= Date.now().",
                "maximum": 9007199254740991
              }
            },
            "required": [
              "all",
              "before"
            ],
            "additionalProperties": false
          }
        ]
      },
      "ReadNotificationsResult": {
        "type": "object",
        "properties": {
          "updated": {
            "type": "integer",
            "minimum": 0
          },
          "readAt": {
            "type": "integer",
            "minimum": 0,
            "description": "Horodatage Unix en millisecondes, UTC."
          },
          "unreadCount": {
            "type": "integer",
            "minimum": 0
          }
        },
        "required": [
          "updated",
          "readAt",
          "unreadCount"
        ],
        "additionalProperties": false
      },
      "TelegramUpdate": {
        "type": "object",
        "properties": {
          "update_id": {
            "type": "integer",
            "minimum": 0,
            "maximum": 9007199254740991
          },
          "message": {
            "type": "object",
            "additionalProperties": true,
            "description": "Message Telegram : le gestionnaire utilise message_id, text, from et chat."
          },
          "callback_query": {
            "type": "object",
            "additionalProperties": true,
            "description": "Télégramme CallbackQuery : le gestionnaire utilise id, from, data et message.chat."
          }
        },
        "required": [
          "update_id"
        ],
        "additionalProperties": true
      },
      "IncomingQuote": {
        "type": "object",
        "properties": {
          "amountMinor": {
            "type": "integer",
            "minimum": 0,
            "maximum": 9007199254740991,
            "description": "Montant entier en unités monétaires mineures."
          },
          "baseMinor": {
            "type": "integer",
            "minimum": 0,
            "maximum": 9007199254740991,
            "description": "Montant entier en unités monétaires mineures."
          },
          "grossMinor": {
            "type": "integer",
            "minimum": 0,
            "maximum": 9007199254740991,
            "description": "Montant entier en unités monétaires mineures."
          },
          "percentageFeeMinor": {
            "type": "integer",
            "minimum": 0,
            "maximum": 9007199254740991,
            "description": "Montant entier en unités monétaires mineures."
          },
          "fixedFeeMinor": {
            "type": "integer",
            "minimum": 0,
            "maximum": 9007199254740991,
            "description": "Montant entier en unités monétaires mineures."
          },
          "nominalFeeMinor": {
            "type": "integer",
            "minimum": 0,
            "maximum": 9007199254740991,
            "description": "Montant entier en unités monétaires mineures."
          },
          "feeMinor": {
            "type": "integer",
            "minimum": 0,
            "maximum": 9007199254740991,
            "description": "Montant entier en unités monétaires mineures."
          },
          "vatMinor": {
            "type": "integer",
            "minimum": 0,
            "maximum": 9007199254740991,
            "description": "Montant entier en unités monétaires mineures."
          },
          "customerVatMinor": {
            "type": "integer",
            "minimum": 0,
            "maximum": 9007199254740991,
            "description": "Montant entier en unités monétaires mineures."
          },
          "merchantVatMinor": {
            "type": "integer",
            "minimum": 0,
            "maximum": 9007199254740991,
            "description": "Montant entier en unités monétaires mineures."
          },
          "customerTotalMinor": {
            "type": "integer",
            "minimum": 0,
            "maximum": 9007199254740991,
            "description": "Montant entier en unités monétaires mineures."
          },
          "merchantDeductionMinor": {
            "type": "integer",
            "minimum": 0,
            "maximum": 9007199254740991,
            "description": "Montant entier en unités monétaires mineures."
          },
          "totalFeeMinor": {
            "type": "integer",
            "minimum": 0,
            "maximum": 9007199254740991,
            "description": "Montant entier en unités monétaires mineures."
          },
          "netMinor": {
            "type": "integer",
            "minimum": 0,
            "maximum": 9007199254740991,
            "description": "Montant entier en unités monétaires mineures."
          },
          "currency": {
            "type": "string",
            "enum": [
              "USD",
              "RUB",
              "USDT"
            ],
            "description": "Unités de facture : cents USD, kopecks RUB ou centièmes d'USDT. Aucune conversion automatique de devise."
          },
          "method": {
            "type": "string",
            "enum": [
              "card",
              "alipay",
              "sbp",
              "crypto"
            ]
          },
          "vatPayer": {
            "type": "string",
            "enum": [
              "customer",
              "merchant",
              "shared"
            ]
          },
          "vatApplies": {
            "type": "boolean"
          },
          "pricingVersion": {
            "type": "integer",
            "minimum": 1,
            "description": "Version de calcul enregistrée avec la facture."
          },
          "vatCustomerPercent": {
            "type": "integer",
            "minimum": 0,
            "maximum": 100,
            "description": "Part du client, en pourcentage entier. 0 signifie que le marchand couvre tout ; 100 signifie que le client couvre tout. Par défaut : la préférence TVA héritée du magasin."
          },
          "feeCustomerPercent": {
            "type": "integer",
            "minimum": 0,
            "maximum": 100,
            "description": "Part du client, en pourcentage entier. 0 signifie que le marchand couvre tout ; 100 signifie que le client couvre tout.",
            "default": 0
          },
          "customerFeeMinor": {
            "type": "integer",
            "minimum": 0,
            "maximum": 9007199254740991,
            "description": "Montant entier en unités monétaires mineures."
          },
          "merchantFeeMinor": {
            "type": "integer",
            "minimum": 0,
            "maximum": 9007199254740991,
            "description": "Montant entier en unités monétaires mineures."
          },
          "settlement": {
            "$ref": "#/components/schemas/SettlementQuote"
          }
        },
        "required": [
          "amountMinor",
          "baseMinor",
          "grossMinor",
          "percentageFeeMinor",
          "fixedFeeMinor",
          "nominalFeeMinor",
          "feeMinor",
          "vatMinor",
          "customerVatMinor",
          "merchantVatMinor",
          "customerTotalMinor",
          "merchantDeductionMinor",
          "totalFeeMinor",
          "netMinor",
          "currency",
          "method",
          "vatPayer",
          "vatApplies",
          "pricingVersion"
        ],
        "additionalProperties": false
      },
      "PaymentLink": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "pattern": "^lnk_[0-9a-f-]{36}$"
          },
          "title": {
            "type": "string",
            "minLength": 1,
            "maxLength": 120
          },
          "description": {
            "type": "string",
            "maxLength": 500
          },
          "merchantName": {
            "type": "string"
          },
          "amountMinor": {
            "type": "integer",
            "minimum": 0,
            "maximum": 9007199254740991,
            "description": "Montant entier en unités monétaires mineures."
          },
          "currency": {
            "type": "string",
            "enum": [
              "USD",
              "RUB",
              "USDT"
            ],
            "description": "Unités de facture : cents USD, kopecks RUB ou centièmes d'USDT. Aucune conversion automatique de devise."
          },
          "vatPayer": {
            "type": "string",
            "enum": [
              "customer",
              "merchant",
              "shared"
            ]
          },
          "methods": {
            "type": "array",
            "minItems": 1,
            "maxItems": 3,
            "uniqueItems": true,
            "items": {
              "type": "string",
              "enum": [
                "card",
                "alipay",
                "sbp",
                "crypto"
              ]
            }
          },
          "quotes": {
            "type": "object",
            "properties": {
              "card": {
                "$ref": "#/components/schemas/IncomingQuote"
              },
              "alipay": {
                "$ref": "#/components/schemas/IncomingQuote"
              },
              "sbp": {
                "$ref": "#/components/schemas/IncomingQuote"
              },
              "crypto": {
                "$ref": "#/components/schemas/IncomingQuote"
              }
            },
            "required": [],
            "additionalProperties": false
          },
          "status": {
            "type": "string",
            "enum": [
              "active",
              "paused",
              "expired",
              "cancelled"
            ],
            "description": "expired est dérivé de expiresAt à la lecture. cancelled est définitif."
          },
          "createdAt": {
            "type": "integer",
            "minimum": 0,
            "description": "Horodatage Unix en millisecondes, UTC."
          },
          "updatedAt": {
            "type": "integer",
            "minimum": 0,
            "description": "Horodatage Unix en millisecondes, UTC."
          },
          "expiresAt": {
            "type": "integer",
            "minimum": 0,
            "description": "Horodatage Unix en millisecondes, UTC."
          },
          "url": {
            "type": "string",
            "format": "uri",
            "description": "Page publique /pay/{token} sur APP_ORIGIN."
          },
          "vatCustomerPercent": {
            "type": "integer",
            "minimum": 0,
            "maximum": 100,
            "description": "Part du client, en pourcentage entier. 0 signifie que le marchand couvre tout ; 100 signifie que le client couvre tout. Par défaut : la préférence TVA héritée du magasin."
          },
          "feeCustomerPercent": {
            "type": "integer",
            "minimum": 0,
            "maximum": 100,
            "description": "Part du client, en pourcentage entier. 0 signifie que le marchand couvre tout ; 100 signifie que le client couvre tout.",
            "default": 0
          },
          "orderId": {
            "type": "string",
            "maxLength": 100,
            "description": "Référence de commande visible par l'acheteur."
          },
          "metadata": {
            "type": "object",
            "maxProperties": 10,
            "propertyNames": {
              "pattern": "^[A-Za-z0-9_-]{1,40}$"
            },
            "additionalProperties": {
              "type": "string",
              "maxLength": 500
            },
            "description": "Métadonnées privées du marchand. Au plus 3000 octets UTF-8 après sérialisation JSON. Jamais exposées dans le paiement public."
          },
          "checkoutUrl": {
            "type": "string",
            "format": "uri"
          },
          "payment": {
            "oneOf": [
              {
                "$ref": "#/components/schemas/PaymentSummary"
              },
              {
                "type": "null"
              }
            ],
            "description": "Inclus par les requêtes GET de liste/détail du propriétaire. Le cycle de vie de la facture est indépendant du statut de paiement vérifié. Ne contient ni carte, ni code bancaire, ni QR, ni détails de l'acheteur."
          },
          "storeId": {
            "type": "string",
            "pattern": "^sto_[A-Za-z0-9_-]+$"
          },
          "pricingMode": {
            "type": "string",
            "enum": [
              "native",
              "universal"
            ]
          },
          "fx": {
            "oneOf": [
              {
                "$ref": "#/components/schemas/ExchangeRate"
              },
              {
                "type": "null"
              }
            ]
          },
          "usdtPerUsd": {
            "type": [
              "string",
              "null"
            ]
          }
        },
        "required": [
          "id",
          "title",
          "description",
          "merchantName",
          "amountMinor",
          "currency",
          "vatPayer",
          "methods",
          "quotes",
          "status",
          "createdAt",
          "updatedAt",
          "expiresAt",
          "url",
          "vatCustomerPercent",
          "feeCustomerPercent",
          "orderId",
          "metadata",
          "checkoutUrl"
        ],
        "additionalProperties": false
      },
      "CreatePaymentLink": {
        "oneOf": [
          {
            "type": "object",
            "properties": {
              "title": {
                "type": "string",
                "minLength": 1,
                "x-trimmedMaxLength": 120,
                "x-trimmedMinLength": 1,
                "description": "Les espaces environnants sont supprimés. Longueur après suppression : 1 à 120 caractères. Les caractères de contrôle, à l'exception des tabulations et des sauts de ligne, sont interdits."
              },
              "description": {
                "type": "string",
                "x-trimmedMaxLength": 500,
                "description": "Facultatif ; une chaîne vide est autorisée. Jusqu'à 500 caractères après suppression des espaces."
              },
              "amountMinor": {
                "type": "integer",
                "minimum": 1,
                "maximum": 10000000000,
                "description": "Limite entière technique, pas une limite commerciale du fournisseur. Un crédit marchand négatif est rejeté."
              },
              "vatPayer": {
                "type": "string",
                "enum": [
                  "customer",
                  "merchant"
                ],
                "description": "Lorsqu'il est omis, la règle actuelle du magasin est enregistrée."
              },
              "expiresAt": {
                "type": "integer",
                "minimum": 0,
                "description": "Un moment futur, au plus 90 jours après la création. Par défaut sept jours. Enregistré dans l'instantané de la facture."
              },
              "currency": {
                "type": "string",
                "const": "USD"
              },
              "methods": {
                "type": "array",
                "minItems": 1,
                "maxItems": 3,
                "uniqueItems": true,
                "items": {
                  "type": "string",
                  "enum": [
                    "card",
                    "alipay",
                    "crypto"
                  ]
                },
                "description": "Le nouveau paiement USD prend en charge la carte et Alipay. Les factures crypto USD sont acceptées uniquement pour compatibilité et renvoient crypto_requires_usdt au paiement en direct ; utilisez USDT pour la crypto."
              },
              "vatCustomerPercent": {
                "type": "integer",
                "minimum": 0,
                "maximum": 100,
                "description": "Part du client, en pourcentage entier. 0 signifie que le marchand couvre tout ; 100 signifie que le client couvre tout. Par défaut : la préférence TVA héritée du magasin."
              },
              "feeCustomerPercent": {
                "type": "integer",
                "minimum": 0,
                "maximum": 100,
                "description": "Part du client, en pourcentage entier. 0 signifie que le marchand couvre tout ; 100 signifie que le client couvre tout.",
                "default": 0
              },
              "orderId": {
                "type": "string",
                "maxLength": 100,
                "description": "Référence de commande visible par l'acheteur."
              },
              "metadata": {
                "type": "object",
                "maxProperties": 10,
                "propertyNames": {
                  "pattern": "^[A-Za-z0-9_-]{1,40}$"
                },
                "additionalProperties": {
                  "type": "string",
                  "maxLength": 500
                },
                "description": "Métadonnées privées du marchand. Au plus 3000 octets UTF-8 après sérialisation JSON. Jamais exposées dans le paiement public."
              },
              "pricingMode": {
                "type": "string",
                "const": "native",
                "description": "Tarification héritée spécifique à la monnaie. Omettre pour la compatibilité avec les intégrations existantes."
              }
            },
            "required": [
              "title",
              "amountMinor",
              "currency",
              "methods"
            ],
            "additionalProperties": false
          },
          {
            "type": "object",
            "properties": {
              "title": {
                "type": "string",
                "minLength": 1,
                "x-trimmedMaxLength": 120,
                "x-trimmedMinLength": 1,
                "description": "Les espaces environnants sont supprimés. Longueur après suppression : 1 à 120 caractères. Les caractères de contrôle, à l'exception des tabulations et des sauts de ligne, sont interdits."
              },
              "description": {
                "type": "string",
                "x-trimmedMaxLength": 500,
                "description": "Facultatif ; une chaîne vide est autorisée. Jusqu'à 500 caractères après suppression des espaces."
              },
              "amountMinor": {
                "type": "integer",
                "minimum": 1,
                "maximum": 10000000000,
                "description": "Limite entière technique, pas une limite commerciale du fournisseur. Un crédit marchand négatif est rejeté."
              },
              "vatPayer": {
                "type": "string",
                "enum": [
                  "customer",
                  "merchant"
                ],
                "description": "Lorsqu'il est omis, la règle actuelle du magasin est enregistrée."
              },
              "expiresAt": {
                "type": "integer",
                "minimum": 0,
                "description": "Un moment futur, au plus 90 jours après la création. Par défaut sept jours. Enregistré dans l'instantané de la facture."
              },
              "currency": {
                "type": "string",
                "const": "RUB"
              },
              "methods": {
                "type": "array",
                "minItems": 1,
                "maxItems": 1,
                "uniqueItems": true,
                "items": {
                  "type": "string",
                  "const": "sbp"
                }
              },
              "vatCustomerPercent": {
                "type": "integer",
                "minimum": 0,
                "maximum": 100,
                "description": "Part du client, en pourcentage entier. 0 signifie que le marchand couvre tout ; 100 signifie que le client couvre tout. Par défaut : la préférence TVA héritée du magasin."
              },
              "feeCustomerPercent": {
                "type": "integer",
                "minimum": 0,
                "maximum": 100,
                "description": "Part du client, en pourcentage entier. 0 signifie que le marchand couvre tout ; 100 signifie que le client couvre tout.",
                "default": 0
              },
              "orderId": {
                "type": "string",
                "maxLength": 100,
                "description": "Référence de commande visible par l'acheteur."
              },
              "metadata": {
                "type": "object",
                "maxProperties": 10,
                "propertyNames": {
                  "pattern": "^[A-Za-z0-9_-]{1,40}$"
                },
                "additionalProperties": {
                  "type": "string",
                  "maxLength": 500
                },
                "description": "Métadonnées privées du marchand. Au plus 3000 octets UTF-8 après sérialisation JSON. Jamais exposées dans le paiement public."
              },
              "pricingMode": {
                "type": "string",
                "const": "native",
                "description": "Tarification héritée spécifique à la monnaie. Omettre pour la compatibilité avec les intégrations existantes."
              }
            },
            "required": [
              "title",
              "amountMinor",
              "currency",
              "methods"
            ],
            "additionalProperties": false
          },
          {
            "type": "object",
            "properties": {
              "title": {
                "type": "string",
                "minLength": 1,
                "x-trimmedMaxLength": 120,
                "x-trimmedMinLength": 1,
                "description": "Les espaces environnants sont supprimés. Longueur après suppression : 1 à 120 caractères. Les caractères de contrôle, à l'exception des tabulations et des sauts de ligne, sont interdits."
              },
              "description": {
                "type": "string",
                "x-trimmedMaxLength": 500,
                "description": "Facultatif ; une chaîne vide est autorisée. Jusqu'à 500 caractères après suppression des espaces."
              },
              "amountMinor": {
                "type": "integer",
                "minimum": 1,
                "maximum": 10000000000,
                "description": "Limite entière technique, pas une limite commerciale du fournisseur. Un crédit marchand négatif est rejeté."
              },
              "vatPayer": {
                "type": "string",
                "enum": [
                  "customer",
                  "merchant"
                ],
                "description": "Lorsqu'il est omis, la règle actuelle du magasin est enregistrée."
              },
              "expiresAt": {
                "type": "integer",
                "minimum": 0,
                "description": "Un moment futur, au plus 90 jours après la création. Par défaut sept jours. Enregistré dans l'instantané de la facture."
              },
              "currency": {
                "type": "string",
                "const": "USDT"
              },
              "methods": {
                "type": "array",
                "minItems": 1,
                "maxItems": 1,
                "uniqueItems": true,
                "items": {
                  "type": "string",
                  "const": "crypto"
                }
              },
              "vatCustomerPercent": {
                "type": "integer",
                "minimum": 0,
                "maximum": 100,
                "description": "Part du client, en pourcentage entier. 0 signifie que le marchand couvre tout ; 100 signifie que le client couvre tout. Par défaut : la préférence TVA héritée du magasin."
              },
              "feeCustomerPercent": {
                "type": "integer",
                "minimum": 0,
                "maximum": 100,
                "description": "Part du client, en pourcentage entier. 0 signifie que le marchand couvre tout ; 100 signifie que le client couvre tout.",
                "default": 0
              },
              "orderId": {
                "type": "string",
                "maxLength": 100,
                "description": "Référence de commande visible par l'acheteur."
              },
              "metadata": {
                "type": "object",
                "maxProperties": 10,
                "propertyNames": {
                  "pattern": "^[A-Za-z0-9_-]{1,40}$"
                },
                "additionalProperties": {
                  "type": "string",
                  "maxLength": 500
                },
                "description": "Métadonnées privées du marchand. Au plus 3000 octets UTF-8 après sérialisation JSON. Jamais exposées dans le paiement public."
              },
              "pricingMode": {
                "type": "string",
                "const": "native",
                "description": "Tarification héritée spécifique à la monnaie. Omettre pour la compatibilité avec les intégrations existantes."
              }
            },
            "required": [
              "title",
              "amountMinor",
              "currency",
              "methods"
            ],
            "additionalProperties": false
          },
          {
            "type": "object",
            "properties": {
              "title": {
                "type": "string",
                "minLength": 1,
                "x-trimmedMaxLength": 120,
                "x-trimmedMinLength": 1,
                "description": "Les espaces environnants sont supprimés. Longueur après suppression : 1 à 120 caractères. Les caractères de contrôle, à l'exception des tabulations et des sauts de ligne, sont interdits."
              },
              "description": {
                "type": "string",
                "x-trimmedMaxLength": 500,
                "description": "Facultatif ; une chaîne vide est autorisée. Jusqu'à 500 caractères après suppression des espaces."
              },
              "amountMinor": {
                "type": "integer",
                "minimum": 1,
                "maximum": 10000000000,
                "description": "Limite entière technique, pas une limite commerciale du fournisseur. Un crédit marchand négatif est rejeté."
              },
              "vatPayer": {
                "type": "string",
                "enum": [
                  "customer",
                  "merchant"
                ],
                "description": "Lorsqu'il est omis, la règle actuelle du magasin est enregistrée."
              },
              "expiresAt": {
                "type": "integer",
                "minimum": 0,
                "description": "Un moment futur, au plus 90 jours après la création. Par défaut sept jours. Enregistré dans l'instantané de la facture."
              },
              "currency": {
                "type": "string",
                "enum": [
                  "USD",
                  "RUB",
                  "USDT"
                ],
                "description": "Unité d'entrée. RUB est converti en USD au taux de référence enregistré ; USDT utilise la règle de facturation explicite 1:1. La monnaie de base de facture enregistrée est USD."
              },
              "methods": {
                "type": "array",
                "minItems": 1,
                "maxItems": 4,
                "uniqueItems": true,
                "items": {
                  "type": "string",
                  "enum": [
                    "card",
                    "alipay",
                    "sbp",
                    "crypto"
                  ]
                }
              },
              "vatCustomerPercent": {
                "type": "integer",
                "minimum": 0,
                "maximum": 100,
                "description": "Part du client, en pourcentage entier. 0 signifie que le marchand couvre tout ; 100 signifie que le client couvre tout. Par défaut : la préférence TVA héritée du magasin."
              },
              "feeCustomerPercent": {
                "type": "integer",
                "minimum": 0,
                "maximum": 100,
                "description": "Part du client, en pourcentage entier. 0 signifie que le marchand couvre tout ; 100 signifie que le client couvre tout.",
                "default": 0
              },
              "orderId": {
                "type": "string",
                "maxLength": 100,
                "description": "Référence de commande visible par l'acheteur."
              },
              "metadata": {
                "type": "object",
                "maxProperties": 10,
                "propertyNames": {
                  "pattern": "^[A-Za-z0-9_-]{1,40}$"
                },
                "additionalProperties": {
                  "type": "string",
                  "maxLength": 500
                },
                "description": "Métadonnées privées du marchand. Au plus 3000 octets UTF-8 après sérialisation JSON. Jamais exposées dans le paiement public."
              },
              "pricingMode": {
                "type": "string",
                "const": "universal"
              }
            },
            "required": [
              "title",
              "amountMinor",
              "currency",
              "methods",
              "pricingMode"
            ],
            "additionalProperties": false
          }
        ],
        "description": "Les montants utilisent des unités mineures. Les frais et la TVA sont calculés sur le montant de commande de base. La part du client est arrondie au demi supérieur ; le commerçant couvre le reste exact. Une vatPayer et vatCustomerPercent hérités contradictoires sont rejetés. L'allocation est immuable après la création ; émettez une nouvelle facture pour la modifier. La carte/Alipay en direct nécessite vatCustomerPercent=100 et feeCustomerPercent=0 car la passerelle configurée ne peut pas remplacer sa taxe exclusive. Les autres allocations enregistrées renvoient unsupported_allocation ; elles ne sont jamais modifiées silencieusement. SBP et USDT crypto prennent en charge l'allocation des frais. Utilisez l'objet de disponibilité de facture public avant de collecter les détails de paiement. Le mode universel stocke un prix de base USD et des cotations de frais natives séparées : USD pour les cartes/Alipay, RUB pour SBP, USDT pour la crypto. La conversion RUB utilise un taux de référence enregistré de la Banque de Russie. La crypto utilise la règle de facturation explicite 1 USD = 1 USDT, pas une cotation de marché. Les factures impliquant une conversion RUB sont plafonnées à 24 heures même lorsque expiresAt demande une date ultérieure. Une défaillance de source FX renvoie 503 FX_UNAVAILABLE. Omettre pricingMode pour le comportement natif hérité.",
        "examples": [
          {
            "pricingMode": "universal",
            "title": "Commande 1042",
            "amountMinor": 1000,
            "currency": "USD",
            "methods": [
              "card",
              "alipay",
              "sbp",
              "crypto"
            ],
            "vatCustomerPercent": 100,
            "feeCustomerPercent": 0
          },
          {
            "title": "Commande 1042",
            "amountMinor": 500,
            "currency": "USD",
            "methods": [
              "card",
              "alipay"
            ],
            "vatCustomerPercent": 100,
            "feeCustomerPercent": 0,
            "orderId": "1042"
          },
          {
            "title": "Commande 1043",
            "amountMinor": 1000,
            "currency": "USDT",
            "methods": [
              "crypto"
            ],
            "feeCustomerPercent": 23,
            "orderId": "1043"
          }
        ]
      },
      "PaymentLinkResponse": {
        "type": "object",
        "properties": {
          "link": {
            "$ref": "#/components/schemas/PaymentLink"
          },
          "checkoutUrl": {
            "type": "string",
            "format": "uri",
            "description": "Inclus dans les réponses de mise à jour de statut ; le lien imbriqué contient toujours son checkoutUrl."
          }
        },
        "required": [
          "link"
        ],
        "additionalProperties": false
      },
      "CreatePaymentLinkResponse": {
        "type": "object",
        "properties": {
          "link": {
            "$ref": "#/components/schemas/PaymentLink"
          },
          "replayed": {
            "type": "boolean"
          },
          "checkoutUrl": {
            "type": "string",
            "format": "uri",
            "description": "Le lien de paiement est disponible immédiatement ; aucune interrogation périodique n'est requise."
          }
        },
        "required": [
          "link",
          "replayed",
          "checkoutUrl"
        ],
        "additionalProperties": false
      },
      "PaymentLinksPage": {
        "type": "object",
        "properties": {
          "links": {
            "type": "array",
            "maxItems": 100,
            "items": {
              "$ref": "#/components/schemas/PaymentLink"
            }
          },
          "nextCursor": {
            "type": [
              "string",
              "null"
            ],
            "description": "Curseur opaque de page suivante. Passez-le comme paramètre de requête encodé URL ; null marque la fin."
          }
        },
        "required": [
          "links",
          "nextCursor"
        ],
        "additionalProperties": false
      },
      "CheckoutQuote": {
        "type": "object",
        "properties": {
          "method": {
            "type": "string",
            "enum": [
              "card",
              "alipay",
              "sbp",
              "crypto"
            ]
          },
          "amountMinor": {
            "type": "integer",
            "minimum": 0,
            "maximum": 9007199254740991,
            "description": "Montant entier en unités monétaires mineures."
          },
          "vatMinor": {
            "type": "integer",
            "minimum": 0,
            "maximum": 9007199254740991,
            "description": "Montant entier en unités monétaires mineures."
          },
          "customerVatMinor": {
            "type": "integer",
            "minimum": 0,
            "maximum": 9007199254740991,
            "description": "Montant entier en unités monétaires mineures."
          },
          "customerTotalMinor": {
            "type": "integer",
            "minimum": 0,
            "maximum": 9007199254740991,
            "description": "Montant entier en unités monétaires mineures."
          },
          "vatApplies": {
            "type": "boolean"
          },
          "pricingVersion": {
            "type": "integer",
            "minimum": 1,
            "description": "Version de calcul enregistrée avec la facture."
          },
          "customerFeeMinor": {
            "type": "integer",
            "minimum": 0,
            "maximum": 9007199254740991,
            "description": "Montant entier en unités monétaires mineures."
          },
          "merchantCoveredMinor": {
            "type": "integer",
            "minimum": 0,
            "maximum": 9007199254740991,
            "description": "Montant entier en unités monétaires mineures."
          }
        },
        "required": [
          "method",
          "amountMinor",
          "vatMinor",
          "customerVatMinor",
          "customerTotalMinor",
          "vatApplies",
          "pricingVersion",
          "customerFeeMinor",
          "merchantCoveredMinor"
        ],
        "additionalProperties": false
      },
      "CheckoutInvoice": {
        "type": "object",
        "properties": {
          "token": {
            "type": "string",
            "pattern": "^[A-Za-z0-9_-]{43}$"
          },
          "title": {
            "type": "string"
          },
          "description": {
            "type": "string"
          },
          "merchantName": {
            "type": "string"
          },
          "amountMinor": {
            "type": "integer",
            "minimum": 0,
            "maximum": 9007199254740991,
            "description": "Montant entier en unités monétaires mineures."
          },
          "currency": {
            "type": "string",
            "enum": [
              "USD",
              "RUB",
              "USDT"
            ],
            "description": "Unités de facture : cents USD, kopecks RUB ou centièmes d'USDT. Aucune conversion automatique de devise."
          },
          "vatPayer": {
            "type": "string",
            "enum": [
              "customer",
              "merchant",
              "shared"
            ]
          },
          "methods": {
            "type": "array",
            "minItems": 1,
            "maxItems": 3,
            "items": {
              "type": "string",
              "enum": [
                "card",
                "alipay",
                "sbp",
                "crypto"
              ]
            }
          },
          "status": {
            "type": "string",
            "enum": [
              "active",
              "paused",
              "expired",
              "cancelled"
            ],
            "description": "expired est dérivé de expiresAt à la lecture. cancelled est définitif."
          },
          "expiresAt": {
            "type": "integer",
            "minimum": 0,
            "description": "Horodatage Unix en millisecondes, UTC."
          },
          "createdAt": {
            "type": "integer",
            "minimum": 0,
            "description": "Horodatage Unix en millisecondes, UTC."
          },
          "quote": {
            "$ref": "#/components/schemas/CheckoutQuote"
          },
          "gatewayConnected": {
            "type": "boolean",
            "description": "Alias de compatibilité pour availability.available, pas un test de santé de passerelle."
          },
          "vatCustomerPercent": {
            "type": "integer",
            "minimum": 0,
            "maximum": 100,
            "description": "Part du client, en pourcentage entier. 0 signifie que le marchand couvre tout ; 100 signifie que le client couvre tout. Par défaut : la préférence TVA héritée du magasin."
          },
          "feeCustomerPercent": {
            "type": "integer",
            "minimum": 0,
            "maximum": 100,
            "description": "Part du client, en pourcentage entier. 0 signifie que le marchand couvre tout ; 100 signifie que le client couvre tout.",
            "default": 0
          },
          "orderId": {
            "type": "string",
            "maxLength": 100,
            "description": "Référence de commande visible par l'acheteur."
          },
          "availability": {
            "$ref": "#/components/schemas/CheckoutAvailability"
          },
          "baseAmountMinor": {
            "type": "integer",
            "minimum": 0,
            "maximum": 9007199254740991,
            "description": "Montant entier en unités monétaires mineures."
          },
          "baseCurrency": {
            "type": "string",
            "enum": [
              "USD",
              "RUB",
              "USDT"
            ]
          },
          "fx": {
            "oneOf": [
              {
                "$ref": "#/components/schemas/ExchangeRate"
              },
              {
                "type": "null"
              }
            ]
          },
          "usdtPerUsd": {
            "type": [
              "string",
              "null"
            ]
          }
        },
        "required": [
          "token",
          "title",
          "description",
          "merchantName",
          "amountMinor",
          "currency",
          "vatPayer",
          "methods",
          "status",
          "expiresAt",
          "createdAt",
          "quote",
          "gatewayConnected",
          "vatCustomerPercent",
          "feeCustomerPercent",
          "orderId",
          "availability"
        ],
        "additionalProperties": false
      },
      "MerchantKey": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "pattern": "^key_[0-9a-f-]{36}$"
          },
          "name": {
            "type": "string"
          },
          "prefix": {
            "type": "string"
          },
          "scopes": {
            "type": "array",
            "minItems": 1,
            "maxItems": 3,
            "uniqueItems": true,
            "items": {
              "type": "string",
              "enum": [
                "invoices:read",
                "invoices:write",
                "events:read"
              ]
            }
          },
          "createdAt": {
            "type": "integer",
            "minimum": 0,
            "description": "Horodatage Unix en millisecondes, UTC."
          },
          "expiresAt": {
            "type": "integer",
            "minimum": 0,
            "description": "Horodatage Unix en millisecondes, UTC."
          },
          "revokedAt": {
            "type": [
              "integer",
              "null"
            ],
            "minimum": 0,
            "description": "Horodatage Unix en millisecondes, UTC."
          },
          "lastUsedAt": {
            "type": [
              "integer",
              "null"
            ],
            "minimum": 0,
            "description": "Horodatage Unix en millisecondes, UTC."
          },
          "storeId": {
            "type": "string",
            "pattern": "^sto_[A-Za-z0-9_-]+$"
          }
        },
        "required": [
          "id",
          "name",
          "prefix",
          "scopes",
          "createdAt",
          "expiresAt"
        ],
        "additionalProperties": false
      },
      "MerchantEvent": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "pattern": "^evt_[a-f0-9]{40}$"
          },
          "type": {
            "type": "string",
            "enum": [
              "invoice.created",
              "invoice.updated",
              "payment.succeeded",
              "payment.failed",
              "payment.refunded",
              "payment.partially_refunded",
              "payment.disputed",
              "payment.review_required"
            ]
          },
          "apiVersion": {
            "type": "string",
            "const": "2026-10-21"
          },
          "createdAt": {
            "type": "integer",
            "minimum": 0,
            "description": "Horodatage Unix en millisecondes, UTC."
          },
          "livemode": {
            "type": "boolean"
          },
          "data": {
            "type": "object",
            "additionalProperties": true,
            "description": "invoice.created/updated : facture marchande persistée, y compris les métadonnées privées. Événements de paiement : faits de paiement vérifiés. Voir le guide de webhook pour des exemples."
          }
        },
        "required": [
          "id",
          "type",
          "apiVersion",
          "createdAt",
          "livemode",
          "data"
        ],
        "additionalProperties": false
      },
      "WebhookEndpoint": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "pattern": "^wh_[0-9a-f-]{36}$"
          },
          "url": {
            "type": "string",
            "format": "uri"
          },
          "description": {
            "type": "string"
          },
          "events": {
            "type": "array",
            "items": {
              "type": "string",
              "enum": [
                "invoice.created",
                "invoice.updated",
                "payment.succeeded",
                "payment.failed",
                "payment.refunded",
                "payment.partially_refunded",
                "payment.disputed",
                "payment.review_required"
              ]
            }
          },
          "status": {
            "type": "string",
            "enum": [
              "unverified",
              "active",
              "paused"
            ]
          },
          "createdAt": {
            "type": "integer",
            "minimum": 0,
            "description": "Horodatage Unix en millisecondes, UTC."
          },
          "verifiedAt": {
            "type": [
              "integer",
              "null"
            ],
            "minimum": 0,
            "description": "Horodatage Unix en millisecondes, UTC."
          },
          "storeId": {
            "type": "string",
            "pattern": "^sto_[A-Za-z0-9_-]+$"
          }
        },
        "required": [
          "id",
          "url",
          "description",
          "events",
          "status",
          "createdAt",
          "verifiedAt"
        ],
        "additionalProperties": false
      },
      "WebhookDelivery": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "event_id": {
            "type": "string"
          },
          "endpoint_id": {
            "type": "string"
          },
          "type": {
            "type": "string",
            "enum": [
              "invoice.created",
              "invoice.updated",
              "payment.succeeded",
              "payment.failed",
              "payment.refunded",
              "payment.partially_refunded",
              "payment.disputed",
              "payment.review_required"
            ]
          },
          "status": {
            "type": "string",
            "enum": [
              "pending",
              "sending",
              "delivered",
              "failed",
              "cancelled"
            ]
          },
          "attempts": {
            "type": "integer",
            "minimum": 0,
            "maximum": 6
          },
          "next_attempt_at": {
            "type": "integer",
            "minimum": 0,
            "description": "Horodatage Unix en millisecondes, UTC."
          },
          "last_status": {
            "type": [
              "integer",
              "null"
            ]
          },
          "last_error": {
            "type": [
              "string",
              "null"
            ]
          },
          "created_at": {
            "type": "integer",
            "minimum": 0,
            "description": "Horodatage Unix en millisecondes, UTC."
          },
          "delivered_at": {
            "type": [
              "integer",
              "null"
            ],
            "minimum": 0,
            "description": "Horodatage Unix en millisecondes, UTC."
          }
        },
        "required": [
          "id",
          "event_id",
          "endpoint_id",
          "type",
          "status",
          "attempts"
        ],
        "additionalProperties": false
      },
      "Application": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "pattern": "^app_[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$"
          },
          "websiteUrl": {
            "type": "string",
            "format": "uri",
            "maxLength": 2048
          },
          "businessName": {
            "type": "string",
            "minLength": 1,
            "maxLength": 100
          },
          "businessType": {
            "type": "string",
            "enum": [
              "digital_goods",
              "software",
              "services",
              "physical_goods",
              "education",
              "other"
            ]
          },
          "description": {
            "type": "string",
            "minLength": 40,
            "maxLength": 3000
          },
          "contactTelegram": {
            "type": "string",
            "pattern": "^@[A-Za-z][A-Za-z0-9_]{4,31}$"
          },
          "status": {
            "type": "string",
            "enum": [
              "processing",
              "needs_review",
              "approved",
              "rejected"
            ]
          },
          "currentStage": {
            "type": "string",
            "enum": [
              "received",
              "ai_review",
              "technical_review",
              "manual_review"
            ]
          },
          "version": {
            "type": "integer",
            "minimum": 1
          },
          "reason": {
            "type": "string",
            "description": "Message d'étape visible par l'utilisateur ou raison de refus modifiée par le modérateur. Les preuves internes sont exclues."
          },
          "createdAt": {
            "type": "integer",
            "minimum": 0,
            "description": "Horodatage Unix en millisecondes, UTC."
          },
          "updatedAt": {
            "type": "integer",
            "minimum": 0,
            "description": "Horodatage Unix en millisecondes, UTC."
          },
          "stages": {
            "type": "array",
            "minItems": 4,
            "maxItems": 4,
            "items": {
              "type": "object",
              "properties": {
                "id": {
                  "type": "string",
                  "enum": [
                    "received",
                    "ai_review",
                    "technical_review",
                    "manual_review"
                  ]
                },
                "state": {
                  "type": "string",
                  "enum": [
                    "pending",
                    "running",
                    "complete",
                    "review",
                    "rejected"
                  ]
                },
                "message": {
                  "type": "string"
                },
                "updatedAt": {
                  "type": [
                    "integer",
                    "null"
                  ],
                  "minimum": 0,
                  "description": "Horodatage Unix en millisecondes, UTC."
                }
              },
              "required": [
                "id",
                "state",
                "message",
                "updatedAt"
              ],
              "additionalProperties": false
            }
          },
          "url": {
            "type": "string",
            "format": "uri",
            "description": "URL de suivi privée avec locale explicite. Connaître l'URL ne donne pas accès."
          },
          "notificationCategory": {
            "type": "string",
            "const": "support"
          },
          "canResume": {
            "type": "boolean"
          },
          "storeId": {
            "type": "string",
            "pattern": "^sto_[A-Za-z0-9_-]+$"
          }
        },
        "required": [
          "id",
          "websiteUrl",
          "businessName",
          "businessType",
          "description",
          "contactTelegram",
          "status",
          "currentStage",
          "version",
          "reason",
          "createdAt",
          "updatedAt",
          "stages",
          "url",
          "notificationCategory",
          "canResume"
        ],
        "additionalProperties": false
      },
      "ApplicationEvent": {
        "type": "object",
        "properties": {
          "version": {
            "type": "integer",
            "minimum": 1
          },
          "stage": {
            "type": "string",
            "enum": [
              "received",
              "ai_review",
              "technical_review",
              "manual_review"
            ]
          },
          "state": {
            "type": "string",
            "enum": [
              "pending",
              "running",
              "complete",
              "review",
              "rejected"
            ]
          },
          "message": {
            "type": "string"
          },
          "createdAt": {
            "type": "integer",
            "minimum": 0,
            "description": "Horodatage Unix en millisecondes, UTC."
          }
        },
        "required": [
          "version",
          "stage",
          "state",
          "message",
          "createdAt"
        ],
        "additionalProperties": false
      },
      "PaymentSummary": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "Identifiant de paiement de passerelle canonique lorsqu'il est connu ; sinon l'identifiant d'opération local gwp_."
          },
          "method": {
            "type": "string",
            "enum": [
              "card",
              "alipay",
              "sbp",
              "crypto"
            ]
          },
          "status": {
            "type": "string",
            "enum": [
              "pending",
              "requires_action",
              "processing",
              "succeeded",
              "failed",
              "expired",
              "review_required",
              "partially_paid",
              "partially_refunded",
              "refunded",
              "disputed"
            ]
          },
          "amountMinor": {
            "type": "integer",
            "minimum": 0,
            "maximum": 9007199254740991,
            "description": "Montant entier en unités monétaires mineures."
          },
          "currency": {
            "type": "string",
            "enum": [
              "USD",
              "RUB",
              "USDT"
            ],
            "description": "Unités de facture : cents USD, kopecks RUB ou centièmes d'USDT. Aucune conversion automatique de devise."
          },
          "fulfillmentReady": {
            "type": "boolean"
          },
          "updatedAt": {
            "type": "integer",
            "minimum": 0,
            "description": "Horodatage Unix en millisecondes, UTC."
          },
          "refundedAmountMinor": {
            "type": "integer",
            "minimum": 0,
            "maximum": 9007199254740991,
            "description": "Montant entier en unités monétaires mineures."
          }
        },
        "required": [
          "id",
          "method",
          "status",
          "amountMinor",
          "currency",
          "fulfillmentReady",
          "updatedAt"
        ],
        "additionalProperties": false
      },
      "CheckoutAvailability": {
        "type": "object",
        "properties": {
          "available": {
            "type": "boolean"
          },
          "code": {
            "type": "string",
            "enum": [
              "ready",
              "not_configured",
              "unsupported_allocation",
              "crypto_requires_usdt",
              "amount_out_of_range",
              "merchant_not_approved"
            ]
          },
          "flow": {
            "type": "string",
            "enum": [
              "encrypted_card",
              "qr",
              "crypto"
            ]
          },
          "minAmountMinor": {
            "type": "integer",
            "minimum": 0,
            "maximum": 9007199254740991,
            "description": "Montant entier en unités monétaires mineures."
          },
          "maxAmountMinor": {
            "type": "integer",
            "minimum": 0,
            "maximum": 9007199254740991,
            "description": "Montant entier en unités monétaires mineures."
          },
          "limitsSource": {
            "type": "string",
            "const": "adapter",
            "description": "Limites conservatrices imposées par cette intégration, pas une affirmation sur les limites commerciales en amont."
          }
        },
        "required": [
          "available",
          "code",
          "flow",
          "minAmountMinor",
          "maxAmountMinor",
          "limitsSource"
        ],
        "additionalProperties": false
      },
      "CheckoutAction": {
        "type": "object",
        "properties": {
          "type": {
            "type": "string",
            "enum": [
              "redirect",
              "qr",
              "crypto"
            ]
          },
          "url": {
            "type": [
              "string",
              "null"
            ],
            "format": "uri"
          },
          "qrImage": {
            "type": [
              "string",
              "null"
            ]
          },
          "qrPayload": {
            "type": [
              "string",
              "null"
            ]
          },
          "address": {
            "type": "string"
          },
          "amount": {
            "type": "string",
            "pattern": "^[0-9]+(?:\\.[0-9]+)?$",
            "description": "Montant exact du transfert dans la pièce sélectionnée ; préservez la chaîne décimale sans arrondi ni conversion en virgule flottante."
          },
          "currency": {
            "type": "string"
          },
          "network": {
            "type": "string"
          },
          "networkLabel": {
            "type": "string"
          },
          "expiresAt": {
            "type": [
              "integer",
              "null"
            ],
            "minimum": 0,
            "description": "Horodatage Unix en millisecondes, UTC."
          },
          "addressQrImage": {
            "type": [
              "string",
              "null"
            ]
          },
          "amountQrImage": {
            "type": [
              "string",
              "null"
            ]
          },
          "amountQrPayload": {
            "type": [
              "string",
              "null"
            ]
          },
          "networkFeePaidByCustomer": {
            "type": "boolean"
          }
        },
        "required": [
          "type",
          "expiresAt"
        ],
        "additionalProperties": false
      },
      "CheckoutPayment": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "pattern": "^gwp_[0-9a-f-]{36}$"
          },
          "method": {
            "type": "string",
            "enum": [
              "card",
              "alipay",
              "sbp",
              "crypto"
            ]
          },
          "status": {
            "type": "string",
            "enum": [
              "pending",
              "requires_action",
              "processing",
              "succeeded",
              "failed",
              "expired",
              "review_required",
              "partially_paid",
              "partially_refunded",
              "refunded",
              "disputed"
            ]
          },
          "providerStatus": {
            "type": "string"
          },
          "fulfillmentReady": {
            "type": "boolean",
            "description": "Uniquement vrai après la liaison exacte du montant/devise/commande et le succès confirmé par le fournisseur. Une redirection de navigateur n'est jamais une confirmation de paiement."
          },
          "amountMinor": {
            "type": "integer",
            "minimum": 0,
            "maximum": 9007199254740991,
            "description": "Montant entier en unités monétaires mineures."
          },
          "currency": {
            "type": "string",
            "enum": [
              "USD",
              "RUB",
              "USDT"
            ],
            "description": "Unités de facture : cents USD, kopecks RUB ou centièmes d'USDT. Aucune conversion automatique de devise."
          },
          "createdAt": {
            "type": "integer",
            "minimum": 0,
            "description": "Horodatage Unix en millisecondes, UTC."
          },
          "updatedAt": {
            "type": "integer",
            "minimum": 0,
            "description": "Horodatage Unix en millisecondes, UTC."
          },
          "nextAction": {
            "oneOf": [
              {
                "$ref": "#/components/schemas/CheckoutAction"
              },
              {
                "type": "null"
              }
            ]
          },
          "expiresAt": {
            "type": [
              "integer",
              "null"
            ],
            "minimum": 0,
            "description": "Horodatage Unix en millisecondes, UTC."
          },
          "authentication": {
            "type": "object",
            "properties": {
              "required": {
                "type": "boolean"
              },
              "expiresAt": {
                "type": [
                  "integer",
                  "null"
                ],
                "minimum": 0,
                "description": "Horodatage Unix en millisecondes, UTC."
              },
              "attempt": {
                "type": "integer",
                "minimum": 0
              }
            },
            "required": [
              "required",
              "expiresAt",
              "attempt"
            ],
            "additionalProperties": false
          },
          "reason": {
            "type": "string",
            "enum": [
              "amount_mismatch",
              "confirmation_required",
              "refund_mismatch",
              "settlement_unverified"
            ]
          },
          "refundedAmountMinor": {
            "type": "integer",
            "minimum": 0,
            "maximum": 9007199254740991,
            "description": "Montant entier en unités monétaires mineures."
          }
        },
        "required": [
          "id",
          "method",
          "status",
          "providerStatus",
          "fulfillmentReady",
          "amountMinor",
          "currency",
          "createdAt",
          "updatedAt",
          "nextAction",
          "expiresAt"
        ],
        "additionalProperties": false
      },
      "SealedCardData": {
        "type": "object",
        "properties": {
          "intent": {
            "type": "string",
            "minLength": 32,
            "maxLength": 8192,
            "description": "Intention d'opération signée et à courte durée de vie provenant de /card-intent ; transmettez-la telle quelle."
          },
          "jwe": {
            "type": "string",
            "maxLength": 14000,
            "pattern": "^[A-Za-z0-9_-]+(?:\\.[A-Za-z0-9_-]+){4}$",
            "description": "JWE compact utilisant RSA-OAEP-256 et A256GCM. En-tête protégé : kid de la clé publique, typ=kevmi-card+jwe. Seul le texte chiffré est envoyé à Kevmi. N'envoyez jamais de carte en clair ni de champs de code bancaire."
          }
        },
        "required": [
          "intent",
          "jwe"
        ],
        "additionalProperties": false
      },
      "CardIntent": {
        "type": "object",
        "properties": {
          "intent": {
            "type": "string",
            "minLength": 32,
            "maxLength": 8192
          },
          "intentId": {
            "type": "string",
            "maxLength": 100
          },
          "expiresAt": {
            "type": "integer",
            "minimum": 0,
            "description": "Horodatage Unix en millisecondes, UTC."
          },
          "key": {
            "type": "object",
            "properties": {
              "kid": {
                "type": "string"
              },
              "kty": {
                "type": "string",
                "const": "RSA"
              },
              "n": {
                "type": "string"
              },
              "e": {
                "type": "string"
              },
              "alg": {
                "type": "string",
                "const": "RSA-OAEP-256"
              },
              "use": {
                "type": "string",
                "const": "enc"
              }
            },
            "required": [
              "kid",
              "kty",
              "n",
              "e",
              "alg",
              "use"
            ],
            "additionalProperties": false
          },
          "algorithm": {
            "type": "string",
            "const": "RSA-OAEP-256"
          },
          "encryption": {
            "type": "string",
            "const": "A256GCM"
          },
          "protocol": {
            "type": "string",
            "const": "kevmi-card-v1"
          }
        },
        "required": [
          "intent",
          "intentId",
          "expiresAt",
          "key",
          "algorithm",
          "encryption",
          "protocol"
        ],
        "additionalProperties": false
      },
      "StartCheckoutPayment": {
        "oneOf": [
          {
            "type": "object",
            "properties": {
              "method": {
                "type": "string",
                "const": "card"
              },
              "sealed": {
                "$ref": "#/components/schemas/SealedCardData"
              }
            },
            "required": [
              "method",
              "sealed"
            ],
            "additionalProperties": false
          },
          {
            "type": "object",
            "properties": {
              "method": {
                "type": "string",
                "const": "alipay"
              },
              "name": {
                "type": "string",
                "minLength": 1,
                "maxLength": 200
              },
              "country": {
                "type": "string",
                "const": "CN"
              },
              "email": {
                "type": "string",
                "format": "email",
                "maxLength": 254
              }
            },
            "required": [
              "method",
              "name",
              "country"
            ],
            "additionalProperties": false
          },
          {
            "type": "object",
            "properties": {
              "method": {
                "type": "string",
                "const": "sbp"
              }
            },
            "required": [
              "method"
            ],
            "additionalProperties": false
          },
          {
            "type": "object",
            "properties": {
              "method": {
                "type": "string",
                "const": "crypto"
              },
              "payerCurrency": {
                "type": "string",
                "pattern": "^[A-Z0-9]{2,16}$"
              },
              "network": {
                "type": "string",
                "pattern": "^[a-z0-9_-]{2,32}$"
              }
            },
            "required": [
              "method",
              "payerCurrency",
              "network"
            ],
            "additionalProperties": false
          }
        ]
      },
      "LegacyGatewayEvent": {
        "type": "object",
        "properties": {
          "eventId": {
            "type": "string",
            "maxLength": 100,
            "pattern": "^[A-Za-z0-9_.:-]+$"
          },
          "paymentId": {
            "type": "string",
            "maxLength": 100,
            "pattern": "^[A-Za-z0-9_.:-]+$"
          },
          "invoiceId": {
            "type": "string",
            "pattern": "^lnk_[0-9a-f-]{36}$"
          },
          "status": {
            "type": "string",
            "enum": [
              "succeeded",
              "failed",
              "refunded"
            ]
          },
          "amountMinor": {
            "type": "integer",
            "minimum": 0,
            "maximum": 9007199254740991,
            "description": "Montant entier en unités monétaires mineures."
          },
          "currency": {
            "type": "string",
            "enum": [
              "USD",
              "RUB",
              "USDT"
            ],
            "description": "Unités de facture : cents USD, kopecks RUB ou centièmes d'USDT. Aucune conversion automatique de devise."
          },
          "method": {
            "type": "string",
            "enum": [
              "card",
              "alipay",
              "sbp",
              "crypto"
            ]
          },
          "occurredAt": {
            "type": "integer",
            "minimum": 0,
            "description": "Horodatage Unix en millisecondes, UTC."
          }
        },
        "required": [
          "eventId",
          "paymentId",
          "invoiceId",
          "status",
          "amountMinor",
          "currency",
          "method",
          "occurredAt"
        ],
        "additionalProperties": false
      },
      "NativeGatewayEvent": {
        "type": "object",
        "required": [
          "id",
          "api_version",
          "mode",
          "type",
          "data"
        ],
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "api_version": {
            "type": "string",
            "const": "3"
          },
          "mode": {
            "type": "string",
            "const": "live"
          },
          "type": {
            "type": "string",
            "pattern": "^[a-z_.]{1,80}$"
          },
          "data": {
            "type": "object",
            "required": [
              "id",
              "version"
            ],
            "properties": {
              "id": {
                "type": "string",
                "format": "uuid"
              },
              "version": {
                "type": "integer",
                "minimum": 0
              },
              "merchant_order_id": {
                "type": "string"
              }
            }
          }
        }
      },
      "Store": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "pattern": "^sto_[A-Za-z0-9_-]+$"
          },
          "name": {
            "type": "string",
            "minLength": 1,
            "maxLength": 80
          },
          "vatPayer": {
            "type": "string",
            "enum": [
              "customer",
              "merchant"
            ]
          },
          "createdAt": {
            "type": "integer",
            "minimum": 0,
            "description": "Horodatage Unix en millisecondes, UTC."
          },
          "updatedAt": {
            "type": "integer",
            "minimum": 0,
            "description": "Horodatage Unix en millisecondes, UTC."
          }
        },
        "required": [
          "id",
          "name",
          "vatPayer",
          "createdAt",
          "updatedAt"
        ],
        "additionalProperties": false
      },
      "ExchangeRate": {
        "type": "object",
        "properties": {
          "rubPerUsdMicros": {
            "type": "integer",
            "minimum": 1000000
          },
          "effectiveAt": {
            "type": "integer",
            "minimum": 0,
            "description": "Horodatage Unix en millisecondes, UTC."
          },
          "fetchedAt": {
            "type": "integer",
            "minimum": 0,
            "description": "Horodatage Unix en millisecondes, UTC."
          },
          "source": {
            "type": "string",
            "const": "https://www.cbr.ru/scripts/XML_daily.asp"
          },
          "usdtPerUsd": {
            "type": "string",
            "const": "1.00"
          }
        },
        "required": [
          "rubPerUsdMicros",
          "effectiveAt",
          "fetchedAt",
          "source",
          "usdtPerUsd"
        ],
        "additionalProperties": false
      },
      "SettlementQuote": {
        "type": "object",
        "properties": {
          "amountMinor": {
            "type": "integer",
            "minimum": 0,
            "maximum": 9007199254740991,
            "description": "Montant entier en unités monétaires mineures."
          },
          "baseMinor": {
            "type": "integer",
            "minimum": 0,
            "maximum": 9007199254740991,
            "description": "Montant entier en unités monétaires mineures."
          },
          "grossMinor": {
            "type": "integer",
            "minimum": 0,
            "maximum": 9007199254740991,
            "description": "Montant entier en unités monétaires mineures."
          },
          "percentageFeeMinor": {
            "type": "integer",
            "minimum": 0,
            "maximum": 9007199254740991,
            "description": "Montant entier en unités monétaires mineures."
          },
          "fixedFeeMinor": {
            "type": "integer",
            "minimum": 0,
            "maximum": 9007199254740991,
            "description": "Montant entier en unités monétaires mineures."
          },
          "nominalFeeMinor": {
            "type": "integer",
            "minimum": 0,
            "maximum": 9007199254740991,
            "description": "Montant entier en unités monétaires mineures."
          },
          "feeMinor": {
            "type": "integer",
            "minimum": 0,
            "maximum": 9007199254740991,
            "description": "Montant entier en unités monétaires mineures."
          },
          "vatMinor": {
            "type": "integer",
            "minimum": 0,
            "maximum": 9007199254740991,
            "description": "Montant entier en unités monétaires mineures."
          },
          "customerVatMinor": {
            "type": "integer",
            "minimum": 0,
            "maximum": 9007199254740991,
            "description": "Montant entier en unités monétaires mineures."
          },
          "merchantVatMinor": {
            "type": "integer",
            "minimum": 0,
            "maximum": 9007199254740991,
            "description": "Montant entier en unités monétaires mineures."
          },
          "customerTotalMinor": {
            "type": "integer",
            "minimum": 0,
            "maximum": 9007199254740991,
            "description": "Montant entier en unités monétaires mineures."
          },
          "merchantDeductionMinor": {
            "type": "integer",
            "minimum": 0,
            "maximum": 9007199254740991,
            "description": "Montant entier en unités monétaires mineures."
          },
          "totalFeeMinor": {
            "type": "integer",
            "minimum": 0,
            "maximum": 9007199254740991,
            "description": "Montant entier en unités monétaires mineures."
          },
          "netMinor": {
            "type": "integer",
            "minimum": 0,
            "maximum": 9007199254740991,
            "description": "Montant entier en unités monétaires mineures."
          },
          "currency": {
            "type": "string",
            "enum": [
              "USD",
              "RUB",
              "USDT"
            ],
            "description": "Unités de facture : cents USD, kopecks RUB ou centièmes d'USDT. Aucune conversion automatique de devise."
          },
          "method": {
            "type": "string",
            "enum": [
              "card",
              "alipay",
              "sbp",
              "crypto"
            ]
          },
          "vatPayer": {
            "type": "string",
            "enum": [
              "customer",
              "merchant",
              "shared"
            ]
          },
          "vatApplies": {
            "type": "boolean"
          },
          "pricingVersion": {
            "type": "integer",
            "minimum": 1,
            "description": "Version de calcul enregistrée avec la facture."
          },
          "vatCustomerPercent": {
            "type": "integer",
            "minimum": 0,
            "maximum": 100,
            "description": "Part du client, en pourcentage entier. 0 signifie que le marchand couvre tout ; 100 signifie que le client couvre tout. Par défaut : la préférence TVA héritée du magasin."
          },
          "feeCustomerPercent": {
            "type": "integer",
            "minimum": 0,
            "maximum": 100,
            "description": "Part du client, en pourcentage entier. 0 signifie que le marchand couvre tout ; 100 signifie que le client couvre tout.",
            "default": 0
          },
          "customerFeeMinor": {
            "type": "integer",
            "minimum": 0,
            "maximum": 9007199254740991,
            "description": "Montant entier en unités monétaires mineures."
          },
          "merchantFeeMinor": {
            "type": "integer",
            "minimum": 0,
            "maximum": 9007199254740991,
            "description": "Montant entier en unités monétaires mineures."
          }
        },
        "required": [
          "amountMinor",
          "baseMinor",
          "grossMinor",
          "percentageFeeMinor",
          "fixedFeeMinor",
          "nominalFeeMinor",
          "feeMinor",
          "vatMinor",
          "customerVatMinor",
          "merchantVatMinor",
          "customerTotalMinor",
          "merchantDeductionMinor",
          "totalFeeMinor",
          "netMinor",
          "currency",
          "method",
          "vatPayer",
          "vatApplies",
          "pricingVersion"
        ],
        "additionalProperties": false
      }
    }
  },
  "webhooks": {
    "merchantEvent": {
      "post": {
        "summary": "Livraison vérifiée au serveur du marchand",
        "description": "Livré uniquement aux points de terminaison actifs et vérifiés par défi. Le JSON brut est signé ; vérifiez l'horodatage et la signature avant d'analyser ou de modifier l'état de la commande. Renvoyez un 2xx après avoir accepté durablement l'événement. Persistez les identifiants d'événement pour gérer les doublons. Les erreurs réseau, les délais d'attente et les statuts non-2xx déclenchent des nouvelles tentatives.",
        "parameters": [
          {
            "name": "Kevmi-Signature",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "Kevmi-Event-Id",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "Kevmi-Delivery-Id",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "description": "Objet JSON. Maximum 16 384 octets. Les champs inconnus sont rejetés.",
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/MerchantEvent"
              }
            }
          }
        },
        "responses": {
          "2XX": {
            "description": "Accepté durablement par le serveur du marchand."
          }
        }
      }
    }
  }
}
