JavaScript et fetch
L'endpoint de soumission en détail : formats de corps, en-têtes, CORS, réponses et idempotence.
Tout code capable d'envoyer une requête HTTP peut soumettre à Ikiform. Envoyez du JSON, du FormData ou des données urlencoded à https://ikiform.com/f/<formId> et lisez une réponse JSON. Le SDK enveloppe tout cela dans une seule fonction, mais il est facultatif. Pour une mise en place pas à pas sur une stack précise, voir Guides par plateforme.
Avec fetch
const response = await fetch("https://ikiform.com/f/frm_xxxxxxxxxxxxxxxx", {
method: "POST",
headers: { "Content-Type": "application/json", Accept: "application/json" },
body: JSON.stringify({
name: "Ada Lovelace",
email: "ada@example.com",
message: "Hello",
}),
});
const result = await response.json();
if (!result.ok) console.error(result.error.code, result.error.fields);Essayez
curl https://ikiform.com/f/frm_xxxxxxxxxxxxxxxx \
-H "Content-Type: application/json" \
-d '{"name":"Ada Lovelace","email":"ada@example.com","message":"Hello from the Ikiform docs."}'Saisissez l'ID de votre formulaire pour envoyer une vraie requête.
Endpoints
| Méthode | Chemin | Rôle |
|---|---|---|
POST | /f/<formId> | Créer une soumission |
OPTIONS | /f/<formId> | Preflight CORS, toujours 204 |
GET | /f/<formId>/schema | La définition publique des champs du formulaire, voir shadcn/ui |
GET | /f/<formId>/thanks | La page de remerciement hébergée |
GET | /f/<formId>/challenge | La frame de vérification humaine d'Ikiform, utilisée par le SDK |
Ouvrir /f/<formId> dans un onglet de navigateur envoie un GET, qui répond 404 not_found. C'est normal : l'endpoint n'accepte que des POST.
Formats de corps
| Content-Type | Forme | Remarques |
|---|---|---|
application/json et les types +json | Un seul objet JSON | Les valeurs peuvent être des chaînes, des nombres, des booléens, des tableaux et des objets, imbriqués sur 3 niveaux au plus en comptant l'objet racine |
application/x-www-form-urlencoded | Paires nom et valeur | Les noms répétés et name[] deviennent des tableaux |
multipart/form-data | Paires nom et valeur | Comme urlencoded ; les vrais fichiers sont refusés |
| Tout le reste | 415 unsupported_media_type |
Les corps doivent être en UTF-8. Un charset autre que utf-8 reçoit 415 unsupported_charset, tout comme des octets qui ne sont pas de l'UTF-8 valide. Un tableau JSON ou un corps mal formé reçoit 400 invalid_body.
En-têtes
En-têtes de requête
| En-tête | Obligatoire | Effet |
|---|---|---|
Content-Type | Oui | Choisit l'analyseur du corps, voir ci-dessus |
Accept | Non | application/json force une réponse JSON, voir JSON ou redirection |
Idempotency-Key | Non | Rend les nouvelles tentatives sûres, jusqu'à 255 caractères, voir Idempotence |
Authorization | Non | Bearer ik_sk_... ou Bearer ik_pub_..., voir Depuis un serveur |
Origin, Referer | Envoyé par les navigateurs | Comparé aux domaines autorisés du formulaire |
En-têtes de réponse
| En-tête | Quand | Valeur |
|---|---|---|
X-Request-Id | Chaque réponse | req_..., repris en request_id dans les corps d'erreur |
Retry-After | 429 | Secondes avant que le prochain envoi soit autorisé |
Location | Succès 303 pour un navigateur | Votre redirection ou la page de remerciement hébergée |
Access-Control-Allow-Origin | Quand la requête a un Origin | L'origine de la requête elle-même |
Access-Control-Expose-Headers | Avec CORS | X-Request-Id, Retry-After, pour que le code navigateur puisse lire les deux |
Cache-Control | Succès | no-store |
CORS
L'endpoint de soumission accepte les envois cross-origin depuis n'importe quel site. Les preflights réussissent toujours et renvoient l'origine de la requête. Aucun cookie ni identifiant n'est utilisé, donc ce n'est pas CORS qui protège votre formulaire, ce sont les domaines autorisés. Avec des domaines autorisés définis, un envoi depuis un autre site est stocké comme spam ou refusé, selon le réglage du formulaire. Voir Domaines autorisés.
OPTIONS /f/frm_xxxxxxxxxxxxxxxx HTTP/1.1
Origin: https://example.com
Access-Control-Request-Method: POST
Access-Control-Request-Headers: content-type
HTTP/1.1 204 No Content
Access-Control-Allow-Origin: https://example.com
Access-Control-Allow-Methods: POST
Access-Control-Allow-Headers: Content-Type, Accept, Idempotency-Key, Authorization
Access-Control-Max-Age: 86400
Vary: Origin/api/v1 est différente : elle n'envoie aucun en-tête CORS et répond aux preflights des navigateurs avec 405, donc une clé secrète ne peut jamais être utilisée depuis une page web.Réponses
Un succès renvoie 201 avec l'id de la nouvelle soumission. Les erreurs utilisent partout la même enveloppe, avec les erreurs de champ indexées par id de champ quand la validation échoue. Tous les codes sont listés sur Erreurs.
{ "ok": true, "id": "sub_01k7v2c9t3e8m5q4x6z0a1b2c3" }201. Le spam est stocké à part et n'atteint jamais vos webhooks ni votre boîte de réception.Idempotence
Envoyez un en-tête Idempotency-Key, ou un champ _idempotency, pour rendre les nouvelles tentatives sûres. Les clés font jusqu'à 255 caractères et sont limitées au formulaire pendant 24 heures. L'en-tête l'emporte si les deux sont envoyés.
| Nouvelle tentative avec | Résultat |
|---|---|
| La même clé et les mêmes champs | La première réponse est rejouée. Aucune seconde soumission n'est créée. |
| La même clé et des champs différents | 422 idempotency_mismatch |
| La même clé pendant que la première requête est encore en cours | 409 idempotency_in_progress |
Les envois qui échouent avant le stockage de la soumission, comme les erreurs de validation, ne conservent pas la clé : vous pouvez corriger les données et réessayer avec la même clé. Une boucle de nouvelles tentatives sûre :
const key = crypto.randomUUID();
async function send(attempt = 1) {
const response = await fetch("https://ikiform.com/f/frm_xxxxxxxxxxxxxxxx", {
method: "POST",
headers: { "Content-Type": "application/json", "Idempotency-Key": key },
body: JSON.stringify({ email: "ada@example.com" }),
}).catch(() => null);
if (response && response.status < 500 && response.status !== 429) return response.json();
if (attempt === 3) throw new Error("Ikiform is unreachable");
const wait = Number(response?.headers.get("Retry-After")) || attempt * 2;
await new Promise((resolve) => setTimeout(resolve, wait * 1000));
return send(attempt + 1);
}Guides par plateforme
Frameworks
- Next.jsUn composant client, ou une server action.
- ReactÉtats d'attente, d'erreur et de succès.
- VueUn composant monofichier.
- NuxtEnvoie avant l'hydratation, puis avec
$fetch. - SvelteUn composant Svelte 5 et une server action.
- SvelteKitUne page prérendue avec amélioration progressive.
- AstroUn formulaire sans JavaScript, amélioré si vous le souhaitez.
- GatsbyUn formulaire statique, ou une Gatsby Function.
- JavaScript pur
fetchavec erreurs affichées en ligne.
Générateurs de sites statiques
- HugoUn partial et un shortcode pour les fichiers de contenu.
- JekyllUn include, et la configuration GitHub Pages.
- EleventyUn shortcode pour Nunjucks, Liquid et Markdown.
Créateurs de sites
- WebflowLe Form Block avec une action personnalisée.
- FramerUn webhook de formulaire, ou un composant de code.
- WordPressLe bloc Custom HTML, ou un shortcode.
- WixUn embed HTML qui envoie avec
fetch. - SquarespaceUn bloc de code, avec ou sans JavaScript.
- CarrdUn formulaire personnalisé qui envoie vers une URL.