Guides13 min de lecture
Vérifier les signatures des webhooks de formulaire
Vérifiez qu'un webhook de formulaire vient bien d'Ikiform : le schéma HMAC-SHA256 de Standard Webhooks, un vecteur de test, les corps bruts, les timestamps et la rotation du secret.
Recalculez vous-même la signature et comparez-la à celle de la requête. Ikiform suit Standard Webhooks : la signature est un HMAC-SHA256 en base64 de webhook-id, webhook-timestamp et du corps brut joints par des points, avec pour clé la partie décodée en base64 de votre secret whsec_, et envoyé sous la forme v1,<signature>. N'acceptez la requête que si une signature v1 correspond lors d'une comparaison en temps constant et que le timestamp date de moins de 5 minutes.
Un endpoint de webhook est une URL publique qui accepte les requêtes POST de n'importe qui. Sans vérification de signature, quiconque découvre l'URL peut injecter de fausses soumissions dans votre CRM, votre base de données ou le chat de votre équipe. La vérification tient en quelques lignes de code, et ce guide porte justement sur l'art d'écrire ces lignes exactement comme il faut.
Comment fonctionne la signature
Ikiform et votre serveur partagent un secret, affiché une seule fois quand vous ajoutez l'endpoint. Avant chaque tentative de livraison, Ikiform hache la requête avec ce secret et place le résultat dans un en-tête. Votre serveur refait le calcul sur ce qu'il a reçu. Seul quelqu'un qui détient le secret peut produire une valeur correspondante, et toute modification du corps, de l'identifiant ou du timestamp la casse.
- Ikiform sérialise l'événement en JSON.
- Il joint l'identifiant de livraison, le timestamp Unix de la tentative et le corps avec des points.
- Il calcule un HMAC-SHA256 de cette chaîne avec chaque secret actif de l'endpoint.
- Il envoie chaque résultat, encodé en base64 et préfixé par v1, dans l'en-tête webhook-signature.
- Votre gestionnaire lit le corps brut et les trois en-têtes avant d'analyser quoi que ce soit.
- Il calcule le même HMAC avec sa copie du secret.
- Il compare en temps constant avec chaque signature v1 et vérifie que le timestamp date de moins de 5 minutes.
- S'il y a correspondance, il répond 2xx et traite l'événement. Sinon, il répond 400 et ne fait rien.
Le schéma, précisément
Ikiform utilise Standard Webhooks, une spécification ouverte que partagent plusieurs fournisseurs de webhooks. Chaque livraison porte trois en-têtes :
| En-tête | Valeur | Remarques |
|---|---|---|
webhook-id | dlv_... | L'identifiant de livraison. Identique à chaque nouvelle tentative, il sert donc aussi de clé d'idempotence |
webhook-timestamp | Secondes Unix, comme 1792300000 | Le moment où cette tentative a été signée. Nouveau à chaque nouvelle tentative |
webhook-signature | v1, suivi de base64 | Une ou plusieurs valeurs séparées par des espaces. Deux pendant la rotation d'un secret |
webhook-id: dlv_01k7v2d4n8r2p6s0w3y5b7c9e1
webhook-timestamp: 1792300000
raw body: {"id":"dlv_01k7...","type":"endpoint.test",...}
signed content = webhook-id + "." + webhook-timestamp + "." + raw body
key = base64-decode(secret without "whsec_")
signature = base64(HMAC-SHA256(key, signed content))
header = "v1," + signaturePour vérifier une livraison, dans cet ordre :
- Lisez le corps brut en octets ou en chaîne exacte. Ne l'analysez pas encore.
- Lisez les trois en-têtes. S'il en manque un, rejetez.
- Vérifiez que le timestamp est un entier à moins de 5 minutes de votre horloge, dans un sens ou dans l'autre. Sinon, rejetez.
- Retirez
whsec_de votre secret et décodez le reste en base64. Ces 32 octets sont la clé HMAC, pas la chaîne elle-même. - Calculez le HMAC-SHA256 de
{webhook-id}.{webhook-timestamp}.{raw body}et encodez-le en base64 avec l'alphabet standard et le padding. - Découpez
webhook-signaturesur les espaces. Pour chaque entrée, découpez sur la première virgule ; ignorez tout ce dont la version n'est pasv1. - Comparez chaque valeur
v1à la vôtre en temps constant. Si l'une correspond, la livraison est authentique. - Seulement maintenant, analysez le JSON et agissez en conséquence.
Un vecteur de test
Avant de déployer un gestionnaire, prouvez qu'il calcule la bonne valeur. Ces entrées sont un vrai corps de livraison endpoint.test signé avec un secret inventé. Votre code doit produire exactement la signature de la dernière ligne. Chaque guide par langage inclut un test unitaire qui les utilise.
| Entrée | Valeur |
|---|---|
| Secret | whsec_Fg8c6hm1Gt41v5RSPKm72LggDx8S1mlWkbBYpHPAnpw= |
webhook-id | dlv_01k7v2d4n8r2p6s0w3y5b7c9e1 |
webhook-timestamp | 1792300000 |
| Corps brut | Les octets exacts du script ci-dessous, sans espaces ni saut de ligne |
webhook-signature attendu | v1,Duq+Iuh0xbRZyS+C/TO5W1hcGLGwg+ifBiWiYkq15qA= |
Vous pouvez le vérifier sans écrire de code. Cette commande s'exécute dans un terminal sous macOS ou Linux et affiche la partie base64 de la signature :
SECRET='whsec_Fg8c6hm1Gt41v5RSPKm72LggDx8S1mlWkbBYpHPAnpw='
ID='dlv_01k7v2d4n8r2p6s0w3y5b7c9e1'
TS='1792300000'
BODY='{"id":"dlv_01k7v2d4n8r2p6s0w3y5b7c9e1","type":"endpoint.test","timestamp":"2026-10-17T12:00:00.000Z","api_version":"2026-10-17","data":{"endpoint_id":"whk_01k7v2e1b3d5f7h9k1m3n5q7s9","message":"This is a test event from Ikiform."}}'
KEY=$(printf '%s' "${SECRET#whsec_}" | base64 -d | xxd -p -c 256)
printf '%s.%s.%s' "$ID" "$TS" "$BODY" \
| openssl dgst -sha256 -mac HMAC -macopt "hexkey:$KEY" -binary \
| base64Deux autres cas méritent chacun un test, car ce sont les deux causes d'échec réelles les plus fréquentes :
| Modification | Signature | Votre gestionnaire doit-il l'accepter ? |
|---|---|---|
| Le même corps mis en forme avec une indentation de deux espaces | v1,se0TDhknrG0hYMvCId83HnpZfJcELG3ozxFJktRUHqY= | Non. Même JSON, octets différents |
webhook-timestamp une seconde plus tard | v1,oBfWyLDPfrwVSo40e6b7B7+LmKkZX7OqT88SsrpY6ks= | Non. Le timestamp est signé |
| Signé avec un second secret pendant une rotation | v1,gfHP3s7ANdIpnM7gewtWtSvTrHgmsbEFgCaeRRR0Eak= | Oui, si l'en-tête contient aussi une valeur qui correspond à votre secret |
Pendant une rotation de secret, l'en-tête porte les deux valeurs. Votre gestionnaire, avec l'un ou l'autre secret, doit accepter ceci :
webhook-id: dlv_01k7v2d4n8r2p6s0w3y5b7c9e1
webhook-timestamp: 1792300000
webhook-signature: v1,Duq+Iuh0xbRZyS+C/TO5W1hcGLGwg+ifBiWiYkq15qA= v1,gfHP3s7ANdIpnM7gewtWtSvTrHgmsbEFgCaeRRR0Eak=Façons de vérifier
| Approche | Points forts | Compromis |
|---|---|---|
verifyWebhook de @ikiform/sdk/webhooks | Un seul appel qui vérifie tout et renvoie un événement typé. Fonctionne sur Node, Workers, Deno et Bun | JavaScript et TypeScript uniquement |
| Une bibliothèque Standard Webhooks | Du code maintenu pour Python, Go, PHP, Ruby, JavaScript, Rust, Java, C# et Elixir | Une dépendance de plus. Vérifiez comment chacune attend les en-têtes ; certaines sont sensibles à la casse |
| Votre propre code HMAC | Aucune dépendance, une vingtaine de lignes, et vous pouvez tester chaque branche | Facile de se tromper subtilement : comparaison de chaînes, mauvais octets de clé ou corps analysé |
| Un chemin secret ou un jeton dans l'URL à la place | Simple | Fuit via les journaux, les proxys et les outils de suivi d'erreurs, ne prouve rien sur le corps et ne peut pas expirer. Ce n'est pas un substitut |
Ikiform signe chaque livraison, et la signature est le moyen pris en charge pour l'authentifier. Sa documentation ne publie pas de liste fixe d'adresses IP d'envoi, donc une liste d'IP autorisées n'est pas une vérification fiable.
Vérifier dans votre framework
Pour les stacks JavaScript, le SDK fait le travail. Ce qui change d'un framework à l'autre, c'est l'emplacement de la route et la façon d'obtenir le corps avant que quoi que ce soit ne l'analyse. Choisissez le vôtre :
Un route handler de l'App Router. request.text() est le corps brut ; appeler request.json() d'abord le perdrait. Le guide sur votre propre serveur ajoute la déduplication et after().
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!,
});
console.info(event.type, request.headers.get("webhook-id"));
return new Response(null, { status: 204 });
} catch (error) {
if (error instanceof WebhookVerificationError) return new Response(error.reason, { status: 400 });
throw error;
}
}Un endpoint avec un export POST. Il nécessite le rendu à la demande, donc gardez prerender = false et un adaptateur. getSecret depuis astro:env/server lit IKIFORM_WEBHOOK_SECRET à l'exécution et uniquement sur le serveur ; import.meta.env l'intégrerait dans le build.
import type { APIRoute } from "astro";
import { getSecret } from "astro:env/server";
import { verifyWebhook, WebhookVerificationError } from "@ikiform/sdk/webhooks";
export const prerender = false;
export const POST: APIRoute = async ({ request }) => {
const payload = await request.text();
try {
const event = await verifyWebhook({
payload,
headers: request.headers,
secret: getSecret("IKIFORM_WEBHOOK_SECRET") ?? "",
});
console.info(event.type, event.id);
return new Response(null, { status: 204 });
} catch (error) {
if (error instanceof WebhookVerificationError) return new Response(error.reason, { status: 400 });
throw error;
}
};Une route serveur : la même route basée sur les fichiers, avec une fonction server.handlers.POST qui reçoit la Request standard.
import { createFileRoute } from "@tanstack/react-router";
import { verifyWebhook, WebhookVerificationError } from "@ikiform/sdk/webhooks";
export const Route = createFileRoute("/api/webhooks/ikiform")({
server: {
handlers: {
POST: async ({ request }) => {
const payload = await request.text();
try {
const event = await verifyWebhook({
payload,
headers: request.headers,
secret: process.env.IKIFORM_WEBHOOK_SECRET!,
});
console.info(event.type, event.id);
return new Response(null, { status: 204 });
} catch (error) {
if (error instanceof WebhookVerificationError) return new Response(error.reason, { status: 400 });
throw error;
}
},
},
},
});Une resource route : un module avec une action et sans export par défaut, enregistré dans routes.ts. Elle répond directement à Ikiform au lieu de rendre une page.
import { type RouteConfig, index, route } from "@react-router/dev/routes";
export default [
index("routes/home.tsx"),
route("webhooks/ikiform", "routes/webhooks.ikiform.ts"),
] satisfies RouteConfig;import { verifyWebhook, WebhookVerificationError } from "@ikiform/sdk/webhooks";
import type { Route } from "./+types/webhooks.ikiform";
export async function action({ request }: Route.ActionArgs) {
if (request.method !== "POST") return new Response(null, { status: 405 });
const payload = await request.text();
try {
const event = await verifyWebhook({
payload,
headers: request.headers,
secret: process.env.IKIFORM_WEBHOOK_SECRET!,
});
console.info(event.type, event.id);
return new Response(null, { status: 204 });
} catch (error) {
if (error instanceof WebhookVerificationError) return new Response(error.reason, { status: 400 });
throw error;
}
}Un endpoint +server.ts. La vérification CSRF intégrée de SvelteKit ne bloque que les types de contenu de formulaire cross-site, et Ikiform envoie application/json, donc aucune modification de config n'est nécessaire. Déclarez IKIFORM_WEBHOOK_SECRET dans src/env.ts avec defineEnvVars pour que $app/env/private puisse le lire.
import { verifyWebhook, WebhookVerificationError } from "@ikiform/sdk/webhooks";
import { IKIFORM_WEBHOOK_SECRET } from "$app/env/private";
import type { RequestHandler } from "./$types";
export const POST: RequestHandler = async ({ request }) => {
const payload = await request.text();
try {
const event = await verifyWebhook({ payload, headers: request.headers, secret: IKIFORM_WEBHOOK_SECRET });
console.info(event.type, event.id);
return new Response(null, { status: 204 });
} catch (error) {
if (error instanceof WebhookVerificationError) return new Response(error.reason, { status: 400 });
throw error;
}
};Une route serveur Nitro. readRawBody renvoie le corps sous forme de chaîne avant que quoi que ce soit ne l'analyse. Définissez NUXT_IKIFORM_WEBHOOK_SECRET et déclarez ikiformWebhookSecret sous runtimeConfig dans nuxt.config.ts.
import { verifyWebhook, WebhookVerificationError } from "@ikiform/sdk/webhooks";
export default defineEventHandler(async (event) => {
const payload = (await readRawBody(event, "utf8")) ?? "";
const { ikiformWebhookSecret } = useRuntimeConfig(event);
try {
const delivery = await verifyWebhook({
payload,
headers: getRequestHeaders(event),
secret: ikiformWebhookSecret,
});
console.info(delivery.type, delivery.id);
setResponseStatus(event, 204);
return null;
} catch (error) {
if (error instanceof WebhookVerificationError) {
throw createError({ status: 400, message: error.reason });
}
throw error;
}
});Chaque gestionnaire répond 204 en cas de succès et 400 avec la raison de l'échec sinon. Faites le travail lent après avoir répondu ; Ikiform attend 15 secondes avant de compter une tentative comme échouée.
Pourquoi le corps brut compte
La signature couvre des octets, pas un sens. {"a":1} et { "a": 1 } sont le même JSON mais des octets différents, donc un corps analysé puis resérialisé ne correspond presque jamais. Il en va de même pour tout ce qui touche au corps à l'arrivée : un middleware qui analyse le JSON, un proxy qui réencode, ou un framework qui vous donne un objet au lieu d'une chaîne.
Lisez le corps une seule fois, en octets ou en chaîne, vérifiez-le, puis analysez cette même chaîne. Chaque guide par langage montre où se trouve le corps brut dans ses frameworks, et le guide sur votre propre serveur contient un tableau pour les frameworks Node et Python courants.
Comparer en temps constant
Une comparaison de chaînes normale s'arrête au premier caractère différent. En théorie, un attaquant capable de chronométrer des milliers de requêtes apprend combien de caractères initiaux d'une tentative sont justes et construit une signature valide caractère par caractère. Une comparaison en temps constant prend le même temps quelle que soit la part qui correspond. Chaque langage en a une :
| Langage | Comparaison en temps constant |
|---|---|
| Node.js | crypto.timingSafeEqual(a, b), qui lève une exception si les longueurs diffèrent, donc vérifiez d'abord la longueur |
| Python | hmac.compare_digest(a, b) |
| Go | hmac.Equal(a, b) |
| PHP | hash_equals($known, $user) |
| Ruby | OpenSSL.secure_compare(a, b), ou ActiveSupport::SecurityUtils.secure_compare dans Rails |
Timestamps et rejeux
Une requête valide capturée une fois reste valide pour toujours, à moins que quelque chose ne la fasse expirer. Le timestamp signé joue ce rôle : rejetez toute livraison à plus de 5 minutes de votre horloge, dans un sens ou dans l'autre. Comme Ikiform signe à nouveau chaque tentative avec un timestamp récent, une nouvelle tentative trois jours plus tard passe encore ; seule une ancienne requête copiée échoue.
Dans ces 5 minutes, un rejeu reste possible, tout comme un véritable doublon : Ikiform réessaie quand il ne reçoit pas de 2xx à temps, même si votre gestionnaire a terminé. Stockez webhook-id avec une contrainte d'unicité et ignorez les identifiants déjà traités. Cela règle les deux cas.
timestamp_too_old ou timestamp_too_new. Les plateformes managées synchronisent généralement les horloges ; sur votre propre VM, exécutez NTP.Faire tourner le secret
Faites tourner le secret quand quelqu'un qui le connaissait s'en va, quand il a pu être journalisé ou commité, ou selon un calendrier. Roll signing secret sur la page de l'endpoint en crée un nouveau, et pendant 24 heures chaque livraison est signée avec les deux :
- Avant la rotation, chaque livraison porte une seule signature, faite avec l'ancien secret.
- Vous choisissez Roll signing secret et copiez le nouveau secret.
- Pendant les 24 heures suivantes, chaque livraison porte deux signatures, donc un serveur qui détient l'un ou l'autre secret l'accepte. Déployez le nouveau secret pendant cette fenêtre.
- Après 24 heures, les livraisons ne portent plus que la nouvelle signature, et un serveur qui détient encore l'ancien secret les rejette.
Si l'ancien secret est toujours déployé après 24 heures, chaque livraison échoue. Ikiform réessaie chacune 8 fois sur environ trois jours, donc une fois le bon secret déployé, utilisez Retry dans le journal de livraison pour tout ce qui a déjà abandonné.
Après la vérification
Une signature valide prouve que la requête vient d'Ikiform et n'a pas été modifiée. Elle ne prouve pas que le contenu est sûr ou nouveau. Trois habitudes complètent le travail :
- Ignorez les répétitions. Stockez
webhook-id, ou l'iddu payload, qui a la même valeur, avec une contrainte d'unicité. Une nouvelle tentative d'une livraison déjà traitée devient sans effet. - Répondez d'abord, travaillez ensuite. Renvoyez
2xxdès que l'événement est stocké ou mis en file d'attente. Ikiform compte comme un échec tout ce qui dépasse 15 secondes et réessaie, ce qui ramène le problème des doublons. - Considérez les champs comme non fiables.
data.fieldscontient ce qu'un visiteur a tapé dans un formulaire public. Échappez-le avant de l'afficher en HTML, dans un message de chat ou dans un e-mail, et ne le passez jamais à un shell ou à une requête non paramétrée.
Tester avant la mise en production
Ikiform ne livre qu'à des URL https: publiques sur le port 443, donc localhost ne peut pas recevoir de webhook. Lancez un tunnel comme cloudflared tunnel --url http://localhost:3000 et ajoutez l'URL qu'il affiche comme second endpoint pendant que vous travaillez. Send test sur la page de l'endpoint envoie une fois un événement endpoint.test, sans nouvelles tentatives, signé exactement comme une vraie livraison. Supprimez l'endpoint de test quand vous avez terminé ; il a son propre secret, donc rien d'autre ne change.
Si une livraison échoue et que vous ne voyez pas pourquoi, copiez ses en-têtes et son corps depuis vos journaux dans le vérificateur de signature. Il exécute la même vérification dans le navigateur et vous indique quelle étape a échoué.
Cinq façons de rater la vérification
| Erreur | Symptôme | Solution |
|---|---|---|
Utiliser toute la chaîne whsec_... comme clé | no_matching_signature à chaque livraison | Retirez whsec_, puis décodez en base64. La clé fait 32 octets |
| Vérifier un corps analysé | no_matching_signature, parfois seulement pour certains payloads | Vérifiez le corps brut ; analysez ensuite |
| Ne vérifier que la première signature | Des échecs qui commencent juste après une rotation | Parcourez chaque entrée v1 séparée par des espaces |
| Hexadécimal au lieu de base64 | no_matching_signature | Standard Webhooks utilise le base64 standard avec padding, pas l'hexadécimal ni le base64url |
| Chercher les en-têtes avec la casse exacte | missing_headers derrière certains proxys | Les noms d'en-têtes ne sont pas sensibles à la casse. Ikiform les envoie en minuscules ; recherchez-les sans tenir compte de la casse |
Il n'y a pas d'intégrations en un clic. Chaque outil est atteint via un webhook signé, directement ou via un outil d'automatisation comme Zapier ou n8n. Un espace de travail peut avoir 3 endpoints, chacun avec son propre secret. Le payload complet, le calendrier des nouvelles tentatives et le journal de livraison se trouvent dans Webhooks.
Choisissez votre cas
Chaque page par langage contient des gestionnaires idiomatiques, une option avec bibliothèque et un test unitaire qui exécute le vecteur de test de cette page.
- Node.jsVérifiez les signatures des webhooks Ikiform en Node.js : corps bruts dans Express et Fastify, le SDK, le package standardwebhooks et une version node:crypto testée.
- PythonVérifiez les signatures des webhooks Ikiform dans Flask, FastAPI et Django, avec le package standardwebhooks ou la bibliothèque standard, plus un test pytest.
- Go net/httpVérifiez les signatures des webhooks Ikiform en Go avec net/http : lisez le corps une seule fois, vérifiez-le avec crypto/hmac, testez-le, ou utilisez la bibliothèque Standard Webhooks.
- PHPVérifiez les signatures des webhooks Ikiform en PHP simple avec hash_hmac et hash_equals, et dans Laravel avec la bibliothèque Standard Webhooks et une exception CSRF.
- RubyVérifiez les signatures des webhooks Ikiform dans Sinatra avec OpenSSL et un test Minitest, et dans Rails avec la gem standardwebhooks et request.raw_post.
Vérifiez que tout fonctionne
Faites-le d'abord sur un endpoint de staging, puis à nouveau en production. Ikiform n'a pas d'offre gratuite. Les tests sont gratuits : un espace de travail sans offre accepte 50 soumissions de test par mois depuis localhost. Pour passer en production, choisissez Pro à partir de 6 $/mois pour 500 soumissions, ou payez une seule fois : 10 000 soumissions pour 49 $ ou 50 000 soumissions pour 149 $, avec des crédits qui n'expirent jamais. Voir les tarifs.
- Passez le vecteur de test dans votre code : il doit produire la signature attendue, accepter l'en-tête à deux signatures et rejeter le corps mis en forme.
- Dans le tableau de bord, ouvrez Webhooks, ajoutez l'URL
https:publique de votre endpoint et copiez le secretwhsec_dans l'environnement de votre serveur. - Choisissez Send test sur la page de l'endpoint. Le journal de livraison affiche votre
204et l'événement arrive dans vos journaux. - Modifiez un caractère du secret sur votre serveur et envoyez un autre test. Le journal affiche
400avecno_matching_signature. - Envoyez le formulaire pour de vrai et vérifiez que la livraison
submission.createdréussit.
Dépannage
| Problème | Que faire |
|---|---|
400 no_matching_signature | Mauvais secret, corps analysé, ou clé non décodée en base64. Passez le vecteur de test par le même chemin de code pour savoir lequel. |
400 timestamp_too_old ou timestamp_too_new | L'horloge du serveur est décalée, ou vous rejouez à la main une requête enregistrée. Synchronisez l'horloge, et utilisez Retry dans le journal de livraison, qui signe à nouveau. |
400 missing_headers | Un proxy ou un framework a supprimé les en-têtes webhook-*, ou la requête ne vient pas d'Ikiform. Journalisez chaque nom d'en-tête que voit votre gestionnaire. |
| Fonctionne pour les événements de test, échoue pour les vraies soumissions | Les vrais payloads contiennent des caractères non ASCII et des textes plus longs. Votre code décode ou réencode le corps avant de le vérifier. Vérifiez les octets exacts. |
| Chaque livraison échoue juste après la rotation du secret | Le gestionnaire ne vérifie que la première signature, ou les 24 heures se sont écoulées avec l'ancien secret déployé. Vérifiez chaque entrée v1 et déployez le nouveau secret. |
| Le journal de livraison affiche un délai dépassé, mais votre gestionnaire s'est exécuté | Ikiform attend 15 secondes. Répondez d'abord et faites le travail lent ensuite, et ignorez les valeurs webhook-id répétées. |
Questions
Recalculez le HMAC-SHA256 de l'identifiant du webhook, du timestamp et du corps brut joints par des points, avec comme clé la partie décodée en base64 de votre secret whsec_. Encodez le résultat en base64 et comparez-le en temps constant avec chaque valeur v1 de l'en-tête webhook-signature.
Presque toujours pour l'une de ces trois raisons : le corps a été analysé et resérialisé avant la vérification, toute la chaîne whsec_ a servi de clé au lieu de ses octets décodés en base64, ou le secret appartient à un autre endpoint.
Une spécification ouverte pour signer les webhooks avec HMAC-SHA256 et les en-têtes webhook-id, webhook-timestamp et webhook-signature. Ikiform la suit, donc n'importe quelle bibliothèque Standard Webhooks peut vérifier les livraisons d'Ikiform.
Sinon, une requête capturée resterait valide pour toujours. Rejeter tout ce qui est à plus de 5 minutes de votre horloge empêche les rejeux d'anciennes requêtes, et Ikiform signe chaque nouvelle tentative avec un timestamp récent, donc les vraies nouvelles tentatives passent toujours.
Pendant 24 heures, chaque livraison est signée avec l'ancien et le nouveau secret, donc vous pouvez déployer le nouveau sans perdre d'événements. Ensuite, seul le nouveau secret fonctionne.
Les deux conviennent pour Ikiform ; tout statut autre que 2xx compte comme un échec et donne lieu à une nouvelle tentative. Ne renvoyez pas 410, qui désactive immédiatement l'endpoint.
Une étape vous bloque, ou vous voulez un guide pour votre cas ? Écrivez à hello@ikiform.com.