ID du formulaire

Webhooks

Payloads de webhook signés, vérification de la signature HMAC-SHA256, calendrier des nouvelles tentatives et logs de livraison.

Les webhooks poussent chaque soumission acceptée vers votre serveur dès son arrivée. Les livraisons sont signées selon le schéma Standard Webhooks, retentées pendant environ trois jours et journalisées tentative par tentative dans le tableau de bord. Des handlers prêts à l'emploi sont dans Recevoir des webhooks.

Ajouter un endpoint

  1. Dans le tableau de bord, ouvrez Webhooks et choisissez Add endpoint.
  2. Saisissez une URL HTTPS et choisissez un formulaire, ou tous les formulaires de l'espace de travail.
  3. Copiez le secret de signature. Il commence par whsec_ et n'est affiché en entier qu'une seule fois.
  4. Envoyez un événement de test pour vérifier votre handler.

URL des endpoints

  • https: uniquement, sur le port par défaut 443, sans nom d'utilisateur ni mot de passe dans l'URL, jusqu'à 2 048 caractères.
  • Un nom d'hôte public. Les adresses IP, localhost et les noms se terminant par .local, .internal, .intranet, .lan, .home.arpa ou .onion sont refusés, tout comme ikiform.com, workers.dev et pages.dev.
  • Le nom d'hôte ne doit résoudre que vers des adresses publiques. Les plages privées, loopback, link-local et carrier-grade NAT sont refusées.
  • L'URL est vérifiée à l'enregistrement puis avant chaque livraison. Un endpoint qui se met à résoudre vers une adresse privée est désactivé.
  • Un espace de travail peut avoir jusqu'à 3 endpoints. Enterprise n'a pas de limite.

Événements

TypeEnvoyé quandRetenté
submission.createdUne soumission est acceptée, ou une soumission spam est marquée comme légitimeOui
endpoint.testVous choisissez Send test sur la page de l'endpointNon, une seule tentative

Le spam ne déclenche jamais de webhook. Les soumissions mises en attente parce que l'espace de travail a dépassé sa limite mensuelle non plus.

La requête

Chaque livraison est un POST avec un corps JSON et trois en-têtes de signature, envoyé par le service de livraison d'Ikiform avec le user agent Ikiform-Webhooks/1.0.

En-têtes
POST /webhooks/ikiform HTTP/1.1
content-type: application/json
user-agent: Ikiform-Webhooks/1.0
webhook-id: dlv_01k7v2d4n8r2p6s0w3y5b7c9e1
webhook-timestamp: 1792300000
webhook-signature: v1,K5oZfzN95Z9UVu1EsfQmfVNQhnkZ2pj9o9NDN/H/pI4=
En-têteValeur
webhook-idL'id de la livraison. Identique à chaque nouvelle tentative de la même livraison : utilisez-le pour ignorer les doublons
webhook-timestampSecondes Unix de la signature de cette tentative. Nouveau à chaque tentative
webhook-signaturev1, suivi d'un HMAC-SHA256 en base64. Plusieurs valeurs séparées par des espaces pendant la rotation d'un secret

Payloads

{
  "id": "dlv_01k7v2d4n8r2p6s0w3y5b7c9e1",
  "type": "submission.created",
  "timestamp": "2026-10-17T12:00:00.000Z",
  "api_version": "2026-10-17",
  "data": {
    "submission_id": "sub_01k7v2c9t3e8m5q4x6z0a1b2c3",
    "form": { "id": "frm_xxxxxxxxxxxxxxxx", "name": "Contact" },
    "fields": { "name": "Ada", "email": "ada@example.com", "message": "Hello" },
    "meta": {
      "user_agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7)",
      "origin": "https://example.com",
      "referrer": "https://example.com/contact",
      "country": "GB",
      "subject": null,
      "reply_to": "ada@example.com"
    },
    "created_at": "2026-10-17T12:00:00.000Z",
    "truncated": false
  }
}
ChampDescription
idL'id de la livraison, identique à webhook-id
typesubmission.created ou endpoint.test
timestampLa date de création de la livraison, en ISO 8601 UTC. Identique à chaque nouvelle tentative
api_versionLa version du payload avec laquelle l'endpoint a été créé, actuellement 2026-10-17
data.submission_idL'id de la soumission. Récupérez-la avec l'API REST
data.formL'id et le name du formulaire
data.fieldsLes valeurs stockées. Avec des champs définis, ce sont les valeurs validées et converties
data.metaLes mêmes clés que le meta de l'API REST. Chaque clé est présente et n'importe quelle valeur peut être null. L'IP du visiteur et son hachage ne sont jamais envoyés
data.meta.user_agentLe user agent du navigateur, jusqu'à 512 caractères
data.meta.origin, referrerLa provenance de l'envoi, quand le navigateur les a transmis
data.meta.countryCode pays à deux lettres, fourni par Cloudflare
data.meta.subject, reply_toIssus des champs _subject et _replyto
data.created_atLa date de stockage de la soumission
data.truncatedtrue quand le corps dépasserait 256 Ko, auquel cas fields est vide

N'importe quelle valeur de meta peut être null. Traitez les valeurs des champs comme des entrées non fiables, exactement comme un envoi de formulaire vers votre propre serveur.

Vérifier la signature

