# Comment ça marche

> Chaque contrôle que traverse une soumission, dans l'ordre, et ce que chacun peut renvoyer.

Chaque envoi vers `/f/<formId>` traverse le même pipeline. Les contrôles peu coûteux passent en premier, donc un flood ou un formulaire inconnu ne coûte presque rien. Le premier contrôle qui échoue détermine la réponse.

Le pipeline, dans l'ordre :

1. Garde anti-flood par IP : au plus 600 envois par minute depuis une même IP, tous formulaires confondus, avant même la recherche du formulaire. Échoue avec `429 rate_limited`.
2. Recherche du formulaire : les formulaires inconnus, supprimés ou désactivés échouent avec `404 not_found`.
3. Limites de débit du formulaire : le préréglage du formulaire, compté par IP de visiteur et par formulaire. Échoue avec `429 rate_limited`.
4. Les formulaires en pause échouent avec `403 form_paused`.
5. Analyse du corps : urlencoded, multipart ou JSON, 256 Ko au maximum. Échoue avec `415`, `400 invalid_body`, `413 payload_too_large`, `422 too_deep` ou `422 files_not_supported`.
6. Clé API, quand un en-tête `Authorization` est envoyé. Échoue avec `403 forbidden` ou `403 origin_not_allowed`.
7. Domaines autorisés : un envoi depuis un autre site est stocké comme spam, ou échoue avec `403 origin_not_allowed`.
8. Vérification humaine : échoue avec `403 challenge_required` ou `403 captcha_failed`. Les envois de formulaire depuis un navigateur reçoivent à la place une page de vérification.
9. Validation des champs : échoue avec `422 validation_failed`.
10. Score de spam : un score de 5 ou plus est stocké comme spam mais reçoit quand même une réponse de succès.
11. Idempotence : une clé répétée rejoue la première réponse, ou échoue avec `409` ou `422`.
12. Usage mensuel : un dépassement important de la limite échoue avec `429 quota_exceeded`.
13. Stockage : `201` avec l'id de la soumission, ou une redirection `303` pour les envois depuis un navigateur. Suivent les webhooks, les e-mails de notification et la mise à jour du tableau de bord en direct.

## Le rôle de chaque étape

### Les limites de débit avant tout

Les limites de débit s'appliquent avant la lecture du corps. La garde anti-flood (600 par minute et par IP) passe même avant la recherche du formulaire, donc un flood n'atteint jamais la base de données. Les règles propres au formulaire s'appliquent juste après la recherche. Les requêtes refusées n'entament pas le quota. Les chiffres exacts sont sur [Limites de débit](https://ikiform.com/fr/docs/rate-limits.md).

### Analyse

Le corps est analysé en urlencoded, multipart ou JSON. Le texte est décodé en UTF-8 et normalisé en NFC, les CRLF deviennent des LF et les caractères de contrôle sont supprimés. Les noms qui commencent par `_` sont des instructions réservées, comme `_redirect` et `_subject`, et ne sont jamais stockés comme réponses. Voir [Formulaires HTML](https://ikiform.com/fr/docs/html.md#reserved-fields).

### Qui l'a envoyé

Un en-tête `Authorization: Bearer` est facultatif. Une clé secrète (`ik_sk_`) avec la portée `submissions:write` contourne le contrôle des domaines autorisés et la vérification humaine d'Ikiform. Une clé publiable (`ik_pub_`) ne fonctionne que depuis les domaines listés sur la clé. Sans clé, l'envoi est jugé selon son `Origin`.

### Spam

Le spam n'est jamais une erreur. Un envoi qui échoue à la règle des domaines autorisés en mode spam, ou dont le score atteint 5 ou plus, est stocké avec le statut `spam` et reçoit la même réponse de succès qu'un vrai envoi : les bots n'apprennent rien. Le spam n'envoie ni webhook ni e-mail et ne compte pas dans l'usage. Voir [Protection anti-spam](https://ikiform.com/fr/docs/spam.md).

### Stocker et livrer

La soumission, ses livraisons de webhook et son e-mail de notification sont écrits en un seul lot dans la base de données, puis confiés à une file d'attente. La livraison des webhooks et l'e-mail ne ralentissent jamais la réponse, et une soumission n'est jamais stockée sans ses livraisons.

## JSON ou redirection

Le même endpoint répond aux navigateurs avec des pages et au code avec du JSON. Ikiform décide à chaque requête :

| Requête | Réponse |
| --- | --- |
| `Accept` contient `application/json` | JSON |
| Le corps est du JSON | JSON |
| `Sec-Fetch-Mode: navigate` (un envoi de formulaire classique) | Redirection ou page hébergée |
| Tout autre `Sec-Fetch-Mode`, comme `cors` depuis `fetch` | JSON |
| Pas de `Sec-Fetch-Mode`, et `Accept` contient `text/html` | Redirection ou page hébergée |
| Tout le reste, comme curl | JSON |

En mode JSON, un succès renvoie `201 { "ok": true, "id": "sub_..." }`. En mode page, un succès est une redirection `303`, et les erreurs affichent une page d'erreur hébergée qui liste les problèmes par libellé de champ.

## Une fois la soumission stockée

| Quoi | Quand | Détails |
| --- | --- | --- |
| Webhooks | Quelques secondes après, avec de nouvelles tentatives pendant environ 3 jours | [Webhooks](https://ikiform.com/fr/docs/webhooks.md) |
| E-mail de notification | Quelques secondes après, si les notifications sont activées pour le formulaire | Plafonné à 50 e-mails par formulaire et par heure (10 pour les espaces de travail de moins de 7 jours), puis un récapitulatif horaire |
| Tableau de bord | Immédiatement, en direct | Le spam apparaît sous le filtre spam |
| API REST | Immédiatement | [API REST](https://ikiform.com/fr/docs/api.md) |

> Les formulaires dont les soumissions contiennent des noms de champs évoquant un mot de passe, une carte bancaire ou une phrase de récupération sont signalés, et leurs e-mails de notification sont retenus pour vérification. Ne collectez pas d'identifiants avec un backend de formulaires.

## Identifiants

| Id | Format | Remarques |
| --- | --- | --- |
| Formulaire | `frm_xxxxxxxxxxxxxxxx` | `frm_` suivi de 16 caractères aléatoires. Public : il figure dans l'URL d'action de votre formulaire. |
| Soumission | `sub_01k7v2c9t3e8m5q4x6z0a1b2c3` | `sub_` suivi de 26 caractères, triés par date. |
| Livraison de webhook | `dlv_...` | Envoyé dans `webhook-id` ; reste identique d'une tentative à l'autre. |
| Requête | `req_...` | Chaque réponse a un en-tête `X-Request-Id`. Indiquez-le quand vous contactez l'assistance. |

**201** (`Succès en JSON`)

```json
{ "ok": true, "id": "sub_01k7v2c9t3e8m5q4x6z0a1b2c3" }
```

**303** (`Succès dans le navigateur`)

```http
HTTP/1.1 303 See Other
Location: https://example.com/thanks
X-Request-Id: req_01k7v2c9t3e8m5q4x6z0a1b2c3
```
