ID du formulaire

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

ID du formulaire
Champs
Corps
Clé d'idempotence
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éthodeCheminRôle
POST/f/<formId>Créer une soumission
OPTIONS/f/<formId>Preflight CORS, toujours 204
GET/f/<formId>/schemaLa définition publique des champs du formulaire, voir shadcn/ui
GET/f/<formId>/thanksLa page de remerciement hébergée
GET/f/<formId>/challengeLa 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-TypeFormeRemarques
application/json et les types +jsonUn seul objet JSONLes 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-urlencodedPaires nom et valeurLes noms répétés et name[] deviennent des tableaux
multipart/form-dataPaires nom et valeurComme urlencoded ; les vrais fichiers sont refusés
Tout le reste415 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êteObligatoireEffet
Content-TypeOuiChoisit l'analyseur du corps, voir ci-dessus
AcceptNonapplication/json force une réponse JSON, voir JSON ou redirection
Idempotency-KeyNonRend les nouvelles tentatives sûres, jusqu'à 255 caractères, voir Idempotence
AuthorizationNonBearer ik_sk_... ou Bearer ik_pub_..., voir Depuis un serveur
Origin, RefererEnvoyé par les navigateursComparé aux domaines autorisés du formulaire

En-têtes de réponse

En-têteQuandValeur
X-Request-IdChaque réponsereq_..., repris en request_id dans les corps d'erreur
Retry-After429Secondes avant que le prochain envoi soit autorisé
LocationSuccès 303 pour un navigateurVotre redirection ou la page de remerciement hébergée
Access-Control-Allow-OriginQuand la requête a un OriginL'origine de la requête elle-même
Access-Control-Expose-HeadersAvec CORSX-Request-Id, Retry-After, pour que le code navigateur puisse lire les deux
Cache-ControlSuccèsno-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.

Preflight
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
L'API REST sous /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" }
Un envoi classé comme spam reçoit quand même 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 avecRésultat
La même clé et les mêmes champsLa première réponse est rejouée. Aucune seconde soumission n'est créée.
La même clé et des champs différents422 idempotency_mismatch
La même clé pendant que la première requête est encore en cours409 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 :

retry.js
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

Générateurs de sites statiques

Créateurs de sites