{"openapi":"3.1.0","info":{"title":"Factur-X Validation API","description":"\n# Factur-X Validation API\n\nAPI REST pour la validation de factures électroniques au format **Factur-X** (PDF avec XML embarqué)\nconforme à la norme européenne **EN16931**.\n\n## 🎯 Fonctionnalités\n\n- ✅ Validation XSD (structure XML)\n- ✅ Validation Schematron (règles métier EN16931)\n- ✅ Support PDF Factur-X (extraction automatique du XML)\n- ✅ Support XML standalone\n- ✅ Stockage temporaire sécurisé (R2/Local)\n- ✅ Rapports de validation détaillés (erreurs + warnings)\n\n## 📖 Utilisation\n\n### Validation d'un fichier\n\n```bash\ncurl -X POST \"http://localhost:8000/api/v1/validate\" \\\n  -H \"Content-Type: multipart/form-data\" \\\n  -H \"Authorization: Bearer <API_KEY>\" \\\n  -F \"file=@facture.xml\"\n```\n\n### Réponse\n\n```json\n{\n  \"valid\": true,\n  \"errors\": [],\n  \"warnings\": [],\n  \"message\": \"Validation completed\"\n}\n```\n\n## 🔗 Ressources\n\n- **Documentation interactive** : [/docs](/docs)\n- **Documentation alternative** : [/redoc](/redoc)\n- **Health check** : [/health](/health)\n        ","contact":{"name":"Factur-X Validation API","url":"https://github.com/yourusername/facturx-validation-api"},"license":{"name":"MIT"},"version":"1.0.0"},"paths":{"/api/v1/api-keys":{"post":{"tags":["api-keys"],"summary":"Create Api Key","description":"Generate a new API key.","operationId":"create_api_key_api_v1_api_keys_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"x-api-key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Api-Key"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiKeyCreateRequest"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiKeyCreateResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"get":{"tags":["api-keys"],"summary":"List Api Keys","description":"List API keys without exposing the secret.","operationId":"list_api_keys_api_v1_api_keys_get","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"x-api-key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Api-Key"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiKeyListResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/api-keys/rotate":{"post":{"tags":["api-keys"],"summary":"Rotate Api Key","description":"Rotate the current API key (revoke + create).","operationId":"rotate_api_key_api_v1_api_keys_rotate_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"x-api-key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Api-Key"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiKeyRotateRequest"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiKeyCreateResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/api-keys/revoke":{"post":{"tags":["api-keys"],"summary":"Revoke Api Key","description":"Revoke an API key.","operationId":"revoke_api_key_api_v1_api_keys_revoke_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"x-api-key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Api-Key"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiKeyRevokeRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiKeyRevokeResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/me/billing/checkout":{"post":{"tags":["billing"],"summary":"Create Checkout Session","description":"Create a Stripe checkout session for plan upgrade.\n\nHYBRID AUTH: Supports both JWT dashboard users and API key clients.\n\nAuthentication:\n    - Dashboard users: JWT access token (Authorization: Bearer)\n    - API clients: API key (X-API-Key header)\n    - Auto-refresh: refresh_token cookie → new JWT","operationId":"create_checkout_session_api_v1_me_billing_checkout_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"x-api-key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Api-Key"}},{"name":"refresh_token","in":"cookie","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Refresh Token"}},{"name":"session","in":"cookie","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Session"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CheckoutRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CheckoutResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/me/billing/portal":{"post":{"tags":["billing"],"summary":"Create Portal Session","description":"Create a Stripe Customer Portal session for managing subscription.\n\nHYBRID AUTH: Supports both JWT dashboard users and API key clients.\n\nThe portal allows users to:\n- Update payment method\n- Cancel subscription\n- View billing history\n- Download invoices\n\nIf the user has no Stripe customer ID yet (never completed checkout),\nreturns 400 with guidance to complete checkout first.\n\nAuthentication:\n    - Dashboard users: JWT access token (Authorization: Bearer)\n    - API clients: API key (X-API-Key header)\n    - Auto-refresh: refresh_token cookie → new JWT","operationId":"create_portal_session_api_v1_me_billing_portal_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"x-api-key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Api-Key"}},{"name":"refresh_token","in":"cookie","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Refresh Token"}},{"name":"session","in":"cookie","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Session"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PortalResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/credits/checkout":{"post":{"tags":["credits"],"summary":"Create Credit Checkout","description":"Create a Stripe checkout session for a credit pack purchase.\n\nOne-time payment (mode=payment, not subscription).\nGated behind CREDIT_SYSTEM_ENABLED feature flag.\nCredits are added to the user's balance via webhook on successful payment.\n\nHYBRID AUTH: Supports both JWT dashboard users and API key clients.","operationId":"create_credit_checkout_credits_checkout_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"x-api-key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Api-Key"}},{"name":"refresh_token","in":"cookie","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Refresh Token"}},{"name":"session","in":"cookie","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Session"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreditCheckoutResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/me/api-keys":{"get":{"tags":["me"],"summary":"List My Api Keys","operationId":"list_my_api_keys_api_v1_me_api_keys_get","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"x-api-key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Api-Key"}},{"name":"refresh_token","in":"cookie","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Refresh Token"}},{"name":"session","in":"cookie","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Session"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SelfApiKeyListResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"post":{"tags":["me"],"summary":"Create My Api Key","operationId":"create_my_api_key_api_v1_me_api_keys_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"x-api-key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Api-Key"}},{"name":"refresh_token","in":"cookie","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Refresh Token"}},{"name":"session","in":"cookie","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Session"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SelfApiKeyCreateRequest"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SelfApiKeyCreateResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/me/api-keys/{key_id}":{"delete":{"tags":["me"],"summary":"Revoke My Api Key","operationId":"revoke_my_api_key_api_v1_me_api_keys__key_id__delete","parameters":[{"name":"key_id","in":"path","required":true,"schema":{"type":"string","title":"Key Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"x-api-key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Api-Key"}},{"name":"refresh_token","in":"cookie","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Refresh Token"}},{"name":"session","in":"cookie","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Session"}}],"responses":{"204":{"description":"Successful Response"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/me/usage":{"get":{"tags":["me"],"summary":"Get My Usage","operationId":"get_my_usage_api_v1_me_usage_get","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"x-api-key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Api-Key"}},{"name":"refresh_token","in":"cookie","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Refresh Token"}},{"name":"session","in":"cookie","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Session"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UsageResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/me":{"get":{"tags":["me"],"summary":"Get Me Summary","description":"Get account summary: email, plan, and current usage.","operationId":"get_me_summary_api_v1_me_get","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"x-api-key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Api-Key"}},{"name":"refresh_token","in":"cookie","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Refresh Token"}},{"name":"session","in":"cookie","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Session"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MeSummaryResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/me/validations":{"get":{"tags":["me"],"summary":"List My Validations","description":"List validation runs with cursor-based pagination.\n\n**Paid tier feature**: Requires Integration or Business plan (legacy Pro grandfathered).\n\nQuery parameters:\n- limit: Max results per page (default 50, max 200)\n- cursor: Pagination cursor from previous response\n- status: Filter by 'valid' or 'invalid'\n- profile: Filter by Factur-X profile (MINIMUM, BASIC, EN16931)\n- from: ISO date filter (inclusive)\n- to: ISO date filter (exclusive)","operationId":"list_my_validations_api_v1_me_validations_get","parameters":[{"name":"limit","in":"query","required":false,"schema":{"type":"integer","default":50,"title":"Limit"}},{"name":"cursor","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Cursor"}},{"name":"status_filter","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Status Filter"}},{"name":"profile","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Profile"}},{"name":"from_date","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"From Date"}},{"name":"to_date","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"To Date"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"x-api-key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Api-Key"}},{"name":"refresh_token","in":"cookie","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Refresh Token"}},{"name":"session","in":"cookie","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Session"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidationRunListResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/me/validations/export":{"get":{"tags":["me"],"summary":"Export My Validations","description":"Export validation history as CSV or JSON.\n\n**Paid tier feature**: Requires Integration or Business plan (legacy Pro grandfathered).\n\nExports up to 30 days of validation history.\n\nQuery parameters:\n- format: 'csv' or 'json' (required)\n- status: Filter by 'valid' or 'invalid'\n- profile: Filter by Factur-X profile (MINIMUM, BASIC, EN16931)\n- from: ISO date filter (inclusive)\n- to: ISO date filter (exclusive)\n\nRGPD-safe: Does not include document content, hash, or error messages.","operationId":"export_my_validations_api_v1_me_validations_export_get","parameters":[{"name":"format","in":"query","required":true,"schema":{"allOf":[{"$ref":"#/components/schemas/ExportFormat"}],"description":"Export format: csv or json","title":"Format"},"description":"Export format: csv or json"},{"name":"status","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Filter: valid or invalid","title":"Status"},"description":"Filter: valid or invalid"},{"name":"profile","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Filter by Factur-X profile","title":"Profile"},"description":"Filter by Factur-X profile"},{"name":"from","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Start date (ISO format)","title":"From"},"description":"Start date (ISO format)"},{"name":"to","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"End date (ISO format)","title":"To"},"description":"End date (ISO format)"},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"x-api-key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Api-Key"}},{"name":"refresh_token","in":"cookie","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Refresh Token"}},{"name":"session","in":"cookie","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Session"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/me/validations/{run_id}":{"get":{"tags":["me"],"summary":"Get My Validation","description":"Get detailed validation run by ID.\n\n**Paid tier feature**: Requires Integration or Business plan (legacy Pro grandfathered).\n\nReturns full details including RGPD-safe issues summary\n(code/field/severity only, no messages or values).","operationId":"get_my_validation_api_v1_me_validations__run_id__get","parameters":[{"name":"run_id","in":"path","required":true,"schema":{"type":"string","title":"Run Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"x-api-key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Api-Key"}},{"name":"refresh_token","in":"cookie","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Refresh Token"}},{"name":"session","in":"cookie","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Session"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidationRunDetailResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/admin/ping":{"get":{"tags":["admin"],"summary":"Admin Ping","description":"Lightweight endpoint for token validation without touching D1.","operationId":"admin_ping_admin_ping_get","parameters":[{"name":"x-admin-token","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Admin-Token"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":{"type":"boolean"},"title":"Response Admin Ping Admin Ping Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/admin/debug-consent/records":{"get":{"tags":["admin"],"summary":"Admin Debug Consent Records","description":"Debug consent records — failed conversions where user opted in to file retention.\n\nShows: active artifacts (not yet cleaned), recent consents, error breakdown.","operationId":"admin_debug_consent_records_admin_debug_consent_records_get","parameters":[{"name":"x-admin-token","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Admin-Token"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","title":"Response Admin Debug Consent Records Admin Debug Consent Records Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/admin/debug-consent/cleanup":{"post":{"tags":["admin"],"summary":"Admin Debug Consent Cleanup","description":"Trigger cleanup of expired debug consent artifacts.\n\nDeletes R2 objects for consents past their 72h TTL and marks\nrecords as deleted. Safe to call multiple times (idempotent).","operationId":"admin_debug_consent_cleanup_admin_debug_consent_cleanup_post","parameters":[{"name":"x-admin-token","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Admin-Token"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","title":"Response Admin Debug Consent Cleanup Admin Debug Consent Cleanup Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/admin/escalation/stats":{"get":{"tags":["admin"],"summary":"Admin Escalation Stats","description":"Shadow mode monitoring dashboard data.\n\nReturns:\n- runtime: in-memory counters since last deploy (volume, parity, confidence)\n- suppliers: persistent D1 data (known suppliers, top by observations)\n- config: current escalation feature flags","operationId":"admin_escalation_stats_admin_escalation_stats_get","parameters":[{"name":"x-admin-token","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Admin-Token"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","title":"Response Admin Escalation Stats Admin Escalation Stats Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/admin/convert/rollout-guard/test-alert":{"post":{"tags":["admin"],"summary":"Admin Convert Rollout Guard Test Alert","operationId":"admin_convert_rollout_guard_test_alert_admin_convert_rollout_guard_test_alert_post","parameters":[{"name":"x-admin-token","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Admin-Token"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RolloutGuardTestAlertRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RolloutGuardTestAlertResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/admin/convert/rollout-guard/preflight":{"get":{"tags":["admin"],"summary":"Admin Convert Rollout Guard Preflight","operationId":"admin_convert_rollout_guard_preflight_admin_convert_rollout_guard_preflight_get","parameters":[{"name":"x-admin-token","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Admin-Token"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RolloutGuardPreflightResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/admin/convert/prod-readiness":{"get":{"tags":["admin"],"summary":"Admin Convert Prod Readiness","operationId":"admin_convert_prod_readiness_admin_convert_prod_readiness_get","parameters":[{"name":"x-admin-token","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Admin-Token"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ConvertProdReadinessResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/admin/system/config":{"get":{"tags":["admin"],"summary":"Admin System Config","operationId":"admin_system_config_admin_system_config_get","parameters":[{"name":"x-admin-token","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Admin-Token"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","title":"Response Admin System Config Admin System Config Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/admin/usage/periods":{"get":{"tags":["admin"],"summary":"Admin Usage Periods","description":"Get available usage periods for the period selector.","operationId":"admin_usage_periods_admin_usage_periods_get","parameters":[{"name":"x-admin-token","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Admin-Token"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","title":"Response Admin Usage Periods Admin Usage Periods Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/admin/usage":{"get":{"tags":["admin"],"summary":"Admin Usage","operationId":"admin_usage_admin_usage_get","parameters":[{"name":"include_zero","in":"query","required":false,"schema":{"type":"boolean","description":"Include users without current-period usage counters","default":true,"title":"Include Zero"},"description":"Include users without current-period usage counters"},{"name":"period_start","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Period start date (YYYY-MM-01 format) or 'all' for all-time. Defaults to current month.","title":"Period Start"},"description":"Period start date (YYYY-MM-01 format) or 'all' for all-time. Defaults to current month."},{"name":"x-admin-token","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Admin-Token"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","title":"Response Admin Usage Admin Usage Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/admin/users/summary":{"get":{"tags":["admin"],"summary":"Admin Users Summary","operationId":"admin_users_summary_admin_users_summary_get","parameters":[{"name":"from","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Start date (YYYY-MM-DD)","title":"From"},"description":"Start date (YYYY-MM-DD)"},{"name":"to","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"End date (YYYY-MM-DD)","title":"To"},"description":"End date (YYYY-MM-DD)"},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":500,"minimum":1,"description":"Max users to return","default":50,"title":"Limit"},"description":"Max users to return"},{"name":"offset","in":"query","required":false,"schema":{"type":"integer","minimum":0,"description":"Offset for pagination","default":0,"title":"Offset"},"description":"Offset for pagination"},{"name":"sort","in":"query","required":false,"schema":{"type":"string","pattern":"^(last_activity_at|usage_api|usage_ui|keys_total_count)$","description":"Sort field: last_activity_at, usage_api, usage_ui, keys_total_count","default":"last_activity_at","title":"Sort"},"description":"Sort field: last_activity_at, usage_api, usage_ui, keys_total_count"},{"name":"sort_order","in":"query","required":false,"schema":{"type":"string","pattern":"^(asc|desc)$","description":"Sort order","default":"desc","title":"Sort Order"},"description":"Sort order"},{"name":"plan","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Filter by plan code (free/pro)","title":"Plan"},"description":"Filter by plan code (free/pro)"},{"name":"activated_api","in":"query","required":false,"schema":{"anyOf":[{"type":"boolean"},{"type":"null"}],"description":"Filter users with API usage","title":"Activated Api"},"description":"Filter users with API usage"},{"name":"activated_ui","in":"query","required":false,"schema":{"anyOf":[{"type":"boolean"},{"type":"null"}],"description":"Filter users with UI usage","title":"Activated Ui"},"description":"Filter users with UI usage"},{"name":"email_domain","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Filter by email domain","title":"Email Domain"},"description":"Filter by email domain"},{"name":"x-admin-token","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Admin-Token"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","title":"Response Admin Users Summary Admin Users Summary Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/admin/funnel/free-api-key":{"get":{"tags":["admin"],"summary":"Admin Free Api Key Funnel","operationId":"admin_free_api_key_funnel_admin_funnel_free_api_key_get","parameters":[{"name":"from","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Start date (YYYY-MM-DD)","title":"From"},"description":"Start date (YYYY-MM-DD)"},{"name":"to","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"End date (YYYY-MM-DD)","title":"To"},"description":"End date (YYYY-MM-DD)"},{"name":"x-admin-token","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Admin-Token"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","title":"Response Admin Free Api Key Funnel Admin Funnel Free Api Key Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/admin/funnel/events":{"get":{"tags":["admin"],"summary":"Admin Funnel Events","operationId":"admin_funnel_events_admin_funnel_events_get","parameters":[{"name":"event_name","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Filter by event name","title":"Event Name"},"description":"Filter by event name"},{"name":"from","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Start date (YYYY-MM-DD)","title":"From"},"description":"Start date (YYYY-MM-DD)"},{"name":"to","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"End date (YYYY-MM-DD)","title":"To"},"description":"End date (YYYY-MM-DD)"},{"name":"email_domain","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Filter by email domain","title":"Email Domain"},"description":"Filter by email domain"},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":500,"minimum":1,"description":"Max events to return","default":200,"title":"Limit"},"description":"Max events to return"},{"name":"x-admin-token","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Admin-Token"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","title":"Response Admin Funnel Events Admin Funnel Events Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/admin/audit":{"get":{"tags":["admin"],"summary":"Get audit log entries (admin only)","description":"Get audit log entries with optional filters.\n\n**Filters**:\n- `user_id`: Filter by user ID\n- `action`: Filter by action (e.g., 'key_create', 'login_success')\n- `status`: Filter by status ('success', 'failure', 'error')\n- `from_date`: Filter from date (YYYY-MM-DD)\n- `to_date`: Filter to date (YYYY-MM-DD)\n\n**Pagination**:\n- `page`: Page number (1-indexed, default: 1)\n- `page_size`: Items per page (default: 50, max: 100)\n\nEntries are ordered by timestamp descending (newest first).","operationId":"admin_audit_admin_audit_get","parameters":[{"name":"user_id","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Filter by user ID","title":"User Id"},"description":"Filter by user ID"},{"name":"action","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Filter by action type","title":"Action"},"description":"Filter by action type"},{"name":"status","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Filter by status","title":"Status"},"description":"Filter by status"},{"name":"from_date","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Filter from date (YYYY-MM-DD)","title":"From Date"},"description":"Filter from date (YYYY-MM-DD)"},{"name":"to_date","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Filter to date (YYYY-MM-DD)","title":"To Date"},"description":"Filter to date (YYYY-MM-DD)"},{"name":"page","in":"query","required":false,"schema":{"type":"integer","minimum":1,"description":"Page number (1-indexed)","default":1,"title":"Page"},"description":"Page number (1-indexed)"},{"name":"page_size","in":"query","required":false,"schema":{"type":"integer","maximum":100,"minimum":1,"description":"Items per page","default":50,"title":"Page Size"},"description":"Items per page"},{"name":"x-admin-token","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Admin-Token"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Audit log entries","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuditLogResponse"}}}},"401":{"description":"Invalid or missing admin token"},"503":{"description":"Database unavailable"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/admin/beta/grant":{"post":{"tags":["admin"],"summary":"Grant beta access bonus quota to a user","description":"Grant bonus quota to a user for beta access or promotional purposes.\n\n**Required**: Either `email` or `user_id` must be provided.\n\n**Audit**: All grants are logged with the admin identifier and optional note.\n\n**Examples**:\n- Beta tester: 2000 bonus, expires in 30 days\n- Promotional: 500 bonus, no expiration (permanent)","operationId":"grant_beta_access_admin_beta_grant_post","parameters":[{"name":"x-admin-token","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Admin-Token"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BetaGrantRequest"}}}},"responses":{"201":{"description":"Beta bonus granted successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BetaGrantResponse"}}}},"400":{"description":"Invalid request (user not found, invalid params)"},"401":{"description":"Invalid or missing admin token"},"503":{"description":"Database unavailable"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/admin/entitlements/{entitlement_id}/extend":{"post":{"tags":["admin"],"summary":"Extend (or shorten) an existing entitlement window","description":"Move an entitlement's `expires_at` to a new value. Use either:\n  - `days`: relative — new expiration = now + N days (default flow:\n    \"Prolonger +15 jours\" button on the admin entitlements page)\n  - `until`: absolute — new expiration set to the provided UTC ISO 8601 datetime\n    (used by the date picker for custom durations like +30 jours pour un gros prospect)\n\nUseful when an ERP integrator needs more time to finish their integration.\nThe new expiration must be strictly after the entitlement's `starts_at`.\nAudit-trail logged with admin actor + previous/new values.","operationId":"extend_entitlement_admin_entitlements__entitlement_id__extend_post","parameters":[{"name":"entitlement_id","in":"path","required":true,"schema":{"type":"string","title":"Entitlement Id"}},{"name":"x-admin-token","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Admin-Token"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ExtendEntitlementRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ExtendEntitlementResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/admin/beta/entitlements/{user_identifier}":{"get":{"tags":["admin"],"summary":"List a user's entitlements","description":"Get all entitlements (active and expired) for a user.\n\n**user_identifier**: Can be either user_id (UUID) or email address.","operationId":"list_user_entitlements_admin_beta_entitlements__user_identifier__get","parameters":[{"name":"user_identifier","in":"path","required":true,"schema":{"type":"string","title":"User Identifier"}},{"name":"x-admin-token","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Admin-Token"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"User entitlements","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserEntitlementsResponse"}}}},"400":{"description":"User not found"},"401":{"description":"Invalid or missing admin token"},"503":{"description":"Database unavailable"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/admin/monetization":{"get":{"tags":["admin"],"summary":"Get monetization funnel conversion metrics (admin only)","description":"Get monetization conversion metrics for the quota-to-subscription funnel.\n\n**Metrics tracked:**\n- `quota_80_reached`: Users hitting 80% quota threshold\n- `quota_100_reached`: Users hitting 100% quota threshold\n- `upgrade_email_sent`: Quota warning emails sent\n- `api_upgrade_intent`: API-originated upgrade intents\n- `checkout_session_created`: Stripe checkout sessions initiated\n- `subscription_activated`: Successful Pro subscriptions\n- `quota_exceeded`: Total quota exceeded events\n- `channel_counts`: Split (`api` / `ui` / `unknown`) for checkout and quota events\n- `data_quality`: Missing/mismatch diagnostics for `channel` and `source` metadata\n\n**Time periods:**\n- 7-day metrics (last 7 days)\n- 30-day metrics (last 30 days)\n\n**Conversion rates:**\n- `email_to_checkout_rate`: upgrade_email_sent → checkout_session_created\n- `checkout_to_subscription_rate`: checkout_session_created → subscription_activated\n- `overall_conversion_rate`: quota_exceeded → subscription_activated\n- `api_upgrade_intent_to_checkout_rate`: api_upgrade_intent → checkout_session_created(channel=api)","operationId":"admin_monetization_metrics_admin_monetization_get","parameters":[{"name":"x-admin-token","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Admin-Token"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Monetization conversion metrics","content":{"application/json":{"schema":{"type":"object","title":"Response Admin Monetization Metrics Admin Monetization Get"}}}},"401":{"description":"Invalid or missing admin token"},"503":{"description":"Database unavailable"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/admin/beta/send-test-email":{"post":{"tags":["admin"],"summary":"Send beta access test email (admin only)","description":"Send a test beta access email to verify email formatting and delivery.\n\n**Email Template Options:**\n- With `api_key`: Shows the specific API key in the email (for new users)\n- Without `api_key` (empty): Generic template referencing existing API key (for existing users)\n\nThis is useful for testing both email templates and ensuring proper configuration\nbefore sending emails to actual beta users.","operationId":"send_beta_test_email_admin_beta_send_test_email_post","parameters":[{"name":"email","in":"query","required":true,"schema":{"type":"string","description":"Email address to send test to","title":"Email"},"description":"Email address to send test to"},{"name":"api_key","in":"query","required":false,"schema":{"type":"string","description":"Optional API key to show in email. If empty, sends generic template for existing users","default":"","title":"Api Key"},"description":"Optional API key to show in email. If empty, sends generic template for existing users"},{"name":"x-admin-token","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Admin-Token"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Test email sent successfully","content":{"application/json":{"schema":{"type":"object","additionalProperties":{"type":"string"},"title":"Response Send Beta Test Email Admin Beta Send Test Email Post"}}}},"401":{"description":"Invalid or missing admin token"},"500":{"description":"Failed to send email"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/admin/validation/analytics":{"get":{"tags":["admin"],"summary":"Validation analytics dashboard","description":"Aggregate validation statistics for product analytics:\n- File type distribution (PDF vs XML)\n- Pass/fail rates\n- Profile distribution\n- Top error codes\n- Source distribution (API vs web)\n- Daily volume trends\n\nData comes from validation_runs table (RGPD-safe, no PII).","operationId":"admin_validation_analytics_admin_validation_analytics_get","parameters":[{"name":"days","in":"query","required":false,"schema":{"type":"integer","maximum":365,"minimum":1,"description":"Number of days to analyze","default":30,"title":"Days"},"description":"Number of days to analyze"},{"name":"x-admin-token","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Admin-Token"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","title":"Response Admin Validation Analytics Admin Validation Analytics Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/admin/convert/analytics":{"get":{"tags":["admin"],"summary":"Convert analytics dashboard","description":"Aggregate conversion statistics for product analytics:\n- Status distribution (completed/failed/expired)\n- Mode distribution (auto/strict_pdfa3/xml_only)\n- Profile distribution (from result_json)\n- Source type distribution (native_text/scan_ocr/hybrid)\n- Packaging rate (PDF/A-3 generation)\n- Daily volume trends\n- Average duration\n- OCR/cloud usage from budget counters\n\nData comes from convert_jobs + convert_budget_daily_counters (RGPD-safe, no PII).","operationId":"admin_convert_analytics_admin_convert_analytics_get","parameters":[{"name":"days","in":"query","required":false,"schema":{"type":"integer","maximum":365,"minimum":1,"description":"Number of days to analyze","default":30,"title":"Days"},"description":"Number of days to analyze"},{"name":"x-admin-token","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Admin-Token"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","title":"Response Admin Convert Analytics Admin Convert Analytics Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/admin/send-trial-emails":{"post":{"tags":["admin"],"summary":"Send Trial Emails","description":"Find eligible users for a Free or Integration-trial drip stage and send the email.\n\nReplaces the legacy `/send-activation-reminders` endpoint. Each stage has its own\nSQL eligibility window (see `_TRIAL_STAGE_WINDOWS`) and is idempotent via the\n`funnel_events` table (event name `trial_email_{stage}`).\n\nPilot users (`integration_pilot` entitlement) and paid users are NEVER targeted —\nthey have dedicated channels.","operationId":"send_trial_emails_admin_send_trial_emails_post","parameters":[{"name":"stage","in":"query","required":true,"schema":{"type":"string","pattern":"^(free_j3_no_call|free_j7_upsell|trial_j3_no_call|trial_j5_post_conversion|trial_j12_remaining|trial_j14_last_day|trial_j16_post_expiration|trial_j30_last_chance)$","description":"Drip stage to process","title":"Stage"},"description":"Drip stage to process"},{"name":"dry_run","in":"query","required":false,"schema":{"type":"boolean","description":"If true, return eligible users without sending","default":true,"title":"Dry Run"},"description":"If true, return eligible users without sending"},{"name":"max_send","in":"query","required":false,"schema":{"type":"integer","maximum":200,"minimum":1,"description":"Max emails to send per call","default":50,"title":"Max Send"},"description":"Max emails to send per call"},{"name":"x-admin-token","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Admin-Token"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","title":"Response Send Trial Emails Admin Send Trial Emails Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/admin/send-credit-announcement":{"post":{"tags":["admin"],"summary":"Send Credit Announcement","description":"Send credit pack announcement to specific users.\n\nTiers:\n- personalized: for high-intent business users (recent, exhausted)\n- informative: for business users (moderate usage)\n- generic: for personal email users (high usage)\n\nDry run by default. Set dry_run=false to send.","operationId":"send_credit_announcement_admin_send_credit_announcement_post","parameters":[{"name":"emails","in":"query","required":true,"schema":{"type":"array","items":{"type":"string"},"description":"List of recipient emails","title":"Emails"},"description":"List of recipient emails"},{"name":"tier","in":"query","required":false,"schema":{"type":"string","description":"Email tier: personalized, informative, generic","default":"generic","title":"Tier"},"description":"Email tier: personalized, informative, generic"},{"name":"dry_run","in":"query","required":false,"schema":{"type":"boolean","description":"If true, show what would be sent without sending","default":true,"title":"Dry Run"},"description":"If true, show what would be sent without sending"},{"name":"x-admin-token","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Admin-Token"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","title":"Response Send Credit Announcement Admin Send Credit Announcement Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/admin/stripe/failed-events":{"get":{"tags":["admin-stripe"],"summary":"List failed Stripe events","description":"Get a list of Stripe events that failed processing and can be replayed.","operationId":"get_failed_events_admin_stripe_failed_events_get","parameters":[{"name":"limit","in":"query","required":false,"schema":{"type":"integer","default":100,"title":"Limit"}},{"name":"x-admin-token","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Admin-Token"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FailedEventsResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/admin/stripe/events":{"get":{"tags":["admin-stripe"],"summary":"List Stripe events","description":"Get a list of recent Stripe events with optional filters.","operationId":"list_events_admin_stripe_events_get","parameters":[{"name":"limit","in":"query","required":false,"schema":{"type":"integer","default":50,"title":"Limit"}},{"name":"event_type","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Event Type"}},{"name":"status","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Status"}},{"name":"x-admin-token","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Admin-Token"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/StripeEventsResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/admin/stripe/subscriptions":{"get":{"tags":["admin-stripe"],"summary":"Subscription health dashboard","description":"Live view of all Stripe subscriptions with status, renewal dates, and payment info.","operationId":"admin_subscriptions_admin_stripe_subscriptions_get","parameters":[{"name":"x-admin-token","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Admin-Token"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","title":"Response Admin Subscriptions Admin Stripe Subscriptions Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/admin/stripe/replay-event":{"post":{"tags":["admin-stripe"],"summary":"Replay a failed Stripe event","description":"DISABLED: Replay functionality requires storing original Stripe payloads.","operationId":"replay_single_event_admin_stripe_replay_event_post","parameters":[{"name":"x-admin-token","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Admin-Token"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReplayEventRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/admin/stripe/replay-all-failed":{"post":{"tags":["admin-stripe"],"summary":"Replay all failed Stripe events","description":"DISABLED: Replay functionality requires storing original Stripe payloads.","operationId":"replay_all_failed_events_admin_stripe_replay_all_failed_post","parameters":[{"name":"limit","in":"query","required":false,"schema":{"type":"integer","default":100,"title":"Limit"}},{"name":"x-admin-token","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Admin-Token"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/validate":{"post":{"tags":["validation"],"summary":"Valider une facture Factur-X","description":"Valide une facture électronique au format **Factur-X** (PDF ou XML).\n\n## 🔐 Authentification\n\nCet endpoint supporte trois méthodes d'authentification :\n\n1. **JWT Dashboard** : `Authorization: Bearer <jwt>` (utilisateurs connectés)\n2. **API Key** : `X-API-Key: <key>` ou `Authorization: Bearer <api_key>` (clients externes)\n3. **Refresh Token** : Cookie `refresh_token` (renouvellement automatique)\n\n## 📥 Formats supportés\n\n- **PDF Factur-X** : PDF avec XML embarqué conforme EN16931\n- **XML standalone** : Fichier XML Factur-X seul\n\n## ✅ Validations effectuées\n\n1. **Validation PDF/A-3** : Vérifie la conformité PDF/A-3 (si PDF)\n2. **Validation structure Factur-X** : Vérifie les pièces jointes et métadonnées XMP (si PDF)\n3. **Validation XSD** : Vérifie la structure XML contre le schéma XSD\n4. **Validation Schematron** : Vérifie les règles métier EN16931\n\n## 📤 Réponses possibles\n\n### Succès (200 OK) - Format unifié (par défaut)\n```json\n{\n  \"valid\": true,\n  \"validatedAt\": \"2025-12-27T12:23:51.676602+00:00\",\n  \"filename\": \"invoice.xml\",\n  \"profile\": \"EN16931\",\n  \"durationMs\": 124,\n  \"summary\": { \"errorCount\": 0, \"warningCount\": 0 },\n  \"issues\": [],\n  \"stages\": {\n    \"pdfa\": { \"passed\": true, \"skipped\": true, \"skip_reason\": \"XML file - PDF/A validation not applicable\", \"error_count\": 0, \"warning_count\": 0, \"info_count\": 0, \"issues\": [] },\n    \"facturx_pdf\": { \"passed\": true, \"skipped\": true, \"skip_reason\": \"XML file - PDF checks not applicable\", \"error_count\": 0, \"warning_count\": 0, \"info_count\": 0, \"issues\": [] },\n    \"xsd\": { \"passed\": true, \"skipped\": false, \"skip_reason\": null, \"error_count\": 0, \"warning_count\": 0, \"info_count\": 0, \"issues\": [] },\n    \"schematron\": { \"passed\": true, \"skipped\": false, \"skip_reason\": null, \"error_count\": 0, \"warning_count\": 0, \"info_count\": 0, \"issues\": [] }\n  },\n  \"errors\": [],\n  \"warnings\": []\n}\n```\n\n### Succès (200 OK) - Format legacy (?legacy=true)\n```json\n{\n  \"valid\": true,\n  \"errors\": [],\n  \"warnings\": [],\n  \"profile\": \"EN16931\",\n  \"message\": \"Validation completed\"\n}\n```\n\n### Validation échouée (200 OK avec valid=false)\n```json\n{\n  \"valid\": false,\n  \"validatedAt\": \"2025-12-27T12:23:51.676602+00:00\",\n  \"filename\": \"invoice.xml\",\n  \"profile\": \"EN16931\",\n  \"durationMs\": 124,\n  \"summary\": { \"errorCount\": 1, \"warningCount\": 0 },\n  \"issues\": [\n    {\n      \"code\": \"XSD_VALIDATION_ERROR\",\n      \"severity\": \"error\",\n      \"stage\": \"xsd\",\n      \"message\": \"Missing required element: InvoiceNumber\",\n      \"line\": 42,\n      \"expected\": \"InvoiceNumber\",\n      \"actual\": null,\n      \"location\": \"line 42\",\n      \"ruleId\": null,\n      \"xpath\": \"/Invoice/ID\"\n    }\n  ],\n  \"stages\": {\n    \"pdfa\": { \"passed\": true, \"skipped\": true, \"skip_reason\": \"XML file - PDF/A validation not applicable\", \"error_count\": 0, \"warning_count\": 0, \"info_count\": 0, \"issues\": [] },\n    \"facturx_pdf\": { \"passed\": true, \"skipped\": true, \"skip_reason\": \"XML file - PDF checks not applicable\", \"error_count\": 0, \"warning_count\": 0, \"info_count\": 0, \"issues\": [] },\n    \"xsd\": { \"passed\": false, \"skipped\": false, \"skip_reason\": null, \"error_count\": 1, \"warning_count\": 0, \"info_count\": 0, \"issues\": [] },\n    \"schematron\": { \"passed\": true, \"skipped\": false, \"skip_reason\": null, \"error_count\": 0, \"warning_count\": 0, \"info_count\": 0, \"issues\": [] }\n  },\n  \"errors\": [],\n  \"warnings\": []\n}\n```\n\n### Erreur fichier vide (400 Bad Request)\n```json\n{\n  \"detail\": \"Empty file\"\n}\n```\n\n## 🌍 Internationalisation\n\nUtilisez le header **Accept-Language** pour obtenir les messages en français:\n- `Accept-Language: en` (par défaut)\n- `Accept-Language: fr`\n\n## 💾 Stockage\n\nLe fichier est stocké temporairement sur R2/stockage local pour traçabilité.","operationId":"validate_facturx_api_v1_validate_post","parameters":[{"name":"legacy","in":"query","required":false,"schema":{"type":"boolean","default":false,"title":"Legacy"}},{"name":"lang","in":"query","required":false,"schema":{"anyOf":[{"type":"string","pattern":"^(en|fr)$"},{"type":"null"}],"description":"Response language (en or fr). Falls back to Accept-Language header if not specified.","title":"Lang"},"description":"Response language (en or fr). Falls back to Accept-Language header if not specified."},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"x-api-key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Api-Key"}},{"name":"refresh_token","in":"cookie","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Refresh Token"}}],"requestBody":{"required":true,"content":{"multipart/form-data":{"schema":{"$ref":"#/components/schemas/Body_validate_facturx_api_v1_validate_post"}}}},"responses":{"200":{"description":"Validation effectuée (succès ou échec)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidationResponse"},"examples":{"valid":{"summary":"Facture valide","value":{"valid":true,"errors":[],"warnings":[],"message":"Validation completed"}},"invalid_xsd":{"summary":"Erreur XSD","value":{"valid":false,"errors":[{"type":"xsd_validation_error","message":"Element 'invoice': Missing child element(s). Expected is ( number ).","line":5}],"warnings":[],"message":"Validation failed"}},"invalid_schematron":{"summary":"Erreur Schematron (règles métier)","value":{"valid":false,"errors":[{"type":"schematron_failed_assert","message":"Invoice number is required"}],"warnings":[],"message":"Validation failed"}},"pdf_no_xml":{"summary":"PDF sans XML Factur-X","value":{"valid":false,"errors":[{"type":"pdf_processing_error","message":"Failed to process PDF: No Factur-X XML found in PDF"}],"warnings":[],"message":"Validation failed"}}}}}},"402":{"description":"Quota dépassé (quota_exceeded)"},"400":{"description":"Fichier vide ou invalide"},"413":{"description":"Fichier trop volumineux (file_too_large)"},"415":{"description":"Type de fichier non supporté (unsupported_media_type)"},"429":{"description":"Rate limit dépassé (rate_limit_exceeded)"},"503":{"description":"Validation engine indisponible (service_unavailable)"},"500":{"description":"Erreur serveur (stockage, traitement)"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/extract":{"post":{"tags":["extract"],"summary":"Extract Factur-X XML from PDF","description":"Extracts the embedded Factur-X XML from a PDF file.\n\n## Features\n\n1. **XML Extraction**: Extracts the embedded XML attachment from Factur-X PDFs\n2. **Profile Detection**: Automatically detects the Factur-X profile (MINIMUM, BASIC_WL, BASIC, EN16931, EXTENDED)\n3. **Optional Validation**: Validates the extracted XML against XSD/Schematron rules\n\n## Authentication\n\nRequires authentication via:\n- `X-API-Key: <api_key>` header, OR\n- `Authorization: Bearer <jwt>` header\n\n## Query Parameters\n\n- `validate=true`: Run full validation on extracted XML (doubles quota usage)\n- `lang=en|fr`: Language for response messages\n\n## Response\n\nReturns base64-encoded extracted XML with metadata:\n- `xml`: Base64-encoded XML content\n- `xmlSize`: Size of XML in bytes\n- `profile`: Detected Factur-X profile\n- `durationMs`: Extraction time in milliseconds\n- `validation`: Optional validation report (if validate=true)\n\n## Quota Usage\n\n- Default: 1 unit\n- With `validate=true`: 2 units","operationId":"extract_facturx_api_v1_extract_post","parameters":[{"name":"validate","in":"query","required":false,"schema":{"type":"boolean","description":"Run validation after extraction (2x quota)","default":false,"title":"Validate"},"description":"Run validation after extraction (2x quota)"},{"name":"lang","in":"query","required":false,"schema":{"anyOf":[{"type":"string","pattern":"^(en|fr)$"},{"type":"null"}],"description":"Response language (en or fr). Falls back to Accept-Language header.","title":"Lang"},"description":"Response language (en or fr). Falls back to Accept-Language header."},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"x-api-key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Api-Key"}},{"name":"refresh_token","in":"cookie","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Refresh Token"}}],"requestBody":{"required":true,"content":{"multipart/form-data":{"schema":{"$ref":"#/components/schemas/Body_extract_facturx_api_v1_extract_post"}}}},"responses":{"200":{"description":"XML extracted successfully","content":{"application/json":{"schema":{},"example":{"xml":"PD94bWwgdmVyc2lvbj0iMS4wIj8+...","xmlSize":2048,"profile":"EN16931","filename":"invoice.pdf","extractedAt":"2024-01-15T12:00:00Z","durationMs":42}}}},"400":{"description":"Invalid request","content":{"application/json":{"example":{"detail":"Empty file"}}}},"401":{"description":"Authentication required","content":{"application/json":{"example":{"error":"authentication_required","message":"Authentication is required for the extract endpoint."}}}},"402":{"description":"Quota exceeded"},"413":{"description":"File too large"},"415":{"description":"Unsupported media type (non-PDF file)"},"422":{"description":"PDF without Factur-X XML","content":{"application/json":{"example":{"error":"unextractable_pdf","message":"PDF does not contain embedded Factur-X XML attachment."}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"example":{"error":"rate_limit_exceeded","message":"Too many requests. Please try again later.","retry_after_seconds":30}}}},"503":{"description":"Service unavailable"}}}},"/api/v1/repair":{"post":{"tags":["repair"],"summary":"Repair Factur-X XML","description":"Repairs a Factur-X XML file to fix common EN16931 validation issues.\n\n## Three-Phase Workflow\n\n1. **Validate BEFORE**: Capture the initial validation state\n2. **Apply Repairs**: Apply deterministic repair rules\n3. **Validate AFTER**: Verify that repairs improved validity\n\n## Repair Rules Applied\n\n- **R001**: Date format correction (YYYY-MM-DD -> YYYYMMDD)\n- **R002**: Decimal format correction (comma -> dot separator)\n- **R005**: Namespace prefix/URI fixes\n- **R006**: Required schemeID attributes\n- **R007**: Currency code casing (eur -> EUR)\n- **R008**: Missing required elements (constraint-gated)\n\n## Authentication\n\nRequires authentication via:\n- `X-API-Key: <api_key>` header, OR\n- `Authorization: Bearer <jwt>` header\n\n## Idempotency\n\nUse `Idempotency-Key` header to ensure safe retries:\n- Same key + same payload = same response (cached)\n- Same key + different payload = 409 Conflict\n\n## Constraints\n\nControl repair behavior via the `constraints` form field (JSON):\n```json\n{\n  \"max_changes_allowed\": 10,\n  \"do_not_modify_amounts\": true,\n  \"do_not_invent_missing_parties\": true,\n  \"target_profile\": \"EN16931\"\n}\n```\n\n## Response\n\nReturns base64-encoded repaired XML with before/after validation reports\nand a diff summary of all changes made.","operationId":"repair_facturx_xml_api_v1_repair_post","parameters":[{"name":"target_profile","in":"query","required":false,"schema":{"anyOf":[{"type":"string","pattern":"^(MINIMUM|BASIC_WL|BASIC|EN16931|EXTENDED)$"},{"type":"null"}],"description":"Target Factur-X profile for repair. If not specified, auto-detected from XML.","title":"Target Profile"},"description":"Target Factur-X profile for repair. If not specified, auto-detected from XML."},{"name":"lang","in":"query","required":false,"schema":{"anyOf":[{"type":"string","pattern":"^(en|fr)$"},{"type":"null"}],"description":"Response language (en or fr). Falls back to Accept-Language header.","title":"Lang"},"description":"Response language (en or fr). Falls back to Accept-Language header."},{"name":"Idempotency-Key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Idempotency key for safe retries (recommended for production use)","title":"Idempotency-Key"},"description":"Idempotency key for safe retries (recommended for production use)"},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"x-api-key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Api-Key"}},{"name":"refresh_token","in":"cookie","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Refresh Token"}}],"requestBody":{"required":true,"content":{"multipart/form-data":{"schema":{"$ref":"#/components/schemas/Body_repair_facturx_xml_api_v1_repair_post"}}}},"responses":{"200":{"description":"XML repaired successfully","content":{"application/json":{"schema":{},"example":{"durationMs":124,"repaired_xml":"PD94bWwgdmVyc2lvbj0iMS4wIj8+...","diff_summary":[{"code":"format_corrected","path":"/rsm:CrossIndustryInvoice/.../ram:IssueDateTime","message":"Corrected date format from '2024-01-15' to '20240115'","beforeValue":"2024-01-15","afterValue":"20240115","ruleReference":"R001"}],"validation_before":{"valid":false,"summary":{"errorCount":2,"warningCount":0}},"validation_after":{"valid":true,"summary":{"errorCount":0,"warningCount":0}}}}}},"400":{"description":"Invalid request","content":{"application/json":{"examples":{"empty_file":{"value":{"detail":"Empty file"}},"invalid_constraints":{"value":{"error":"invalid_constraints_json","message":"Malformed JSON in constraints: ..."}}}}}},"402":{"description":"Quota exceeded"},"409":{"description":"Idempotency conflict","content":{"application/json":{"example":{"error":"idempotency_conflict","message":"Idempotency key was already used with a different request payload. Use a new Idempotency-Key for different requests."}}}},"413":{"description":"File too large"},"415":{"description":"Unsupported media type (non-XML file)"},"422":{"description":"Unprocessable entity","content":{"application/json":{"examples":{"xml_parse_error":{"value":{"error":"xml_parse_error","message":"XML syntax error: ..."}},"xml_security_error":{"value":{"error":"xml_security_error","message":"XML security violation: potential XXE attack detected"}},"constraint_exceeded":{"value":{"error":"repair_constraint_exceeded","message":"Repair would exceed max_changes_allowed limit"}}}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"example":{"error":"rate_limit_exceeded","message":"Too many requests. Please try again later.","retry_after_seconds":30}}}},"503":{"description":"Service unavailable"}}}},"/api/v1/convert/analyze":{"post":{"tags":["convert","convert"],"summary":"Analyze PDF before conversion (no credits consumed)","description":"Lightweight pre-conversion analysis: validates the PDF, detects scan/native text,\ncounts pages, and estimates the credit cost — **without consuming any credits**.\n\nUse this before `/convert` to show users what will happen and how much it will cost.","operationId":"analyze_before_convert_api_v1_convert_analyze_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"x-api-key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Api-Key"}},{"name":"refresh_token","in":"cookie","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Refresh Token"}}],"requestBody":{"required":true,"content":{"multipart/form-data":{"schema":{"$ref":"#/components/schemas/Body_analyze_before_convert_api_v1_convert_analyze_post"}}}},"responses":{"200":{"description":"Analysis result with cost estimate","content":{"application/json":{"schema":{}}}},"400":{"description":"Invalid request (empty file)"},"401":{"description":"Authentication required"},"413":{"description":"File too large"},"415":{"description":"Unsupported file type (not PDF)"},"429":{"description":"Rate limit exceeded"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/convert":{"post":{"tags":["convert"],"summary":"Convert PDF to Factur-X PDF/A-3","description":"Converts a PDF invoice to a Factur-X compliant PDF/A-3 document.\n\n## Conversion Workflow\n\n1. **Scan Detection**: Determines if PDF is native text or scanned image\n2. **Data Extraction**: Extracts invoice data (text parsing or OCR)\n3. **XML Generation**: Creates EN16931-compliant Factur-X XML\n4. **PDF/A-3 Generation**: Embeds XML into PDF/A-3 format\n5. **Validation**: Validates the complete Factur-X document\n\n## Authentication\n\nRequires authentication via:\n- `X-API-Key: <api_key>` header, OR\n- `Authorization: Bearer <jwt>` header\n\n## Idempotency\n\nUse `Idempotency-Key` header to ensure safe retries:\n- Same key + same payload = same response (cached)\n- Same key + different payload = 409 Conflict\n\n## Options\n\nControl conversion behavior via the `options` form field (JSON):\n```json\n{\n  \"output_profile\": \"EN16931\",\n  \"lang\": \"en\",\n  \"confidence_mode\": \"strict\",\n  \"seller_hint\": \"FR\",\n  \"buyer_hint\": \"DE\"\n}\n```\n\n### Confidence Mode\n- `strict` (default): Reject OCR results with confidence < 70%\n- `lenient`: Accept low confidence with warning in response\n\n## Response\n\nReturns the generated Factur-X PDF/A-3 with embedded XML and validation report.\nPDF is base64-encoded in the response. Use the `xml` field for the embedded XML.\n\n## Quota Usage\n\n- Native text PDF: 5 units\n- Scanned PDF (OCR): 5-15 units based on page count","operationId":"convert_invoice_api_v1_convert_post","parameters":[{"name":"lang","in":"query","required":false,"schema":{"anyOf":[{"type":"string","pattern":"^(en|fr)$"},{"type":"null"}],"description":"Response language (en or fr). Falls back to Accept-Language header.","title":"Lang"},"description":"Response language (en or fr). Falls back to Accept-Language header."},{"name":"Idempotency-Key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Idempotency key for safe retries (recommended for production use)","title":"Idempotency-Key"},"description":"Idempotency key for safe retries (recommended for production use)"},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"x-api-key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Api-Key"}},{"name":"refresh_token","in":"cookie","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Refresh Token"}}],"requestBody":{"required":true,"content":{"multipart/form-data":{"schema":{"$ref":"#/components/schemas/Body_convert_invoice_api_v1_convert_post"}}}},"responses":{"200":{"description":"PDF converted successfully","content":{"application/json":{"schema":{},"example":{"success":true,"result":{"durationMs":2500,"convertedAt":"2026-01-04T12:00:00+00:00","targetProfile":"EN16931","conversionSuccessful":true,"xmlSize":4096,"xml":"PD94bWwgdmVyc2lvbj0iMS4wIj8+...","pdf":"JVBERi0xLjQK...","pdfSize":102400,"extraction":{"sourceType":"native_text","pageCount":1},"validation":{"valid":true,"profile":"EN16931"}}}}}},"202":{"description":"Conversion accepted for async processing","content":{"application/json":{"example":{"success":true,"result":{"status":"processing","jobId":"2a65594d-7f60-431c-8e35-139a6eeb4cec"}}}}},"400":{"description":"Invalid request","content":{"application/json":{"examples":{"empty_file":{"value":{"detail":"Empty file"}},"invalid_options":{"value":{"error":"invalid_options_json","message":"Malformed JSON in options: ..."}}}}}},"401":{"description":"Authentication required","content":{"application/json":{"example":{"error":"authentication_required","message":"Authentication is required for the convert endpoint."}}}},"402":{"description":"Quota exceeded","content":{"application/json":{"example":{"error":"quota_exceeded","message":"You have reached the monthly quota for your current plan.","plan":"free","quota":100,"period_start":"2026-01-01T00:00:00Z"}}}},"409":{"description":"Idempotency conflict","content":{"application/json":{"examples":{"conflict":{"value":{"error":"idempotency_conflict","message":"Idempotency key was already used with a different request payload. Use a new Idempotency-Key for different requests."}},"in_progress":{"value":{"error":"idempotency_in_progress","message":"Operation with this idempotency key is currently in progress. Wait for the operation to complete before retrying.","processing_since":"2026-01-04T12:00:00Z"}}}}}},"413":{"description":"File too large"},"415":{"description":"Unsupported media type (non-PDF file)"},"422":{"description":"Unprocessable entity","content":{"application/json":{"examples":{"unextractable":{"value":{"error":"unextractable_pdf","message":"Unable to extract invoice data from PDF. The PDF may be corrupt, password-protected, or not contain invoice content."}},"low_confidence":{"value":{"error":"ocr_confidence_too_low","message":"OCR confidence (45.2%) is below threshold (70%). Use confidence_mode='lenient' to accept low-confidence results.","confidence":45.2,"threshold":70.0}},"insufficient_data":{"value":{"error":"insufficient_data","message":"Required invoice fields are missing.","missing_fields":["seller.name","invoice_number"]}},"invalid_format":{"value":{"error":"invalid_data_format","message":"Invoice data contains invalid format.","field":"issue_date"}},"validation_failed":{"value":{"error":"pdf_validation_failed","message":"Generated PDF failed compliance validation."}}}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"example":{"error":"rate_limit_exceeded","message":"Too many requests. Please try again later.","retry_after_seconds":30}}}},"503":{"description":"Service unavailable (OCR or PDF generation)","content":{"application/json":{"example":{"error":"ocr_service_unavailable","message":"OCR service is temporarily unavailable. Please try again later.","retry_after_seconds":60}}}},"504":{"description":"Timeout (OCR or PDF generation)","content":{"application/json":{"example":{"error":"ocr_timeout","message":"OCR processing timed out. The document may be too large or complex."}}}}}}},"/api/v1/convert/jobs/{job_id}":{"get":{"tags":["convert"],"summary":"Get convert job status","description":"Get status/result for a convert async job.","operationId":"get_convert_job_api_v1_convert_jobs__job_id__get","parameters":[{"name":"job_id","in":"path","required":true,"schema":{"type":"string","title":"Job Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"x-api-key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Api-Key"}},{"name":"refresh_token","in":"cookie","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Refresh Token"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/convert/jobs/{job_id}/artifacts/{artifact_name}":{"get":{"tags":["convert"],"summary":"Get signed URL for convert job artifact","description":"Return signed download URL for a completed convert job artifact.","operationId":"get_convert_job_artifact_api_v1_convert_jobs__job_id__artifacts__artifact_name__get","parameters":[{"name":"job_id","in":"path","required":true,"schema":{"type":"string","title":"Job Id"}},{"name":"artifact_name","in":"path","required":true,"schema":{"type":"string","title":"Artifact Name"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"x-api-key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Api-Key"}},{"name":"refresh_token","in":"cookie","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Refresh Token"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/convert/jobs/{job_id}/cancel":{"post":{"tags":["convert"],"summary":"Cancel convert job","description":"Cancel a queued/running convert job.","operationId":"cancel_convert_job_api_v1_convert_jobs__job_id__cancel_post","parameters":[{"name":"job_id","in":"path","required":true,"schema":{"type":"string","title":"Job Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"x-api-key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Api-Key"}},{"name":"refresh_token","in":"cookie","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Refresh Token"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/webhooks/stripe":{"post":{"tags":["webhooks"],"summary":"Stripe Webhook V1","description":"Handle Stripe webhook events (API v1 endpoint).\n\nMain webhook endpoint for Stripe events.\nAvailable at: POST /api/v1/webhooks/stripe","operationId":"stripe_webhook_v1_api_v1_webhooks_stripe_post","parameters":[{"name":"Stripe-Signature","in":"header","required":false,"schema":{"type":"string","title":"Stripe-Signature"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","title":"Response Stripe Webhook V1 Api V1 Webhooks Stripe Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/webhooks/stripe":{"post":{"tags":["webhooks-legacy"],"summary":"Stripe Webhook Legacy","description":"Handle Stripe webhook events (legacy endpoint).\n\nLegacy webhook endpoint maintained for backward compatibility.\nAvailable at: POST /webhooks/stripe\n\nNOTE: This endpoint is deprecated. Use /api/v1/webhooks/stripe instead.","operationId":"stripe_webhook_legacy_webhooks_stripe_post","parameters":[{"name":"Stripe-Signature","in":"header","required":false,"schema":{"type":"string","title":"Stripe-Signature"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","title":"Response Stripe Webhook Legacy Webhooks Stripe Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/public/api-keys/free/step1":{"post":{"tags":["public"],"summary":"Request a free API key (Step 1)","description":"Request a free API key for an email address.\n\n## Behavior depends on server configuration:\n\n### When email verification is ENABLED (default):\n**Step 1 of 2**: This endpoint sends a verification token to your email.\nUse the token with `POST /public/api-keys/free/step2` to complete the process.\n\n- **Uniform response**: Same response whether email is new, has a key, or is invalid\n  (prevents email enumeration attacks)\n- **Token expiry**: Verification tokens expire after 30 minutes\n- **Rate limiting**: 5 requests/hour, 10 requests/day per IP\n\n### When email verification is DISABLED:\n**Single step**: This endpoint immediately creates and returns your API key.\n\n- **Warning**: Returns 409 if email already has a key (reveals email existence)\n- **Use case**: Internal/trusted environments only\n- The `/free/step2` endpoint is not needed in this mode\n\n**Important**: Save the returned API key immediately - it cannot be retrieved later!","operationId":"request_free_api_key_public_api_keys_free_step1_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicFreeApiKeyRequest"}}},"required":true},"responses":{"200":{"description":"Success - either verification requested (2-step mode) or API key created (immediate mode)","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/VerificationRequestedResponse"},{"$ref":"#/components/schemas/ApiKeyCreatedResponse"}],"title":"Response Request Free Api Key Public Api Keys Free Step1 Post"},"examples":{"verification_enabled":{"summary":"Verification requested (2-step mode)","value":{"message":"If this email is valid, a verification code has been sent.","email":"dev@example.com","expires_in_seconds":1800}},"verification_disabled":{"summary":"API key created (immediate mode)","value":{"email":"dev@example.com","api_key":"fxk_abc123...","plan":{"code":"free","monthly_quota":10},"message":"API key created successfully."}}}}}},"409":{"description":"Email already has an active API key (only in immediate mode)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ConfirmationErrorResponse"}}}},"422":{"description":"Invalid email address","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidationErrorResponse"}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RateLimitErrorResponse"}}}},"500":{"description":"Server configuration error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ServerErrorResponse"}}}},"503":{"description":"Email service unavailable (only in 2-step mode)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ServiceUnavailableResponse"}}}}}}},"/public/api-keys/free/step2":{"post":{"tags":["public"],"summary":"Confirm and create API key (Step 2)","description":"**Step 2 of 2**: Confirm your email and receive your API key.\n\nUse the verification token from your email to complete the API key creation.\n\n## Important\n\n- The API key is returned **ONLY ONCE**. Save it immediately!\n- If you already have an active API key, you'll receive an error.\n- Tokens expire after 30 minutes.\n- If email verification is disabled on this server, returns 400 \"verification_not_required\".","operationId":"confirm_free_api_key_public_api_keys_free_step2_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ConfirmationRequest"}}},"required":true},"responses":{"201":{"description":"API key created successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiKeyCreatedResponse"}}}},"400":{"description":"Invalid or expired token, or verification not required","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ConfirmationErrorResponse"}}}},"409":{"description":"Email already has an active API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AlreadyHasActiveKeyResponse"}}}},"422":{"description":"Invalid request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidationErrorResponse"}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RateLimitErrorResponse"}}}},"500":{"description":"Server configuration error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ServerErrorResponse"}}}},"503":{"description":"Service unavailable","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ServiceUnavailableResponse"}}}}}}},"/public/api-keys/upgrade-trial":{"post":{"tags":["public"],"summary":"Activate the 15-day Integration trial for an existing Free user","description":"One-click upgrade endpoint hit by the Free J+7 upsell email (`/api-key?source=upgrade_from_free&email=...&activate_trial=true`). Idempotent: returns 409 if the user already has an active trial. Does not require auth — the magic-link-style activation is bound to the email pre-baked in our own outbound email.","operationId":"upgrade_trial_public_api_keys_upgrade_trial_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpgradeTrialRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpgradeTrialResponse"}}}},"404":{"description":"User not found or no active Free key"},"409":{"description":"Trial already active for this user"},"422":{"description":"Invalid email"},"429":{"description":"Rate limit exceeded"},"503":{"description":"Service unavailable"}}}},"/meta":{"get":{"tags":["meta"],"summary":"Public metadata (source of truth)","description":"Public, read-only metadata used as a single source of truth for versions, artefacts, limits, and plan quotas.","operationId":"get_meta_meta_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MetaResponse"}}}}}}},"/api/v1/issuer":{"put":{"tags":["invoicing-issuer"],"summary":"Upsert Issuer","description":"Create or update the issuer profile.","operationId":"upsert_issuer_api_v1_issuer_put","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"x-api-key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Api-Key"}},{"name":"refresh_token","in":"cookie","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Refresh Token"}},{"name":"session","in":"cookie","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Session"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/IssuerRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/IssuerResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"get":{"tags":["invoicing-issuer"],"summary":"Get Issuer","description":"Get the current user's issuer profile.","operationId":"get_issuer_api_v1_issuer_get","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"x-api-key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Api-Key"}},{"name":"refresh_token","in":"cookie","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Refresh Token"}},{"name":"session","in":"cookie","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Session"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/IssuerResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/clients":{"post":{"tags":["invoicing-clients"],"summary":"Create Client","description":"Create a new client.","operationId":"create_client_api_v1_clients_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"x-api-key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Api-Key"}},{"name":"refresh_token","in":"cookie","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Refresh Token"}},{"name":"session","in":"cookie","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Session"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ClientRequest"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ClientResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"get":{"tags":["invoicing-clients"],"summary":"List Clients","description":"List all clients for the current user.","operationId":"list_clients_api_v1_clients_get","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"x-api-key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Api-Key"}},{"name":"refresh_token","in":"cookie","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Refresh Token"}},{"name":"session","in":"cookie","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Session"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ClientListResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/clients/{client_id}":{"get":{"tags":["invoicing-clients"],"summary":"Get Client","description":"Get a client by ID.","operationId":"get_client_api_v1_clients__client_id__get","parameters":[{"name":"client_id","in":"path","required":true,"schema":{"type":"string","title":"Client Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"x-api-key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Api-Key"}},{"name":"refresh_token","in":"cookie","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Refresh Token"}},{"name":"session","in":"cookie","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Session"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ClientResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"put":{"tags":["invoicing-clients"],"summary":"Update Client","description":"Update a client.","operationId":"update_client_api_v1_clients__client_id__put","parameters":[{"name":"client_id","in":"path","required":true,"schema":{"type":"string","title":"Client Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"x-api-key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Api-Key"}},{"name":"refresh_token","in":"cookie","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Refresh Token"}},{"name":"session","in":"cookie","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Session"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ClientRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ClientResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"delete":{"tags":["invoicing-clients"],"summary":"Delete Client","description":"Delete a client.","operationId":"delete_client_api_v1_clients__client_id__delete","parameters":[{"name":"client_id","in":"path","required":true,"schema":{"type":"string","title":"Client Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"x-api-key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Api-Key"}},{"name":"refresh_token","in":"cookie","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Refresh Token"}},{"name":"session","in":"cookie","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Session"}}],"responses":{"204":{"description":"Successful Response"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/invoices/access-check":{"get":{"tags":["invoicing-invoices"],"summary":"Check Invoicing Access","description":"Check if the current user has invoicing beta access.\n\nReturns {\"has_access\": true/false}. Used by the frontend to show/hide\nthe invoicing navigation section. Does NOT raise 403.","operationId":"check_invoicing_access_api_v1_invoices_access_check_get","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"x-api-key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Api-Key"}},{"name":"refresh_token","in":"cookie","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Refresh Token"}},{"name":"session","in":"cookie","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Session"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","title":"Response Check Invoicing Access Api V1 Invoices Access Check Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/invoices":{"post":{"tags":["invoicing-invoices"],"summary":"Create Invoice","description":"Create a new draft invoice with lines.","operationId":"create_invoice_api_v1_invoices_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"x-api-key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Api-Key"}},{"name":"refresh_token","in":"cookie","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Refresh Token"}},{"name":"session","in":"cookie","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Session"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateInvoiceRequest"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/InvoiceResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"get":{"tags":["invoicing-invoices"],"summary":"List Invoices","description":"List invoices for the current user.","operationId":"list_invoices_api_v1_invoices_get","parameters":[{"name":"invoice_status","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Invoice Status"}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","default":50,"title":"Limit"}},{"name":"offset","in":"query","required":false,"schema":{"type":"integer","default":0,"title":"Offset"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"x-api-key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Api-Key"}},{"name":"refresh_token","in":"cookie","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Refresh Token"}},{"name":"session","in":"cookie","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Session"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/InvoiceListResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/invoices/{invoice_id}":{"get":{"tags":["invoicing-invoices"],"summary":"Get Invoice","description":"Get an invoice with lines.","operationId":"get_invoice_api_v1_invoices__invoice_id__get","parameters":[{"name":"invoice_id","in":"path","required":true,"schema":{"type":"string","title":"Invoice Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"x-api-key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Api-Key"}},{"name":"refresh_token","in":"cookie","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Refresh Token"}},{"name":"session","in":"cookie","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Session"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/InvoiceResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"put":{"tags":["invoicing-invoices"],"summary":"Update Invoice","description":"Update a draft invoice. Rejects if not DRAFT.","operationId":"update_invoice_api_v1_invoices__invoice_id__put","parameters":[{"name":"invoice_id","in":"path","required":true,"schema":{"type":"string","title":"Invoice Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"x-api-key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Api-Key"}},{"name":"refresh_token","in":"cookie","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Refresh Token"}},{"name":"session","in":"cookie","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Session"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateInvoiceRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/InvoiceResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/invoices/{invoice_id}/issue":{"post":{"tags":["invoicing-invoices"],"summary":"Issue Invoice","description":"Issue a draft invoice — generates Factur-X PDF/A-3, assigns number.\n\nThis is the point of no return. The invoice becomes immutable.","operationId":"issue_invoice_api_v1_invoices__invoice_id__issue_post","parameters":[{"name":"invoice_id","in":"path","required":true,"schema":{"type":"string","title":"Invoice Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"x-api-key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Api-Key"}},{"name":"refresh_token","in":"cookie","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Refresh Token"}},{"name":"session","in":"cookie","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Session"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/IssueResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/invoices/{invoice_id}/pdf":{"get":{"tags":["invoicing-invoices"],"summary":"Download Pdf","description":"Download the issued invoice as Factur-X PDF.\n\nFor M1, re-generates the PDF on the fly (no R2 storage yet).","operationId":"download_pdf_api_v1_invoices__invoice_id__pdf_get","parameters":[{"name":"invoice_id","in":"path","required":true,"schema":{"type":"string","title":"Invoice Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"x-api-key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Api-Key"}},{"name":"refresh_token","in":"cookie","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Refresh Token"}},{"name":"session","in":"cookie","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Session"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/invoices/{invoice_id}/preview":{"post":{"tags":["invoicing-invoices"],"summary":"Preview Invoice","description":"Generate a draft PDF preview (with BROUILLON watermark, no number).","operationId":"preview_invoice_api_v1_invoices__invoice_id__preview_post","parameters":[{"name":"invoice_id","in":"path","required":true,"schema":{"type":"string","title":"Invoice Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"x-api-key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Api-Key"}},{"name":"refresh_token","in":"cookie","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Refresh Token"}},{"name":"session","in":"cookie","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Session"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/invoices/{invoice_id}/paid":{"post":{"tags":["invoicing-invoices"],"summary":"Mark Paid","description":"Mark an issued invoice as paid.","operationId":"mark_paid_api_v1_invoices__invoice_id__paid_post","parameters":[{"name":"invoice_id","in":"path","required":true,"schema":{"type":"string","title":"Invoice Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"x-api-key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Api-Key"}},{"name":"refresh_token","in":"cookie","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Refresh Token"}},{"name":"session","in":"cookie","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Session"}}],"requestBody":{"content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/MarkPaidRequest"},{"type":"null"}],"title":"Payload"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/InvoiceResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/invoices/{invoice_id}/credit-note":{"post":{"tags":["invoicing-invoices"],"summary":"Create Credit Note","description":"Create a credit note (avoir) linked to an issued invoice.","operationId":"create_credit_note_api_v1_invoices__invoice_id__credit_note_post","parameters":[{"name":"invoice_id","in":"path","required":true,"schema":{"type":"string","title":"Invoice Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"x-api-key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Api-Key"}},{"name":"refresh_token","in":"cookie","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Refresh Token"}},{"name":"session","in":"cookie","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Session"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateCreditNoteRequest"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/InvoiceResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/internal/pay/{invoice_id}":{"get":{"tags":["pay"],"summary":"Get payment page data (public, HMAC-secured)","description":"Public endpoint consumed by the Astro SSR payment page.\n\n**Security**: HMAC-SHA256(invoice_id, PAYMENT_TOKEN_SECRET) == t\n- Resolves by invoice UUID (globally unique — no cross-tenant collision).\n- Returns 404 for any failure — no distinction to prevent information leaks.\n- DRAFT invoices are not accessible via the public payment page.","operationId":"get_payment_page_api_v1_internal_pay__invoice_id__get","parameters":[{"name":"invoice_id","in":"path","required":true,"schema":{"type":"string","title":"Invoice Id"}},{"name":"t","in":"query","required":false,"schema":{"type":"string","default":"","title":"T"}}],"responses":{"200":{"description":"Payment page data","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PaymentPageResponse"}}}},"404":{"description":"Not found (invalid/missing token, unknown invoice, or DRAFT)"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/internal/pay/{invoice_id}/event":{"post":{"tags":["pay"],"summary":"Record payment analytics event (public, HMAC-secured)","operationId":"record_payment_event_api_v1_internal_pay__invoice_id__event_post","parameters":[{"name":"invoice_id","in":"path","required":true,"schema":{"type":"string","title":"Invoice Id"}},{"name":"t","in":"query","required":false,"schema":{"type":"string","default":"","title":"T"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PaymentEventRequest"}}}},"responses":{"204":{"description":"Event recorded"},"404":{"description":"Not found (invalid or missing token)"},"422":{"description":"Unknown event type"}}}},"/api/v1/internal/pay/{invoice_id}/pdf":{"get":{"tags":["pay"],"summary":"Download invoice PDF (public, HMAC-secured)","description":"Download the issued Factur-X PDF via the payment page.\nHMAC-secured: no user auth required.\n\nThe PDF is rebuilt from frozen snapshots (issuer_snapshot, client_snapshot) — it\nalways matches the document as-issued, never live post-issuance data.\nReturns 404 if snapshots are absent to preserve document integrity.","operationId":"download_payment_pdf_api_v1_internal_pay__invoice_id__pdf_get","parameters":[{"name":"invoice_id","in":"path","required":true,"schema":{"type":"string","title":"Invoice Id"}},{"name":"t","in":"query","required":false,"schema":{"type":"string","default":"","title":"T"}}],"responses":{"200":{"description":"Factur-X PDF/A-3 document","content":{"application/json":{"schema":{}}}},"404":{"description":"Not found (invalid token, draft, or snapshot absent)"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/auth/request-magic-link":{"post":{"tags":["auth"],"summary":"Request a magic link for authentication","description":"Request a magic link to be sent to your email address.\n\n## Security\n- **Rate limited**: 5 requests/hour, 10 requests/day per IP\n- **Uniform response**: Same response whether email exists or not (prevents enumeration)\n- **Token expiry**: Magic links expire after 15 minutes\n\n## Flow\n1. Submit your email\n2. If valid, a magic link is sent to your inbox\n3. Click the link to authenticate\n4. Use the `/auth/verify-magic-link` endpoint with the token","operationId":"request_magic_link_auth_request_magic_link_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MagicLinkRequest"}}},"required":true},"responses":{"200":{"description":"Magic link requested (uniform response)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MagicLinkResponse"}}}},"429":{"description":"Rate limit exceeded"},"503":{"description":"Service unavailable (email or database)"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/auth/verify-magic-link":{"post":{"tags":["auth"],"summary":"Verify magic link token and create session (V2)","description":"Verify a magic link token and create an authenticated session.\n\n## Auth V2 Response\n- **access_token**: JWT (15 min) - Store in memory, use in Authorization: Bearer\n- **refresh_token**: Set as HttpOnly cookie (30 days) - Browser handles automatically\n\n## Security\n- **Single-use**: Magic link token is invalidated after use\n- **Expiration**: Token must not be expired (15 min default)\n- **Access Token**: Short-lived, stored in memory (not localStorage!)\n- **Refresh Token**: HttpOnly cookie, SameSite=Lax, Secure\n\n## Flow\n1. Extract token from magic link URL\n2. Submit to this endpoint\n3. Store access_token in memory (React state/singleton)\n4. Use access_token for `/dashboard/*` endpoints\n5. When access_token expires, call `/auth/refresh`","operationId":"verify_magic_link_auth_verify_magic_link_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/VerifyMagicLinkRequest"}}},"required":true},"responses":{"200":{"description":"Login successful, access_token in body, refresh_token in cookie","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SessionResponseV2"}}}},"400":{"description":"Invalid or expired token"},"403":{"description":"User account is suspended or deleted"},"503":{"description":"Service unavailable"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/auth/refresh":{"post":{"tags":["auth"],"summary":"Refresh access token (V2)","description":"Exchange a valid refresh token for a new access token.\n\n## Token Rotation\n- The refresh token is **rotated** on each use (new token issued)\n- Old refresh token is invalidated\n- New refresh token set via HttpOnly cookie\n\n## Security\n- Detects token reuse (potential theft) and revokes session\n- Verifies user is still active (not suspended/deleted)\n\n## Usage\n```javascript\n// Automatic: browser sends refresh_token cookie\nconst response = await fetch('/auth/refresh', { method: 'POST', credentials: 'include' });\nconst { access_token, expires_in } = await response.json();\n// Store access_token in memory, use for next 15 minutes\n```","operationId":"refresh_token_auth_refresh_post","parameters":[{"name":"refresh_token","in":"cookie","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Refresh Token"}}],"responses":{"200":{"description":"New access token issued, refresh token rotated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RefreshResponse"}}}},"401":{"description":"Invalid, expired, or revoked refresh token"},"503":{"description":"Service unavailable"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/auth/logout":{"post":{"tags":["auth"],"summary":"Log out current session","description":"Log out the current session and clear cookies.\n\n## Behavior\n- Revokes the current session in DB (refresh token invalidated)\n- Clears refresh_token cookie\n- Clears V1 session cookie (backward compatibility)\n\n## Note\nThis endpoint works even without a valid session - it will clear cookies regardless.\nFor authenticated logout with user context, use with Authorization header.","operationId":"logout_auth_logout_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"refresh_token","in":"cookie","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Refresh Token"}}],"responses":{"200":{"description":"Logged out successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/LogoutResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/auth/logout-all":{"post":{"tags":["auth"],"summary":"Log out all sessions","description":"Revoke all active sessions for the current user.\n\n## Use Cases\n- Security event (password change, suspicious activity)\n- User wants to log out from all devices\n- Account deletion preparation\n\n## Requires\n- Valid access token in Authorization: Bearer header","operationId":"logout_all_auth_logout_all_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"All sessions revoked","content":{"application/json":{"schema":{"$ref":"#/components/schemas/LogoutAllResponse"}}}},"401":{"description":"Not authenticated"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/dashboard/me":{"get":{"tags":["dashboard"],"summary":"Get current user profile","description":"Get the authenticated user's profile information including plan and verification status.","operationId":"get_me_dashboard_me_get","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"session","in":"cookie","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Session"}}],"responses":{"200":{"description":"User profile","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DashboardMeResponse"}}}},"401":{"description":"Not authenticated"},"403":{"description":"User suspended"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/dashboard/keys":{"get":{"tags":["dashboard"],"summary":"List user's API keys","description":"Get all API keys belonging to the authenticated user.","operationId":"list_keys_dashboard_keys_get","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"session","in":"cookie","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Session"}}],"responses":{"200":{"description":"List of API keys","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DashboardKeysListResponse"}}}},"401":{"description":"Not authenticated"},"503":{"description":"Database unavailable"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/dashboard/keys/create":{"post":{"tags":["dashboard"],"summary":"Create a new API key","description":"Create a new API key for the authenticated user.\n\n**Important**: The API key is returned only once. Save it immediately!\n\n## Limits\n- Free plan: Only 1 active key allowed\n- Paid plans: Multiple keys allowed","operationId":"create_key_dashboard_keys_create_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"session","in":"cookie","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Session"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateKeyRequest"}}}},"responses":{"201":{"description":"API key created","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateKeyResponse"}}}},"400":{"description":"Invalid request (e.g., free key limit reached)"},"401":{"description":"Not authenticated"},"503":{"description":"Database unavailable"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/dashboard/keys/revoke":{"post":{"tags":["dashboard"],"summary":"Revoke an API key","description":"Revoke an API key belonging to the authenticated user.","operationId":"revoke_key_dashboard_keys_revoke_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"session","in":"cookie","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Session"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RevokeKeyRequest"}}}},"responses":{"204":{"description":"API key revoked"},"400":{"description":"Invalid request"},"401":{"description":"Not authenticated"},"404":{"description":"API key not found"},"503":{"description":"Database unavailable"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/dashboard/keys/rename":{"post":{"tags":["dashboard"],"summary":"Rename an API key","description":"Update the label of an API key belonging to the authenticated user.","operationId":"rename_key_dashboard_keys_rename_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"session","in":"cookie","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Session"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RenameKeyRequest"}}}},"responses":{"200":{"description":"API key renamed","content":{"application/json":{"schema":{"type":"object","additionalProperties":{"type":"string"},"title":"Response Rename Key Dashboard Keys Rename Post"}}}},"400":{"description":"Invalid request"},"401":{"description":"Not authenticated"},"404":{"description":"API key not found"},"503":{"description":"Database unavailable"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/dashboard/usage":{"get":{"tags":["dashboard"],"summary":"Get current usage statistics","description":"Get the authenticated user's current monthly usage statistics.","operationId":"get_usage_dashboard_usage_get","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"session","in":"cookie","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Session"}}],"responses":{"200":{"description":"Current usage statistics","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DashboardUsageResponse"}}}},"401":{"description":"Not authenticated"},"503":{"description":"Database unavailable"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/dashboard/feedback":{"post":{"tags":["dashboard"],"summary":"Submit Feedback","description":"Submit contextual feedback from dashboard UI.\n\nLogs the feedback as a funnel event and records it in the\nfeedback_prompt_log for frequency cap analysis.\n\nDesigned for ultra-lightweight, non-intrusive feedback collection:\n- One question per moment\n- Never blocking\n- Optional free text","operationId":"submit_feedback_dashboard_feedback_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"session","in":"cookie","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Session"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SubmitFeedbackRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FeedbackResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/internal/debug/session":{"get":{"tags":["internal"],"summary":"Debug current session (DEBUG mode only)","description":"Decode and return the JWT claims from the current session cookie.\n\n**SECURITY**: This endpoint is only available when DEBUG=true.\nIn production, it returns 404.\n\nUseful for:\n- Debugging cookie issues during frontend development\n- Verifying JWT claims and expiration\n- Testing authentication flow","operationId":"debug_session_internal_debug_session_get","parameters":[{"name":"session","in":"cookie","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Session"}}],"responses":{"200":{"description":"Session claims decoded","content":{"application/json":{"schema":{"type":"object","title":"Response Debug Session Internal Debug Session Get"}}}},"401":{"description":"No session or invalid session"},"404":{"description":"Endpoint not available (DEBUG=false)"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/internal/debug/config":{"get":{"tags":["internal"],"summary":"Debug configuration (DEBUG mode only)","description":"Show non-sensitive configuration values for debugging.","operationId":"debug_config_internal_debug_config_get","responses":{"200":{"description":"Configuration overview","content":{"application/json":{"schema":{"type":"object","title":"Response Debug Config Internal Debug Config Get"}}}},"404":{"description":"Endpoint not available (DEBUG=false)"}}}},"/":{"get":{"summary":"Root","description":"Root endpoint - API information.","operationId":"root__get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"additionalProperties":{"type":"string"},"type":"object","title":"Response Root  Get"}}}}}}},"/health":{"get":{"summary":"Health","description":"Health check endpoint with database ping.\n\nReturns:\n    - {\"status\": \"ok\", \"db\": \"ok\"} if all systems operational\n    - {\"status\": \"degraded\", \"db\": \"error\"} with 503 if DB unavailable","operationId":"health_health_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}}},"components":{"schemas":{"AlreadyHasActiveKeyResponse":{"properties":{"detail":{"type":"string","title":"Detail","description":"Error code","default":"email_already_has_active_key"},"error_code":{"type":"string","title":"Error Code","description":"Stable error code","default":"email_already_has_active_key"},"message_human":{"type":"string","title":"Message Human","description":"Human-friendly guidance","default":"Une clé API gratuite est déjà active pour cet email."},"next_steps":{"items":{"$ref":"#/components/schemas/NextStepAction"},"type":"array","title":"Next Steps","description":"Recommended next actions"}},"type":"object","title":"AlreadyHasActiveKeyResponse","description":"Structured response for existing key conflicts.","example":{"detail":"email_already_has_active_key","error_code":"email_already_has_active_key","message_human":"Une clé API gratuite est déjà active pour cet email.","next_steps":[{"href":"/login","label":"Accéder au tableau de bord"},{"action":"magic_link_resend","label":"Renvoyer un email de connexion"}]}},"ApiKeyCreateRequest":{"properties":{"user_email":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"User Email","description":"Optional owner email for traceability"},"label":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Label","description":"Optional label to identify the key"}},"type":"object","title":"ApiKeyCreateRequest","description":"Request payload for creating an API key."},"ApiKeyCreateResponse":{"properties":{"api_key":{"type":"string","title":"Api Key","description":"Use this value as Authorization Bearer or x-api-key"},"key":{"$ref":"#/components/schemas/ApiKeyPublic"}},"type":"object","required":["api_key","key"],"title":"ApiKeyCreateResponse","description":"Response payload containing the generated key (secret)."},"ApiKeyCreatedResponse":{"properties":{"email":{"type":"string","title":"Email","description":"The email address"},"apiKey":{"type":"string","title":"Apikey","description":"The API key. SAVE THIS NOW - it cannot be retrieved later!"},"plan":{"allOf":[{"$ref":"#/components/schemas/app__application__dto__api_keys__PlanInfo"}],"description":"Plan information"},"createdAt":{"type":"string","title":"Createdat","description":"ISO 8601 timestamp when the key was created"},"message":{"type":"string","title":"Message","description":"Success message","default":"API key created successfully. Save it now - it cannot be retrieved later!"}},"type":"object","required":["email","apiKey","plan","createdAt"],"title":"ApiKeyCreatedResponse","description":"Response for Step 2: API key created successfully.","example":{"apiKey":"fxk_abc123...","createdAt":"2025-12-03T14:50:00Z","email":"dev@example.com","message":"API key created successfully. Save it now - it cannot be retrieved later!","plan":{"code":"free","monthly_quota":10}}},"ApiKeyListResponse":{"properties":{"keys":{"items":{"$ref":"#/components/schemas/ApiKeyPublic"},"type":"array","title":"Keys"}},"type":"object","required":["keys"],"title":"ApiKeyListResponse","description":"List of keys."},"ApiKeyMe":{"properties":{"id":{"type":"string","title":"Id"},"label":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Label"},"status":{"type":"string","title":"Status"},"plan":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Plan"},"created_at":{"type":"string","format":"date-time","title":"Created At"},"revoked":{"type":"boolean","title":"Revoked","default":false}},"type":"object","required":["id","status","created_at"],"title":"ApiKeyMe","description":"Public API key representation for /me endpoints."},"ApiKeyPublic":{"properties":{"id":{"type":"string","title":"Id"},"label":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Label"},"user_email":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"User Email"},"created_at":{"type":"string","format":"date-time","title":"Created At"},"revoked":{"type":"boolean","title":"Revoked","default":false}},"type":"object","required":["id","created_at"],"title":"ApiKeyPublic","description":"Public metadata for an API key (no secret)."},"ApiKeyRevokeRequest":{"properties":{"api_key":{"type":"string","title":"Api Key","description":"API key to revoke (plaintext)"}},"type":"object","required":["api_key"],"title":"ApiKeyRevokeRequest","description":"Request payload to revoke a key."},"ApiKeyRevokeResponse":{"properties":{"revoked":{"type":"boolean","title":"Revoked"}},"type":"object","required":["revoked"],"title":"ApiKeyRevokeResponse","description":"Response after revocation."},"ApiKeyRotateRequest":{"properties":{"user_email":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"User Email","description":"Optional owner email for the new key"},"label":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Label","description":"Optional label to identify the new key"}},"type":"object","title":"ApiKeyRotateRequest","description":"Request payload for rotating a key."},"AuditLogEntry":{"properties":{"id":{"type":"string","title":"Id","description":"Audit log entry ID"},"created_at":{"type":"string","title":"Created At","description":"Event timestamp (ISO 8601)"},"user_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"User Id","description":"User ID (if authenticated)"},"user_email":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"User Email","description":"User email (if authenticated)"},"ip_address":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Ip Address","description":"Client IP address"},"action":{"type":"string","title":"Action","description":"Action performed"},"details":{"type":"object","title":"Details","description":"Action details"},"resource_type":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Resource Type","description":"Resource type affected"},"resource_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Resource Id","description":"Resource ID affected"},"status":{"type":"string","title":"Status","description":"Action status (success, failure, error)"},"error_code":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Error Code","description":"Error code if failed"},"user_agent":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"User Agent","description":"User agent string"},"latency_ms":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Latency Ms","description":"Request latency in milliseconds"}},"type":"object","required":["id","created_at","action","status"],"title":"AuditLogEntry","description":"Single audit log entry.","example":{"action":"key_create","created_at":"2025-01-15T10:30:00Z","details":{"key_type":"user","plan":"free"},"id":"audit_123456","ip_address":"192.168.1.1","latency_ms":45,"resource_id":"key_xyz789","resource_type":"api_key","status":"success","user_agent":"Mozilla/5.0...","user_email":"user@example.com","user_id":"user_abc123"}},"AuditLogResponse":{"properties":{"entries":{"items":{"$ref":"#/components/schemas/AuditLogEntry"},"type":"array","title":"Entries","description":"Audit log entries"},"total":{"type":"integer","title":"Total","description":"Total entries matching filters"},"page":{"type":"integer","title":"Page","description":"Current page (1-indexed)"},"page_size":{"type":"integer","title":"Page Size","description":"Entries per page"},"has_more":{"type":"boolean","title":"Has More","description":"Whether more pages exist"}},"type":"object","required":["entries","total","page","page_size","has_more"],"title":"AuditLogResponse","description":"Paginated audit log response."},"BetaGrantRequest":{"properties":{"email":{"anyOf":[{"type":"string","maxLength":255,"minLength":1},{"type":"null"}],"title":"Email","description":"User email (either email or user_id required)"},"user_id":{"anyOf":[{"type":"string","maxLength":36,"minLength":1},{"type":"null"}],"title":"User Id","description":"User ID (either email or user_id required)"},"kind":{"type":"string","maxLength":50,"title":"Kind","description":"Entitlement kind: 'integration_pilot', 'beta_bonus_quota', 'invoicing_beta', 'promotional'","default":"integration_pilot"},"bonus_quota":{"type":"integer","maximum":100000.0,"minimum":0.0,"title":"Bonus Quota","description":"Additional quota to grant (0 for feature-only entitlements)","default":0},"expires_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Expires At","description":"Expiration timestamp (ISO 8601). Null for permanent bonus."},"note":{"anyOf":[{"type":"string","maxLength":500},{"type":"null"}],"title":"Note","description":"Optional note for audit trail"}},"type":"object","title":"BetaGrantRequest","description":"Request to grant beta access bonus quota to a user.","example":{"bonus_quota":2000,"email":"beta@example.com","expires_at":"2025-02-15T23:59:59Z","note":"Beta tester invite - wave 1"}},"BetaGrantResponse":{"properties":{"status":{"type":"string","title":"Status","description":"Status: ok"},"user_id":{"type":"string","title":"User Id","description":"User ID that received the bonus"},"user_email":{"type":"string","title":"User Email","description":"User email"},"entitlement_id":{"type":"string","title":"Entitlement Id","description":"Created entitlement ID"},"bonus_quota":{"type":"integer","title":"Bonus Quota","description":"Bonus quota granted"},"starts_at":{"type":"string","title":"Starts At","description":"When entitlement becomes active (ISO 8601)"},"expires_at":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Expires At","description":"When entitlement expires (ISO 8601)"},"effective_quota_preview":{"type":"integer","title":"Effective Quota Preview","description":"Total effective quota after grant (plan + all active bonuses)"}},"type":"object","required":["status","user_id","user_email","entitlement_id","bonus_quota","starts_at","effective_quota_preview"],"title":"BetaGrantResponse","description":"Response after granting beta access.","example":{"bonus_quota":2000,"effective_quota_preview":2010,"entitlement_id":"660e9500-f29c-52e5-b827-557766551111","expires_at":"2025-02-15T23:59:59Z","starts_at":"2025-01-15T10:30:00Z","status":"ok","user_email":"beta@example.com","user_id":"550e8400-e29b-41d4-a716-446655440000"}},"Body_analyze_before_convert_api_v1_convert_analyze_post":{"properties":{"file":{"type":"string","contentMediaType":"application/octet-stream","title":"File","description":"PDF file to analyze before conversion"}},"type":"object","required":["file"],"title":"Body_analyze_before_convert_api_v1_convert_analyze_post"},"Body_convert_invoice_api_v1_convert_post":{"properties":{"file":{"type":"string","contentMediaType":"application/octet-stream","title":"File","description":"PDF file to convert to Factur-X format"},"options":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Options","description":"JSON object with conversion options (optional)"},"invoice_data":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Invoice Data","description":"JSON structured invoice data from ERP (optional). When provided, PDF text parsing is skipped."}},"type":"object","required":["file"],"title":"Body_convert_invoice_api_v1_convert_post"},"Body_extract_facturx_api_v1_extract_post":{"properties":{"file":{"type":"string","contentMediaType":"application/octet-stream","title":"File","description":"PDF file to extract XML from (Factur-X format)"}},"type":"object","required":["file"],"title":"Body_extract_facturx_api_v1_extract_post"},"Body_repair_facturx_xml_api_v1_repair_post":{"properties":{"file":{"type":"string","contentMediaType":"application/octet-stream","title":"File","description":"XML file to repair (Factur-X/EN16931 format)"},"constraints":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Constraints","description":"JSON object with repair constraints (optional)"}},"type":"object","required":["file"],"title":"Body_repair_facturx_xml_api_v1_repair_post"},"Body_validate_facturx_api_v1_validate_post":{"properties":{"file":{"type":"string","contentMediaType":"application/octet-stream","title":"File","description":"Fichier XML ou PDF Factur-X à valider (limite configurable via MAX_UPLOAD_SIZE_BYTES)","examples":["facture.xml","invoice.pdf"]}},"type":"object","required":["file"],"title":"Body_validate_facturx_api_v1_validate_post"},"BonusQuotaInfo":{"properties":{"active":{"type":"boolean","title":"Active","description":"Whether any bonus quota is currently active"},"total_bonus":{"type":"integer","title":"Total Bonus","description":"Total bonus quota from all active entitlements"},"nearest_expiration":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Nearest Expiration","description":"Nearest expiration of active entitlements (ISO 8601). Null if permanent or none active."},"active_count":{"type":"integer","title":"Active Count","description":"Number of active entitlements"}},"type":"object","required":["active","total_bonus","active_count"],"title":"BonusQuotaInfo","description":"Information about active bonus quota entitlements.","example":{"active":true,"active_count":1,"nearest_expiration":"2025-02-15T23:59:59Z","total_bonus":2000}},"BuildInfo":{"properties":{"commit_sha":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Commit Sha","description":"Git commit SHA for this build, if provided at build time"},"build_date":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Build Date","description":"Build date/time, if provided at build time"},"env":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Env","description":"Runtime environment identifier (development/production/test)"}},"type":"object","title":"BuildInfo"},"CapabilitiesInfo":{"properties":{"supports_facturx_108_xsd":{"type":"boolean","title":"Supports Facturx 108 Xsd"},"supports_en16931_compiled_xslt":{"type":"boolean","title":"Supports En16931 Compiled Xslt"},"supports_pdfa_via_verapdf":{"type":"boolean","title":"Supports Pdfa Via Verapdf"},"facturx_108_schematron_guaranteed":{"type":"boolean","title":"Facturx 108 Schematron Guaranteed","description":"True when Factur-X 1.08 Schematron validation for all profiles is guaranteed (Saxon available and compiled artefacts bundled)."},"supports_extract":{"type":"boolean","title":"Supports Extract","description":"True when POST /api/v1/extract endpoint is available. Always true as extraction has no external dependencies.","default":true},"supports_repair":{"type":"boolean","title":"Supports Repair","description":"True when POST /api/v1/repair endpoint is available. Requires Saxon processor for Schematron validation.","default":false},"supports_convert":{"type":"boolean","title":"Supports Convert","description":"True when POST /api/v1/convert endpoint is available. Requires callas pdfToolbox (REST or CLI).","default":false}},"type":"object","required":["supports_facturx_108_xsd","supports_en16931_compiled_xslt","supports_pdfa_via_verapdf","facturx_108_schematron_guaranteed"],"title":"CapabilitiesInfo"},"CheckoutRequest":{"properties":{"plan_code":{"type":"string","title":"Plan Code","description":"Plan to purchase (pro/business)","default":"pro"},"source":{"anyOf":[{"type":"string","maxLength":64,"minLength":1},{"type":"null"}],"title":"Source","description":"Optional conversion source tag (e.g. api_validate_quota_exceeded, dashboard_account)"},"trigger_event":{"anyOf":[{"type":"string","maxLength":64,"minLength":1},{"type":"null"}],"title":"Trigger Event","description":"Optional trigger event name (e.g. quota_exceeded, upgrade_cta_clicked)"},"from_endpoint":{"anyOf":[{"type":"string","maxLength":128,"minLength":1},{"type":"null"}],"title":"From Endpoint","description":"Optional originating API endpoint when triggered from a 402 flow"}},"type":"object","title":"CheckoutRequest","description":"Request payload for creating a Stripe checkout session."},"CheckoutResponse":{"properties":{"checkout_url":{"type":"string","title":"Checkout Url","description":"Redirect user to this URL"}},"type":"object","required":["checkout_url"],"title":"CheckoutResponse","description":"Response with Stripe checkout URL."},"ClientListResponse":{"properties":{"clients":{"items":{"$ref":"#/components/schemas/ClientResponse"},"type":"array","title":"Clients"},"total":{"type":"integer","title":"Total"}},"type":"object","required":["clients","total"],"title":"ClientListResponse","description":"List of clients."},"ClientRequest":{"properties":{"name":{"type":"string","maxLength":200,"minLength":1,"title":"Name"},"type":{"type":"string","pattern":"^(B2B|B2C)$","title":"Type","default":"B2B"},"address_line1":{"type":"string","maxLength":300,"minLength":1,"title":"Address Line1"},"postal_code":{"type":"string","maxLength":10,"minLength":1,"title":"Postal Code"},"city":{"type":"string","maxLength":100,"minLength":1,"title":"City"},"country_code":{"type":"string","pattern":"^[A-Z]{2}$","title":"Country Code","default":"FR"},"siret":{"anyOf":[{"type":"string","pattern":"^\\d{14}$"},{"type":"null"}],"title":"Siret"},"vat_number":{"anyOf":[{"type":"string","maxLength":20},{"type":"null"}],"title":"Vat Number"},"address_line2":{"anyOf":[{"type":"string","maxLength":300},{"type":"null"}],"title":"Address Line2"},"email":{"anyOf":[{"type":"string","maxLength":254},{"type":"null"}],"title":"Email"}},"type":"object","required":["name","address_line1","postal_code","city"],"title":"ClientRequest","description":"Create or update a client."},"ClientResponse":{"properties":{"id":{"type":"string","title":"Id"},"name":{"type":"string","title":"Name"},"type":{"type":"string","title":"Type"},"address_line1":{"type":"string","title":"Address Line1"},"postal_code":{"type":"string","title":"Postal Code"},"city":{"type":"string","title":"City"},"country_code":{"type":"string","title":"Country Code"},"siret":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Siret"},"vat_number":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Vat Number"},"address_line2":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Address Line2"},"email":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Email"},"created_at":{"type":"string","title":"Created At"},"updated_at":{"type":"string","title":"Updated At"}},"type":"object","required":["id","name","type","address_line1","postal_code","city","country_code","created_at","updated_at"],"title":"ClientResponse","description":"Client response."},"ConfirmationErrorResponse":{"properties":{"detail":{"type":"string","title":"Detail","description":"Error message"}},"type":"object","required":["detail"],"title":"ConfirmationErrorResponse","description":"Response for invalid/expired token.","example":{"detail":"invalid_or_expired_token"}},"ConfirmationRequest":{"properties":{"email":{"type":"string","title":"Email","description":"Email address used in Step 1"},"token":{"type":"string","title":"Token","description":"Verification token from email"},"regenerate":{"type":"boolean","title":"Regenerate","description":"If true and email already has an active key, revoke it and create a new one. Requires valid token (proves email ownership).","default":false}},"type":"object","required":["email","token"],"title":"ConfirmationRequest","description":"Request body for Step 2: confirm with token."},"ConvertProdReadinessItem":{"properties":{"key":{"type":"string","title":"Key"},"status":{"type":"string","enum":["pass","warn","fail"],"title":"Status"},"reason":{"type":"string","title":"Reason"},"detail":{"anyOf":[{"type":"object"},{"type":"null"}],"title":"Detail"}},"type":"object","required":["key","status","reason"],"title":"ConvertProdReadinessItem","description":"One operational readiness check for convert production setup."},"ConvertProdReadinessResponse":{"properties":{"ok":{"type":"boolean","title":"Ok"},"environment":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Environment"},"summary":{"$ref":"#/components/schemas/ConvertProdReadinessSummary"},"items":{"items":{"$ref":"#/components/schemas/ConvertProdReadinessItem"},"type":"array","title":"Items"},"rollout_guard":{"$ref":"#/components/schemas/RolloutGuardPreflightResponse"}},"type":"object","required":["ok","summary","items","rollout_guard"],"title":"ConvertProdReadinessResponse","description":"Response payload for convert production readiness checks."},"ConvertProdReadinessSummary":{"properties":{"pass_count":{"type":"integer","title":"Pass Count"},"warn_count":{"type":"integer","title":"Warn Count"},"fail_count":{"type":"integer","title":"Fail Count"}},"type":"object","required":["pass_count","warn_count","fail_count"],"title":"ConvertProdReadinessSummary","description":"Aggregate counts for convert production readiness checks."},"CreateCreditNoteRequest":{"properties":{"lines":{"items":{"$ref":"#/components/schemas/InvoiceLineRequest"},"type":"array","minItems":1,"title":"Lines"},"notes":{"anyOf":[{"type":"string","maxLength":2000},{"type":"null"}],"title":"Notes"}},"type":"object","required":["lines"],"title":"CreateCreditNoteRequest","description":"Create a credit note for an issued invoice."},"CreateInvoiceRequest":{"properties":{"client_id":{"type":"string","title":"Client Id"},"lines":{"items":{"$ref":"#/components/schemas/InvoiceLineRequest"},"type":"array","minItems":1,"title":"Lines"},"service_date":{"anyOf":[{"type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}$"},{"type":"null"}],"title":"Service Date"},"payment_terms_days":{"type":"integer","maximum":365.0,"minimum":1.0,"title":"Payment Terms Days","default":30},"notes":{"anyOf":[{"type":"string","maxLength":2000},{"type":"null"}],"title":"Notes"}},"type":"object","required":["client_id","lines"],"title":"CreateInvoiceRequest","description":"Create a new draft invoice."},"CreateKeyRequest":{"properties":{"label":{"anyOf":[{"type":"string","maxLength":100},{"type":"null"}],"title":"Label","description":"Optional label for the key"},"plan_code":{"type":"string","title":"Plan Code","description":"Plan code for the new key","default":"free"}},"type":"object","title":"CreateKeyRequest","description":"Request to create a new API key."},"CreateKeyResponse":{"properties":{"api_key":{"type":"string","title":"Api Key","description":"The API key. SAVE THIS NOW - it cannot be retrieved later!"},"key":{"allOf":[{"$ref":"#/components/schemas/DashboardKeyResponse"}],"description":"Key metadata"}},"type":"object","required":["api_key","key"],"title":"CreateKeyResponse","description":"Response with new API key (shown ONCE).","example":{"api_key":"fxk_abc123def456...","key":{"created_at":"2025-01-15T10:30:00Z","id":"key_123456","label":"My API Key","plan":"free","status":"active"}}},"CreditCheckoutResponse":{"properties":{"checkout_url":{"type":"string","title":"Checkout Url"}},"type":"object","required":["checkout_url"],"title":"CreditCheckoutResponse","description":"Response from credit pack checkout session creation."},"DashboardKeyResponse":{"properties":{"id":{"type":"string","title":"Id","description":"API key ID"},"label":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Label","description":"User-defined label"},"status":{"type":"string","title":"Status","description":"Key status (active, revoked)"},"plan":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Plan","description":"Plan code associated with key"},"created_at":{"type":"string","format":"date-time","title":"Created At","description":"Creation timestamp"},"last_used_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Last Used At","description":"Last usage timestamp if available"},"revoked_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Revoked At","description":"Revocation timestamp if revoked"},"key_masked":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Key Masked","description":"Masked key suffix (e.g., ••••abcd)"}},"type":"object","required":["id","status","created_at"],"title":"DashboardKeyResponse","description":"API key info for dashboard (no hash/plaintext exposed).","example":{"created_at":"2025-01-15T10:30:00Z","id":"key_123456","key_masked":"••••abcd","label":"Production API","last_used_at":"2025-02-01T10:30:00Z","plan":"free","status":"active"}},"DashboardKeysListResponse":{"properties":{"keys":{"items":{"$ref":"#/components/schemas/DashboardKeyResponse"},"type":"array","title":"Keys","description":"List of API keys"}},"type":"object","required":["keys"],"title":"DashboardKeysListResponse","description":"List of user's API keys."},"DashboardMeResponse":{"properties":{"user_id":{"type":"string","title":"User Id","description":"User ID"},"email":{"type":"string","title":"Email","description":"User email"},"status":{"type":"string","title":"Status","description":"Account status (active, suspended)"},"plan":{"type":"string","title":"Plan","description":"Current plan code"},"created_at":{"type":"string","format":"date-time","title":"Created At","description":"Account creation timestamp"},"verified_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Verified At","description":"First login timestamp"},"stripe_customer_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Stripe Customer Id","description":"Stripe customer ID if linked"},"activated_api":{"type":"boolean","title":"Activated Api","description":"Whether the user has made at least one API call"},"subscription":{"anyOf":[{"$ref":"#/components/schemas/SubscriptionInfo"},{"type":"null"}],"description":"Subscription and payment status. Null if Stripe not configured."},"bonus_quota":{"anyOf":[{"$ref":"#/components/schemas/BonusQuotaInfo"},{"type":"null"}],"description":"Active bonus quota from entitlements. Null if entitlement system unavailable."},"pilot_access":{"anyOf":[{"$ref":"#/components/schemas/PilotAccessInfo"},{"type":"null"}],"description":"Integration pilot access status. Present when user has an active integration_pilot entitlement."}},"type":"object","required":["user_id","email","status","plan","created_at","activated_api"],"title":"DashboardMeResponse","description":"User profile for dashboard.","example":{"activated_api":false,"bonus_quota":{"active":true,"active_count":1,"nearest_expiration":"2025-02-15T23:59:59Z","total_bonus":2000},"created_at":"2025-01-15T10:30:00Z","email":"user@example.com","plan":"free","status":"active","subscription":{"payment_status":"ok","subscription_status":"none"},"user_id":"550e8400-e29b-41d4-a716-446655440000","verified_at":"2025-01-15T10:35:00Z"}},"DashboardUsageResponse":{"properties":{"plan":{"type":"string","title":"Plan","description":"Plan code"},"quota":{"type":"integer","title":"Quota","description":"Total monthly quota limit (base + bonus)"},"count":{"type":"integer","title":"Count","description":"Current usage count"},"period_start":{"type":"string","title":"Period Start","description":"Billing period start (ISO 8601)"},"period_end":{"type":"string","title":"Period End","description":"Billing period end (ISO 8601)"},"remaining":{"type":"integer","title":"Remaining","description":"Remaining quota (quota - count)"},"usage_percent":{"type":"number","title":"Usage Percent","description":"Usage percentage (count / quota * 100)"},"base_quota":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Base Quota","description":"Base quota from plan (before bonus). Null for backwards compat."},"bonus_quota":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Bonus Quota","description":"Active bonus quota from entitlements. Null if none or unavailable."},"credits_remaining":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Credits Remaining","description":"Purchased credits remaining. Null if credit system not available. 0 if no credits purchased."}},"type":"object","required":["plan","quota","count","period_start","period_end","remaining","usage_percent"],"title":"DashboardUsageResponse","description":"Monthly usage for dashboard.","example":{"base_quota":10,"bonus_quota":2000,"count":3,"period_end":"2025-02-01","period_start":"2025-01-01","plan":"free","quota":2010,"remaining":2007,"usage_percent":0.1}},"EntitlementInfo":{"properties":{"id":{"type":"string","title":"Id","description":"Entitlement ID"},"kind":{"type":"string","title":"Kind","description":"Entitlement type"},"bonus_quota":{"type":"integer","title":"Bonus Quota","description":"Bonus quota amount"},"starts_at":{"type":"string","title":"Starts At","description":"Start timestamp (ISO 8601)"},"expires_at":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Expires At","description":"Expiration timestamp (ISO 8601)"},"is_active":{"type":"boolean","title":"Is Active","description":"Whether entitlement is currently active"},"granted_by":{"type":"string","title":"Granted By","description":"Admin who granted this"},"note":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Note","description":"Optional note"},"created_at":{"type":"string","title":"Created At","description":"Creation timestamp (ISO 8601)"}},"type":"object","required":["id","kind","bonus_quota","starts_at","is_active","granted_by","created_at"],"title":"EntitlementInfo","description":"Information about a single entitlement."},"ExportFormat":{"type":"string","enum":["csv","json"],"title":"ExportFormat","description":"Supported export formats."},"ExtendEntitlementRequest":{"properties":{"days":{"anyOf":[{"type":"integer","maximum":365.0,"minimum":1.0},{"type":"null"}],"title":"Days","description":"Extend by this many days starting from now (1..365). Mutually exclusive with `until`."},"until":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Until","description":"Absolute new expiration (UTC ISO 8601). Mutually exclusive with `days`."},"note":{"anyOf":[{"type":"string","maxLength":500},{"type":"null"}],"title":"Note","description":"Optional reason for the extension (logged in audit trail)"}},"type":"object","title":"ExtendEntitlementRequest","description":"Request to extend (or shorten) an existing entitlement's expiration.\n\nExactly ONE of `days` or `until` must be set.\n  - days: relative extension applied from datetime.utcnow()\n  - until: absolute UTC ISO 8601 deadline\nBoth compute the same column update."},"ExtendEntitlementResponse":{"properties":{"status":{"type":"string","title":"Status","default":"ok"},"entitlement_id":{"type":"string","title":"Entitlement Id"},"user_id":{"type":"string","title":"User Id"},"kind":{"type":"string","title":"Kind"},"previous_expires_at":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Previous Expires At","description":"Previous expiration (ISO 8601). Null if it was permanent."},"new_expires_at":{"type":"string","title":"New Expires At","description":"New expiration (ISO 8601). Always set after extension."}},"type":"object","required":["entitlement_id","user_id","kind","new_expires_at"],"title":"ExtendEntitlementResponse","description":"Response after extending an entitlement window."},"FailedEventsResponse":{"properties":{"events":{"items":{"$ref":"#/components/schemas/StripeEventDTO"},"type":"array","title":"Events"},"count":{"type":"integer","title":"Count"},"message":{"type":"string","title":"Message"}},"type":"object","required":["events","count","message"],"title":"FailedEventsResponse","description":"Response for listing failed Stripe events."},"FeedbackResponse":{"properties":{"status":{"type":"string","title":"Status","default":"ok"},"message":{"type":"string","title":"Message","default":"Merci pour votre retour."}},"type":"object","title":"FeedbackResponse"},"HTTPValidationError":{"properties":{"detail":{"items":{"$ref":"#/components/schemas/ValidationError"},"type":"array","title":"Detail"}},"type":"object","title":"HTTPValidationError"},"InvoiceLineRequest":{"properties":{"description":{"type":"string","maxLength":500,"minLength":1,"title":"Description"},"quantity":{"anyOf":[{"type":"number","exclusiveMinimum":0.0},{"type":"string"}],"title":"Quantity"},"unit_price_ht":{"anyOf":[{"type":"number","minimum":0.0},{"type":"string"}],"title":"Unit Price Ht"},"vat_rate":{"anyOf":[{"type":"number","minimum":0.0},{"type":"string"}],"title":"Vat Rate","default":"0"},"vat_category":{"type":"string","pattern":"^(E|S|AE|K|G|O|Z)$","title":"Vat Category","default":"E"}},"type":"object","required":["description","quantity","unit_price_ht"],"title":"InvoiceLineRequest","description":"A single invoice line item."},"InvoiceLineResponse":{"properties":{"id":{"type":"string","title":"Id"},"position":{"type":"integer","title":"Position"},"description":{"type":"string","title":"Description"},"quantity":{"type":"string","title":"Quantity"},"unit_price_ht":{"type":"string","title":"Unit Price Ht"},"vat_rate":{"type":"string","title":"Vat Rate"},"vat_category":{"type":"string","title":"Vat Category"},"line_total_ht":{"type":"string","title":"Line Total Ht"},"line_total_tax":{"type":"string","title":"Line Total Tax"}},"type":"object","required":["id","position","description","quantity","unit_price_ht","vat_rate","vat_category","line_total_ht","line_total_tax"],"title":"InvoiceLineResponse","description":"Invoice line in response."},"InvoiceListResponse":{"properties":{"invoices":{"items":{"$ref":"#/components/schemas/InvoiceSummaryResponse"},"type":"array","title":"Invoices"},"total":{"type":"integer","title":"Total"}},"type":"object","required":["invoices","total"],"title":"InvoiceListResponse","description":"Paginated list of invoices."},"InvoiceResponse":{"properties":{"id":{"type":"string","title":"Id"},"invoice_number":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Invoice Number"},"type":{"type":"string","title":"Type"},"status":{"type":"string","title":"Status"},"client_id":{"type":"string","title":"Client Id"},"client_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Client Name"},"issue_date":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Issue Date"},"service_date":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Service Date"},"due_date":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Due Date"},"currency":{"type":"string","title":"Currency"},"total_ht":{"type":"string","title":"Total Ht"},"total_tax":{"type":"string","title":"Total Tax"},"total_ttc":{"type":"string","title":"Total Ttc"},"payment_terms_days":{"type":"integer","title":"Payment Terms Days"},"notes":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Notes"},"related_invoice_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Related Invoice Id"},"facturx_profile":{"type":"string","title":"Facturx Profile"},"has_pdf":{"type":"boolean","title":"Has Pdf"},"lines":{"items":{"$ref":"#/components/schemas/InvoiceLineResponse"},"type":"array","title":"Lines"},"created_at":{"type":"string","title":"Created At"},"updated_at":{"type":"string","title":"Updated At"}},"type":"object","required":["id","type","status","client_id","currency","total_ht","total_tax","total_ttc","payment_terms_days","facturx_profile","has_pdf","lines","created_at","updated_at"],"title":"InvoiceResponse","description":"Full invoice response (with lines)."},"InvoiceSummaryResponse":{"properties":{"id":{"type":"string","title":"Id"},"invoice_number":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Invoice Number"},"type":{"type":"string","title":"Type"},"status":{"type":"string","title":"Status"},"client_id":{"type":"string","title":"Client Id"},"client_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Client Name"},"issue_date":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Issue Date"},"due_date":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Due Date"},"total_ht":{"type":"string","title":"Total Ht"},"total_ttc":{"type":"string","title":"Total Ttc"},"has_pdf":{"type":"boolean","title":"Has Pdf"},"created_at":{"type":"string","title":"Created At"}},"type":"object","required":["id","type","status","client_id","total_ht","total_ttc","has_pdf","created_at"],"title":"InvoiceSummaryResponse","description":"Invoice in list view (without lines)."},"IssueResponse":{"properties":{"invoice_id":{"type":"string","title":"Invoice Id"},"invoice_number":{"type":"string","title":"Invoice Number"},"status":{"type":"string","title":"Status","default":"ISSUED"},"pdf_storage_key":{"type":"string","title":"Pdf Storage Key"},"total_ht":{"type":"string","title":"Total Ht"},"total_tax":{"type":"string","title":"Total Tax"},"total_ttc":{"type":"string","title":"Total Ttc"}},"type":"object","required":["invoice_id","invoice_number","pdf_storage_key","total_ht","total_tax","total_ttc"],"title":"IssueResponse","description":"Response after successful issuance."},"IssueSummaryItem":{"properties":{"severity":{"type":"string","title":"Severity","description":"Issue severity: 'error' | 'warning' | 'info'"},"code":{"type":"string","title":"Code","description":"Issue code (e.g., 'BR-CO-10')"},"field":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Field","description":"Field reference (e.g., 'BT-110')"}},"type":"object","required":["severity","code"],"title":"IssueSummaryItem","description":"RGPD-safe issue summary (code/field/severity only)."},"IssuerRequest":{"properties":{"name":{"type":"string","maxLength":200,"minLength":1,"title":"Name"},"siret":{"type":"string","pattern":"^\\d{14}$","title":"Siret"},"vat_regime":{"type":"string","pattern":"^(FRANCHISE_293B|STANDARD)$","title":"Vat Regime"},"address_line1":{"type":"string","maxLength":300,"minLength":1,"title":"Address Line1"},"postal_code":{"type":"string","maxLength":10,"minLength":1,"title":"Postal Code"},"city":{"type":"string","maxLength":100,"minLength":1,"title":"City"},"country_code":{"type":"string","pattern":"^[A-Z]{2}$","title":"Country Code","default":"FR"},"legal_form":{"anyOf":[{"type":"string","maxLength":100},{"type":"null"}],"title":"Legal Form"},"vat_number":{"anyOf":[{"type":"string","maxLength":20},{"type":"null"}],"title":"Vat Number"},"address_line2":{"anyOf":[{"type":"string","maxLength":300},{"type":"null"}],"title":"Address Line2"},"iban":{"anyOf":[{"type":"string","maxLength":34},{"type":"null"}],"title":"Iban"},"bic":{"anyOf":[{"type":"string","maxLength":11},{"type":"null"}],"title":"Bic"},"capital":{"anyOf":[{"type":"string","maxLength":50},{"type":"null"}],"title":"Capital"},"rcs_city":{"anyOf":[{"type":"string","maxLength":100},{"type":"null"}],"title":"Rcs City"},"numbering_prefix":{"type":"string","maxLength":10,"title":"Numbering Prefix","default":"F"}},"type":"object","required":["name","siret","vat_regime","address_line1","postal_code","city"],"title":"IssuerRequest","description":"Create or update issuer profile."},"IssuerResponse":{"properties":{"id":{"type":"string","title":"Id"},"name":{"type":"string","title":"Name"},"siret":{"type":"string","title":"Siret"},"vat_regime":{"type":"string","title":"Vat Regime"},"address_line1":{"type":"string","title":"Address Line1"},"postal_code":{"type":"string","title":"Postal Code"},"city":{"type":"string","title":"City"},"country_code":{"type":"string","title":"Country Code"},"legal_form":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Legal Form"},"vat_number":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Vat Number"},"address_line2":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Address Line2"},"iban":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Iban"},"bic":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Bic"},"capital":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Capital"},"rcs_city":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Rcs City"},"numbering_prefix":{"type":"string","title":"Numbering Prefix","default":"F"},"created_at":{"type":"string","title":"Created At"},"updated_at":{"type":"string","title":"Updated At"}},"type":"object","required":["id","name","siret","vat_regime","address_line1","postal_code","city","country_code","created_at","updated_at"],"title":"IssuerResponse","description":"Issuer profile response."},"LimitsInfo":{"properties":{"max_upload_size_bytes":{"type":"integer","title":"Max Upload Size Bytes"},"accepted_formats":{"items":{"type":"string"},"type":"array","title":"Accepted Formats"},"rate_limit_enabled":{"type":"boolean","title":"Rate Limit Enabled"},"rate_limit_requests_per_minute":{"type":"integer","title":"Rate Limit Requests Per Minute"},"rate_limit_mode":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Rate Limit Mode"}},"type":"object","required":["max_upload_size_bytes","accepted_formats","rate_limit_enabled","rate_limit_requests_per_minute"],"title":"LimitsInfo"},"LinksInfo":{"properties":{"docs":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Docs"},"openapi":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Openapi"},"changelog":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Changelog"},"status":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Status"},"generated_at":{"type":"string","title":"Generated At"}},"type":"object","required":["generated_at"],"title":"LinksInfo"},"LogoutAllResponse":{"properties":{"message":{"type":"string","title":"Message","description":"Success message","default":"All sessions revoked"},"sessions_revoked":{"type":"integer","title":"Sessions Revoked","description":"Number of sessions that were revoked"}},"type":"object","required":["sessions_revoked"],"title":"LogoutAllResponse","description":"Response after revoking all sessions.","example":{"message":"All sessions revoked","sessions_revoked":3}},"LogoutResponse":{"properties":{"message":{"type":"string","title":"Message","description":"Success message","default":"Logged out successfully"}},"type":"object","title":"LogoutResponse","description":"Response after successful logout."},"MagicLinkRequest":{"properties":{"email":{"type":"string","format":"email","title":"Email","description":"User email address"}},"type":"object","required":["email"],"title":"MagicLinkRequest","description":"Request for magic link email."},"MagicLinkResponse":{"properties":{"message":{"type":"string","title":"Message","description":"Confirmation message (same for all cases)","default":"If this email is valid, a magic link has been sent. Check your inbox."},"expires_in_seconds":{"type":"integer","title":"Expires In Seconds","description":"Token validity period in seconds","default":900}},"type":"object","title":"MagicLinkResponse","description":"Uniform response for magic link request (prevents enumeration).","example":{"expires_in_seconds":900,"message":"If this email is valid, a magic link has been sent. Check your inbox."}},"MarkPaidRequest":{"properties":{"paid_at":{"anyOf":[{"type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}"},{"type":"null"}],"title":"Paid At"}},"type":"object","title":"MarkPaidRequest","description":"Mark invoice as paid."},"MeSummaryResponse":{"properties":{"email":{"type":"string","title":"Email"},"plan":{"$ref":"#/components/schemas/app__application__dto__api_keys__PlanInfo"},"usage":{"$ref":"#/components/schemas/UsageSummary"}},"type":"object","required":["email","plan","usage"],"title":"MeSummaryResponse","description":"Complete account summary for /me endpoint."},"MetaResponse":{"properties":{"build":{"$ref":"#/components/schemas/BuildInfo"},"validation_engine":{"$ref":"#/components/schemas/ValidationEngineInfo"},"limits":{"$ref":"#/components/schemas/LimitsInfo"},"plans":{"anyOf":[{"items":{"$ref":"#/components/schemas/app__infrastructure__api__routes__meta__PlanInfo"},"type":"array"},{"type":"null"}],"title":"Plans"},"capabilities":{"$ref":"#/components/schemas/CapabilitiesInfo"},"links":{"$ref":"#/components/schemas/LinksInfo"}},"type":"object","required":["build","validation_engine","limits","capabilities","links"],"title":"MetaResponse"},"NextStepAction":{"properties":{"label":{"type":"string","title":"Label","description":"Action label for the user"},"href":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Href","description":"Optional URL for the action"},"action":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Action","description":"Optional action identifier"}},"type":"object","required":["label"],"title":"NextStepAction","description":"Suggested next step for the user."},"PaymentEventRequest":{"properties":{"event":{"type":"string","title":"Event"}},"type":"object","required":["event"],"title":"PaymentEventRequest"},"PaymentLineResponse":{"properties":{"description":{"type":"string","title":"Description"},"quantity":{"type":"string","title":"Quantity"},"unit_price_ht":{"type":"string","title":"Unit Price Ht"},"line_total_ht":{"type":"string","title":"Line Total Ht"}},"type":"object","required":["description","quantity","unit_price_ht","line_total_ht"],"title":"PaymentLineResponse"},"PaymentPageResponse":{"properties":{"issuer_name":{"type":"string","title":"Issuer Name"},"issuer_siret":{"type":"string","title":"Issuer Siret"},"invoice_number":{"type":"string","title":"Invoice Number"},"issue_date":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Issue Date"},"due_date":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Due Date"},"status":{"type":"string","title":"Status"},"total_ht":{"type":"string","title":"Total Ht"},"total_tax":{"type":"string","title":"Total Tax"},"total_ttc":{"type":"string","title":"Total Ttc"},"paid_amount":{"type":"string","title":"Paid Amount"},"remaining_amount":{"type":"string","title":"Remaining Amount"},"lines":{"items":{"$ref":"#/components/schemas/PaymentLineResponse"},"type":"array","title":"Lines"},"iban":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Iban"},"bic":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Bic"},"payment_ref":{"type":"string","title":"Payment Ref"},"epc_qr_data_uri":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Epc Qr Data Uri"},"pdf_available":{"type":"boolean","title":"Pdf Available"}},"type":"object","required":["issuer_name","issuer_siret","invoice_number","issue_date","due_date","status","total_ht","total_tax","total_ttc","paid_amount","remaining_amount","lines","iban","bic","payment_ref","epc_qr_data_uri","pdf_available"],"title":"PaymentPageResponse"},"PdfaEngineInfo":{"properties":{"provider":{"type":"string","title":"Provider"},"configured":{"type":"boolean","title":"Configured"},"version":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Version"}},"type":"object","required":["provider","configured"],"title":"PdfaEngineInfo"},"PilotAccessInfo":{"properties":{"active":{"type":"boolean","title":"Active","description":"Whether a pilot or trial access is currently active"},"kind":{"type":"string","title":"Kind","description":"Entitlement kind: 'integration_pilot' (admin-granted) or 'integration_trial' (self-serve).","default":"integration_pilot"},"label":{"type":"string","title":"Label","description":"User-facing label for the access type","default":"Accès intégration"},"expires_at":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Expires At","description":"When the access expires (ISO 8601). Null if permanent."},"days_remaining":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Days Remaining","description":"Whole days until expires_at (rounded down). Null if permanent."},"note":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Note","description":"Admin note (only shared with user if appropriate)"}},"type":"object","required":["active"],"title":"PilotAccessInfo","description":"Integration pilot or trial access status — user-facing.\n\nUsed by the dashboard to (a) hide the credit-based pricing semantics from\npilot users (Nicolas-style ERP integrators) and (b) surface a trial banner\nwith days_remaining for self-serve Integration trials.","example":{"active":true,"expires_at":"2026-05-09T23:59:59Z","label":"Accès intégration"}},"PortalResponse":{"properties":{"url":{"type":"string","title":"Url","description":"Redirect user to this URL to manage subscription"}},"type":"object","required":["url"],"title":"PortalResponse","description":"Response with Stripe Customer Portal URL."},"PublicFreeApiKeyRequest":{"properties":{"email":{"type":"string","title":"Email","description":"User email address (required)"},"label":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Label","description":"Optional label to identify the key"},"source":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Source","description":"CTA the user clicked on the public site (free_cta, integration_cta, business_cta, erp_cta). Drives onboarding email drip + trial entitlement. Validated against SignupSource server-side."}},"type":"object","required":["email"],"title":"PublicFreeApiKeyRequest","description":"Request payload for getting/creating a free API key."},"RateLimitErrorDetail":{"properties":{"error":{"type":"string","title":"Error","description":"Error code","default":"rate_limit_exceeded"},"message":{"type":"string","title":"Message","description":"Human-readable error message","default":"Too many requests. Please try again later."},"retry_after_seconds":{"type":"integer","title":"Retry After Seconds","description":"Seconds to wait before retrying"}},"type":"object","required":["retry_after_seconds"],"title":"RateLimitErrorDetail","description":"Detail payload for 429 Rate Limit response."},"RateLimitErrorResponse":{"properties":{"detail":{"allOf":[{"$ref":"#/components/schemas/RateLimitErrorDetail"}],"description":"Error details"}},"type":"object","required":["detail"],"title":"RateLimitErrorResponse","description":"Response model for 429 Rate Limit Exceeded."},"RefreshResponse":{"properties":{"access_token":{"type":"string","title":"Access Token","description":"New JWT access token (15 min)"},"token_type":{"type":"string","title":"Token Type","description":"Token type","default":"Bearer"},"expires_in":{"type":"integer","title":"Expires In","description":"Token expiry in seconds"}},"type":"object","required":["access_token","expires_in"],"title":"RefreshResponse","description":"Response after successful token refresh.\n\nNew refresh token is set as HttpOnly cookie.","example":{"access_token":"eyJhbGciOiJIUzI1NiIs...","expires_in":900,"token_type":"Bearer"}},"RenameKeyRequest":{"properties":{"key_id":{"type":"string","minLength":1,"title":"Key Id","description":"API key ID to rename"},"label":{"anyOf":[{"type":"string","maxLength":100},{"type":"null"}],"title":"Label","description":"New label for the key"}},"type":"object","required":["key_id"],"title":"RenameKeyRequest","description":"Request to rename an API key."},"ReplayEventRequest":{"properties":{"event_id":{"type":"string","title":"Event Id"}},"type":"object","required":["event_id"],"title":"ReplayEventRequest","description":"Request to replay a specific Stripe event."},"RevokeKeyRequest":{"properties":{"key_id":{"type":"string","minLength":1,"title":"Key Id","description":"API key ID to revoke"}},"type":"object","required":["key_id"],"title":"RevokeKeyRequest","description":"Request to revoke an API key."},"RolloutGuardPreflightChannelStatus":{"properties":{"channel":{"type":"string","enum":["dashboard","paging","automation"],"title":"Channel"},"configured":{"type":"boolean","title":"Configured"},"eligible":{"type":"boolean","title":"Eligible"},"payload_event":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Payload Event"},"reason":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Reason"}},"type":"object","required":["channel","configured","eligible"],"title":"RolloutGuardPreflightChannelStatus","description":"Per-channel readiness status for a rollout-guard rule."},"RolloutGuardPreflightResponse":{"properties":{"ok":{"type":"boolean","title":"Ok"},"enabled":{"type":"boolean","title":"Enabled"},"automation_enabled":{"type":"boolean","title":"Automation Enabled"},"automation_dry_run":{"type":"boolean","title":"Automation Dry Run"},"bearer_token_configured":{"type":"boolean","title":"Bearer Token Configured"},"webhook_timeout_seconds":{"type":"number","title":"Webhook Timeout Seconds"},"max_pending_dispatches":{"type":"integer","title":"Max Pending Dispatches"},"reserved_critical_pending_slots":{"type":"integer","title":"Reserved Critical Pending Slots"},"issues":{"items":{"type":"string"},"type":"array","title":"Issues"},"warnings":{"items":{"type":"string"},"type":"array","title":"Warnings"},"rules":{"items":{"$ref":"#/components/schemas/RolloutGuardPreflightRuleStatus"},"type":"array","title":"Rules"}},"type":"object","required":["ok","enabled","automation_enabled","automation_dry_run","bearer_token_configured","webhook_timeout_seconds","max_pending_dispatches","reserved_critical_pending_slots","issues","warnings","rules"],"title":"RolloutGuardPreflightResponse","description":"Response payload for rollout-guard preflight checks."},"RolloutGuardPreflightRuleStatus":{"properties":{"rule":{"type":"string","enum":["failed_spike_1h","fallback_spike_24h"],"title":"Rule"},"ok":{"type":"boolean","title":"Ok"},"channels":{"items":{"$ref":"#/components/schemas/RolloutGuardPreflightChannelStatus"},"type":"array","title":"Channels"}},"type":"object","required":["rule","ok","channels"],"title":"RolloutGuardPreflightRuleStatus","description":"Preflight readiness for a specific rollout-guard rule."},"RolloutGuardTestAlertRequest":{"properties":{"rule":{"type":"string","enum":["failed_spike_1h","fallback_spike_24h"],"title":"Rule","default":"failed_spike_1h"},"current_rate":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Current Rate"},"baseline_rate":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Baseline Rate"},"current_count":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Current Count"},"baseline_count":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Baseline Count"},"current_total":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Current Total"},"baseline_total":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Baseline Total"}},"type":"object","title":"RolloutGuardTestAlertRequest","description":"Request payload for a synthetic rollout-guard alert."},"RolloutGuardTestAlertResponse":{"properties":{"ok":{"type":"boolean","title":"Ok"},"alert":{"type":"object","title":"Alert"},"results":{"items":{"$ref":"#/components/schemas/RolloutGuardTestDispatchResult"},"type":"array","title":"Results"}},"type":"object","required":["ok","alert","results"],"title":"RolloutGuardTestAlertResponse","description":"Response payload for rollout-guard smoke tests."},"RolloutGuardTestDispatchResult":{"properties":{"channel":{"type":"string","enum":["dashboard","paging","automation"],"title":"Channel"},"configured":{"type":"boolean","title":"Configured"},"attempted":{"type":"boolean","title":"Attempted"},"delivered":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Delivered"},"payload_event":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Payload Event"},"reason":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Reason"}},"type":"object","required":["channel","configured","attempted"],"title":"RolloutGuardTestDispatchResult","description":"Per-channel rollout-guard smoke test result."},"SelfApiKeyCreateRequest":{"properties":{"label":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Label","description":"Optional label to identify the key"},"plan_code":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Plan Code","description":"Plan code to attach the key to (defaults to free)"}},"type":"object","title":"SelfApiKeyCreateRequest","description":"Request payload for creating a key for the current user."},"SelfApiKeyCreateResponse":{"properties":{"api_key":{"type":"string","title":"Api Key"},"key":{"$ref":"#/components/schemas/ApiKeyMe"}},"type":"object","required":["api_key","key"],"title":"SelfApiKeyCreateResponse","description":"Response after creating a key for the current user."},"SelfApiKeyListResponse":{"properties":{"keys":{"items":{"$ref":"#/components/schemas/ApiKeyMe"},"type":"array","title":"Keys"}},"type":"object","required":["keys"],"title":"SelfApiKeyListResponse","description":"List of keys for the current user."},"ServerErrorResponse":{"properties":{"detail":{"type":"string","title":"Detail","description":"Error message"}},"type":"object","required":["detail"],"title":"ServerErrorResponse","description":"Response model for 500 Server Error."},"ServiceUnavailableResponse":{"properties":{"detail":{"type":"string","title":"Detail","description":"Error message"}},"type":"object","required":["detail"],"title":"ServiceUnavailableResponse","description":"Response model for 503 Service Unavailable."},"SessionResponseV2":{"properties":{"access_token":{"type":"string","title":"Access Token","description":"JWT access token (15 min)"},"token_type":{"type":"string","title":"Token Type","description":"Token type","default":"Bearer"},"expires_in":{"type":"integer","title":"Expires In","description":"Token expiry in seconds"},"user":{"type":"object","title":"User","description":"User info (id, email, verified_at)"}},"type":"object","required":["access_token","expires_in","user"],"title":"SessionResponseV2","description":"Response after successful authentication (V2).\n\nReturns access_token in body. Refresh token is set as HttpOnly cookie.","example":{"access_token":"eyJhbGciOiJIUzI1NiIs...","expires_in":900,"token_type":"Bearer","user":{"email":"user@example.com","id":"550e8400-e29b-41d4-a716-446655440000","verified_at":"2024-01-15T10:30:00Z"}}},"StripeEventDTO":{"properties":{"record_id":{"type":"string","title":"Record Id"},"event_id":{"type":"string","title":"Event Id"},"event_type":{"type":"string","title":"Event Type"},"stripe_id":{"type":"string","title":"Stripe Id"},"user_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"User Id"},"api_key_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Api Key Id"},"processed_at":{"type":"string","format":"date-time","title":"Processed At"},"status":{"type":"string","title":"Status"},"metadata":{"anyOf":[{"additionalProperties":{"type":"string"},"type":"object"},{"type":"null"}],"title":"Metadata"},"error_details":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Error Details"}},"type":"object","required":["record_id","event_id","event_type","stripe_id","user_id","api_key_id","processed_at","status"],"title":"StripeEventDTO","description":"DTO for Stripe event information."},"StripeEventsResponse":{"properties":{"events":{"items":{"$ref":"#/components/schemas/StripeEventDTO"},"type":"array","title":"Events"},"count":{"type":"integer","title":"Count"},"last_webhook_received_at":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Last Webhook Received At"}},"type":"object","required":["events","count"],"title":"StripeEventsResponse","description":"Response for listing Stripe events with filters."},"SubmitFeedbackRequest":{"properties":{"feedback_type":{"type":"string","title":"Feedback Type","description":"Moment type: quota_wall, error_clarity, activation, post_verify"},"sentiment":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Sentiment","description":"positive or negative (for binary feedback)"},"selected_option":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Selected Option","description":"Selected radio option (for multiple choice)"},"rating":{"anyOf":[{"type":"integer","maximum":4.0,"minimum":1.0},{"type":"null"}],"title":"Rating","description":"Rating 1-4 (for scale feedback)"},"free_text":{"anyOf":[{"type":"string","maxLength":500},{"type":"null"}],"title":"Free Text","description":"Optional comment"},"context":{"anyOf":[{"type":"object"},{"type":"null"}],"title":"Context","description":"Event-specific context (error_code, plan, etc.)"}},"type":"object","required":["feedback_type"],"title":"SubmitFeedbackRequest"},"SubscriptionInfo":{"properties":{"subscription_status":{"type":"string","title":"Subscription Status","description":"Subscription status: none, active, trialing, past_due, unpaid, canceled"},"current_period_end":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Current Period End","description":"When current billing period ends (ISO 8601). Null if no subscription."},"payment_status":{"type":"string","title":"Payment Status","description":"Payment status: ok, past_due, unpaid. 'ok' if no issues or no subscription."}},"type":"object","required":["subscription_status","payment_status"],"title":"SubscriptionInfo","description":"Stripe subscription status for billing UI.","example":{"current_period_end":"2025-02-15T10:30:00Z","payment_status":"ok","subscription_status":"active"}},"UpdateInvoiceRequest":{"properties":{"client_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Client Id"},"lines":{"anyOf":[{"items":{"$ref":"#/components/schemas/InvoiceLineRequest"},"type":"array"},{"type":"null"}],"title":"Lines"},"service_date":{"anyOf":[{"type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}$"},{"type":"null"}],"title":"Service Date"},"payment_terms_days":{"anyOf":[{"type":"integer","maximum":365.0,"minimum":1.0},{"type":"null"}],"title":"Payment Terms Days"},"notes":{"anyOf":[{"type":"string","maxLength":2000},{"type":"null"}],"title":"Notes"}},"type":"object","title":"UpdateInvoiceRequest","description":"Update a draft invoice."},"UpgradeTrialRequest":{"properties":{"email":{"type":"string","title":"Email","description":"Email of an existing Free user"}},"type":"object","required":["email"],"title":"UpgradeTrialRequest","description":"Request body for one-click trial activation from the J+7 Free upsell email."},"UpgradeTrialResponse":{"properties":{"email":{"type":"string","title":"Email"},"bonus_quota":{"type":"integer","title":"Bonus Quota"},"expires_at":{"type":"string","title":"Expires At"},"message":{"type":"string","title":"Message","default":"Essai Integration activé. Vos 100 opérations sont disponibles."}},"type":"object","required":["email","bonus_quota","expires_at"],"title":"UpgradeTrialResponse","description":"Successful trial activation response."},"UsageResponse":{"properties":{"plan":{"type":"string","title":"Plan","description":"Plan code (free/pro/business)"},"quota":{"type":"integer","title":"Quota","description":"Monthly quota for the plan"},"count":{"type":"integer","title":"Count","description":"Number of validations used in current period"},"period_start":{"type":"string","title":"Period Start"},"period_end":{"type":"string","title":"Period End"}},"type":"object","required":["plan","quota","count","period_start","period_end"],"title":"UsageResponse","description":"Current monthly usage snapshot."},"UsageSummary":{"properties":{"period_start":{"type":"string","title":"Period Start"},"period_end":{"type":"string","title":"Period End"},"count":{"type":"integer","title":"Count"}},"type":"object","required":["period_start","period_end","count"],"title":"UsageSummary","description":"Usage summary for current period."},"UserEntitlementsResponse":{"properties":{"user_id":{"type":"string","title":"User Id","description":"User ID"},"user_email":{"type":"string","title":"User Email","description":"User email"},"entitlements":{"items":{"$ref":"#/components/schemas/EntitlementInfo"},"type":"array","title":"Entitlements","description":"All entitlements"},"active_bonus_total":{"type":"integer","title":"Active Bonus Total","description":"Sum of all active bonus quotas"},"nearest_expiration":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Nearest Expiration","description":"Nearest active entitlement expiration (ISO 8601)"}},"type":"object","required":["user_id","user_email","entitlements","active_bonus_total"],"title":"UserEntitlementsResponse","description":"Response listing a user's entitlements."},"ValidationArtifact":{"properties":{"kind":{"type":"string","title":"Kind","description":"Artefact kind (xsd, schematron, xslt, xsl)"},"path":{"type":"string","title":"Path","description":"Path inside the application package"},"sha256":{"type":"string","title":"Sha256"},"size_bytes":{"type":"integer","title":"Size Bytes"},"version_declared":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Version Declared"},"version_detected":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Version Detected"},"origin":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Origin"}},"type":"object","required":["kind","path","sha256","size_bytes"],"title":"ValidationArtifact"},"ValidationEngineInfo":{"properties":{"artifacts":{"items":{"$ref":"#/components/schemas/ValidationArtifact"},"type":"array","title":"Artifacts"},"pdfa":{"$ref":"#/components/schemas/PdfaEngineInfo"}},"type":"object","required":["artifacts","pdfa"],"title":"ValidationEngineInfo"},"ValidationError":{"properties":{"loc":{"items":{"anyOf":[{"type":"string"},{"type":"integer"}]},"type":"array","title":"Location"},"msg":{"type":"string","title":"Message"},"type":{"type":"string","title":"Error Type"},"input":{"title":"Input"},"ctx":{"type":"object","title":"Context"}},"type":"object","required":["loc","msg","type"],"title":"ValidationError"},"ValidationErrorResponse":{"properties":{"detail":{"type":"string","title":"Detail","description":"Error message","default":"invalid_email"}},"type":"object","title":"ValidationErrorResponse","description":"Response model for 422 Validation Error.","example":{"detail":"invalid_email"}},"ValidationResponse":{"properties":{"valid":{"type":"boolean","title":"Valid","description":"True if the file is valid according to XSD and Schematron rules, False otherwise"},"errors":{"items":{"additionalProperties":{"type":"string"},"type":"object"},"type":"array","title":"Errors","description":"List of validation errors (XSD, Schematron, PDF processing)","examples":[[{"line":"42","message":"Missing required element: InvoiceNumber","type":"xsd_validation_error"}]]},"warnings":{"items":{"additionalProperties":{"type":"string"},"type":"object"},"type":"array","title":"Warnings","description":"List of validation warnings (non-blocking issues)","examples":[[{"message":"Recommended field missing","type":"schematron_report"}]]},"profile":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Profile","description":"Detected Factur-X profile (MINIMUM, BASIC_WL, BASIC, EN16931, EXTENDED, or UNKNOWN)","examples":["EN16931","BASIC","MINIMUM","EXTENDED"]},"message":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Message","description":"Human-readable summary message","examples":["Validation completed","Validation failed"]}},"type":"object","required":["valid"],"title":"ValidationResponse","description":"Response from validation endpoint.\n\nContains the validation result with detailed errors and warnings.","examples":[{"errors":[],"message":"Validation completed","profile":"EN16931","valid":true,"warnings":[]},{"errors":[{"line":"5","message":"Element 'invoice': Missing child element(s). Expected is ( number ).","type":"xsd_validation_error"}],"message":"Validation failed","valid":false,"warnings":[]}]},"ValidationRunDetailResponse":{"properties":{"id":{"type":"string","title":"Id","description":"Validation run UUID"},"created_at":{"type":"string","title":"Created At","description":"ISO-8601 UTC timestamp"},"source":{"type":"string","title":"Source","description":"Source: 'api' | 'web_demo' | 'web_app'"},"is_valid":{"type":"boolean","title":"Is Valid","description":"Whether validation passed"},"profile":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Profile","description":"Detected Factur-X profile"},"error_count":{"type":"integer","title":"Error Count","description":"Number of errors"},"warning_count":{"type":"integer","title":"Warning Count","description":"Number of warnings"},"duration_ms":{"type":"integer","title":"Duration Ms","description":"Validation duration in milliseconds"},"filename":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Filename","description":"Original filename"},"issues_top":{"items":{"$ref":"#/components/schemas/IssueSummaryItem"},"type":"array","title":"Issues Top","description":"Top issues (RGPD-safe: code/field/severity only)"}},"type":"object","required":["id","created_at","source","is_valid","error_count","warning_count","duration_ms"],"title":"ValidationRunDetailResponse","description":"Detailed validation run response (includes issues_top).\n\nRGPD-safe: issues_top contains only code/field/severity."},"ValidationRunItem":{"properties":{"id":{"type":"string","title":"Id","description":"Validation run UUID"},"created_at":{"type":"string","title":"Created At","description":"ISO-8601 UTC timestamp"},"source":{"type":"string","title":"Source","description":"Source: 'api' | 'web_demo' | 'web_app'"},"is_valid":{"type":"boolean","title":"Is Valid","description":"Whether validation passed"},"profile":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Profile","description":"Detected Factur-X profile"},"error_count":{"type":"integer","title":"Error Count","description":"Number of errors"},"warning_count":{"type":"integer","title":"Warning Count","description":"Number of warnings"},"duration_ms":{"type":"integer","title":"Duration Ms","description":"Validation duration in milliseconds"},"filename":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Filename","description":"Original filename"}},"type":"object","required":["id","created_at","source","is_valid","error_count","warning_count","duration_ms"],"title":"ValidationRunItem","description":"Single validation run in list response.\n\nRGPD-safe: Does not expose document_hash, api_key_id, or issues_top_json."},"ValidationRunListResponse":{"properties":{"items":{"items":{"$ref":"#/components/schemas/ValidationRunItem"},"type":"array","title":"Items","description":"Validation runs"},"next_cursor":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Next Cursor","description":"Cursor for next page (base64 encoded JSON)"}},"type":"object","required":["items"],"title":"ValidationRunListResponse","description":"Paginated list of validation runs."},"VerificationRequestedResponse":{"properties":{"message":{"type":"string","title":"Message","description":"Uniform message for all cases","default":"If this email is valid, a verification code has been sent. Check your inbox and use POST /public/api-keys/free/step2 to complete."},"email":{"type":"string","title":"Email","description":"The email address (normalized)"},"expires_in_seconds":{"type":"integer","title":"Expires In Seconds","description":"Token validity period"}},"type":"object","required":["email","expires_in_seconds"],"title":"VerificationRequestedResponse","description":"Response for Step 1: verification token requested.\n\nSECURITY: This response is IDENTICAL regardless of whether:\n- Email is new\n- Email already has an API key\n- Email has a pending verification\n\nThis prevents email enumeration attacks.","example":{"email":"dev@example.com","expires_in_seconds":1800,"message":"If this email is valid, a verification code has been sent. Check your inbox and use POST /public/api-keys/free/step2 to complete."}},"VerifyMagicLinkRequest":{"properties":{"token":{"type":"string","minLength":1,"title":"Token","description":"Magic link token from email"}},"type":"object","required":["token"],"title":"VerifyMagicLinkRequest","description":"Request to verify magic link token."},"app__application__dto__api_keys__PlanInfo":{"properties":{"code":{"type":"string","title":"Code"},"monthly_quota":{"type":"integer","title":"Monthly Quota"}},"type":"object","required":["code","monthly_quota"],"title":"PlanInfo","description":"Plan information returned in API responses."},"app__infrastructure__api__routes__meta__PlanInfo":{"properties":{"code":{"type":"string","title":"Code"},"name":{"type":"string","title":"Name"},"monthly_quota":{"type":"integer","title":"Monthly Quota"},"price":{"anyOf":[{},{"type":"null"}],"title":"Price"}},"type":"object","required":["code","name","monthly_quota"],"title":"PlanInfo"}}}}