Comment la signature du webhook est calculée
webhook-id.webhook-timestamp.corps brut

HMAC-SHA256, avec pour clé le secret décodé en base64 après whsec_

webhook-signature: v1,base64(signature)

  • Utilisez le corps brut de la requête exactement tel que reçu. Analyser puis resérialiser le JSON modifie les octets.
  • L'en-tête peut contenir plusieurs signatures séparées par des espaces. Acceptez la requête si l'une des signatures v1 correspond.
  • Comparez en temps constant, et rejetez les horodatages éloignés de plus de 5 minutes de l'heure actuelle.

Avec le SDK

verifyWebhook fait tout cela avec Web Crypto et renvoie l'événement analysé, ou lève une WebhookVerificationError avec une reason. Il accepte un objet Headers ou un simple objet comme le req.headers de Node.

app/api/webhooks/ikiform/route.ts
import { verifyWebhook, WebhookVerificationError } from "@ikiform/sdk/webhooks";

export async function POST(request: Request) {
  const payload = await request.text();
  try {
    const event = await verifyWebhook({
      payload,
      headers: request.headers,
      secret: process.env.IKIFORM_WEBHOOK_SECRET!,
    });
    if (event.type === "submission.created") {
      console.log(event.data.fields);
    }
    return new Response(null, { status: 204 });
  } catch (error) {
    if (error instanceof WebhookVerificationError) {
      return new Response(error.reason, { status: 400 });
    }
    throw error;
  }
}

Sans le SDK

N'importe quelle bibliothèque Standard Webhooks fonctionne aussi. À la main, il suffit de quelques lignes :

import { createHmac, timingSafeEqual } from "node:crypto";

const secret = Buffer.from(process.env.IKIFORM_WEBHOOK_SECRET.replace(/^whsec_/, ""), "base64");

export function verify(body, headers) {
  const id = headers["webhook-id"];
  const timestamp = headers["webhook-timestamp"];
  const signatures = headers["webhook-signature"];
  if (!id || !timestamp || !signatures) return false;
  if (Math.abs(Date.now() / 1000 - Number(timestamp)) > 300) return false;
  const expected = createHmac("sha256", secret).update(`${id}.${timestamp}.${body}`).digest();
  return signatures.split(" ").some((entry) => {
    const [version, signature] = entry.split(",");
    if (version !== "v1" || !signature) return false;
    const received = Buffer.from(signature, "base64");
    return received.length === expected.length && timingSafeEqual(received, expected);
  });
}

Vérificateur de signature

Ignorer la fenêtre de 5 minutes

Utilise verifyWebhook du SDK avec Web Crypto dans votre navigateur. Rien de ce que vous collez ne quitte cette page.

Répondre

Votre réponseCe que fait Ikiform
N'importe quel 2xx en moins de 15 secondesMarque la livraison comme réussie
410 GoneFait échouer la livraison et désactive immédiatement l'endpoint
429 ou 503 avec Retry-AfterRéessaie après ce délai, mais pas plus tard que l'étape suivante du calendrier
Une redirectionNon suivie. Compte comme un échec
Tout autre statut, un timeout ou une erreur réseauRéessaie selon le calendrier ci-dessous

Faites le travail lent après avoir répondu, par exemple dans une file d'attente. Les 4 premiers Ko du corps de votre réponse sont enregistrés dans le log de livraison.

Nouvelles tentatives

Une tentative échouée est retentée selon ce calendrier, chaque délai variant aléatoirement de 20 % au plus dans un sens ou dans l'autre. Après la 9e tentative, environ 3 j 6 h après la première, la livraison est marquée comme échouée.

Calendrier des nouvelles tentatives de webhook, avec le délai avant chaque tentative sur une échelle logarithmique
TentativeAttente avantDepuis la 1re
  1. 1maintenant0 s
  2. 25 s5 s
  3. 35 min5 min
  4. 430 min35 min
  5. 52 h2 h 35 min
  6. 65 h7 h 35 min
  7. 710 h17 h 35 min
  8. 81 j1 j 18 h
  9. 91 j 12 h3 j 6 h

Les barres foncées montrent l'attente la plus courte et les barres claires la plus longue, avec 20 % de jitter dans un sens ou dans l'autre. Après la tentative 9, la livraison est marquée comme échouée.

Endpoints désactivés

Si toutes les livraisons vers un endpoint échouent pendant 5 jours, l'endpoint est désactivé et les propriétaires de l'espace de travail reçoivent un e-mail. Une réponse 410 Gone ou une destination qui devient privée le désactive immédiatement. Réactivez-le depuis la page de l'endpoint une fois le problème corrigé.

Logs de livraison

La page de chaque endpoint liste ses livraisons avec leur statut. Dépliez-en une pour voir chaque tentative : quand elle a eu lieu, si elle était automatique, manuelle ou un test, le code de statut, la durée, et les 4 premiers Ko de votre réponse ou l'erreur. Vous pouvez y relancer n'importe quelle livraison. Les tentatives sont conservées 30 jours et les livraisons 90 jours.

Rotation des secrets

Roll signing secret crée un nouveau secret. Pendant les 24 heures suivantes, chaque livraison est signée à la fois avec l'ancien et le nouveau secret : webhook-signature contient alors deux valeurs, et vous pouvez déployer le nouveau secret sans perdre d'événement.