Passer au contenu

Essayez un sujet, un nom de fonction ou un code d’erreur comme FORM_CLOSED.

English formerie.com

Vérifier les réponses avec votre serveur

Demandez à votre propre système si une réponse est bonne avant que Formerie l’accepte : un vrai numéro de commande, du stock restant, une plage libre, un compte connu. Les règles et les formules de votre formulaire s’occupent de tout ce qu’elles peuvent voir. Ceci sert à ce que seul votre système sait.

La vérification se fait une seule fois, à l’envoi, après les contrôles anti-pourriel et les règles de votre formulaire. Votre serveur n’entend donc parler que des réponses qui seraient autrement acceptées. Vérifier une réponse pendant la saisie ne l’appelle pas, pas plus que passer d’une étape à l’autre.

La configurer

  1. Ouvrez votre formulaire, puis Livraison, et trouvez Vérifier les réponses avec votre serveur.
  2. Choisissez Configurer une vérification et collez votre adresse. Elle doit être publique et commencer par https://.
  3. Copiez le secret de signature. Il n’est affiché que cette fois-ci.
  4. Faites répondre votre serveur à la confirmation ci-dessous, puis choisissez Confirmer l’adresse.
  5. Choisissez Envoyer un test pour voir ce que répond votre serveur et ce qu’un visiteur verrait.
  6. Choisissez Activer.

Définir une adresse demande une connexion récente et envoie un courriel aux propriétaires et aux administrateurs de l’espace de travail. Chaque formulaire peut avoir une vérification.

Ce que reçoit votre serveur

HTTP
POST /formerie/check HTTP/1.1
content-type: application/json
user-agent: Formerie-Webhooks/1
webhook-id: evt_3f9a1c07d2b84e6fa05c7d19be42
webhook-timestamp: 1793577600
webhook-signature: v1,K5oZfzN95Z9UVu1EsfQmfVNQhnkZ2pj9o9NDN/H/pI4=
formerie-event: submission.validate
formerie-delivery: wvc_0196f2a7c41e7b
JSON
{
  "version": 1,
  "id": "evt_3f9a1c07d2b84e6fa05c7d19be42",
  "type": "submission.validate",
  "timestamp": "2026-11-02T18:40:00.000Z",
  "test": false,
  "data": {
    "workspace": { "id": "ws_2kq8m4tz7hn3xd" },
    "form": { "id": "fm_t77txbvg43nmar", "name": "Commande", "version": 3 },
    "submission": {
      "language": "fr-CA",
      "origin": "https://acme.example",
      "pageUrl": "https://acme.example/commande"
    },
    "fields": { "order_number": "A-1009" },
    "answers": [
      { "path": "order_number", "label": "Numéro de commande", "text": "A-1009", "value": "A-1009" }
    ]
  }
}

C’est la même forme qu’un webhook, sans identifiant de réponse, puisque rien n’est encore conservé.

  • Les mots de passe, les jetons de vérification, les adresses IP et les détails anti-pourriel ne sont jamais envoyés.
  • language est la langue du visiteur, pour que vous puissiez lui répondre dans cette langue.
  • test vaut true pour un test. Voir Tests.
  • origin et pageUrl peuvent être désactivés. Vous pouvez aussi envoyer seulement les réponses utiles à votre vérification : vérifier une commande ne demande pas tout le message.

Vérifier la signature

Elle est signée comme un webhook. Utilisez donc le code de Vérifier la signature. Faites-le d’abord, sur le contenu brut.

Quoi répondre

Répondez 200 avec du JSON, l’une ou l’autre de ces formes :

JSON
{ "ok": true }
JSON
{
  "ok": false,
  "errors": [
    { "path": "order_number", "code": "ORDER_NOT_FOUND", "message": "We can't find this order." }
  ],
  "message": "Please check your answers."
}
  • path nomme un champ du formulaire, et le message s’affiche à côté. Tout le reste s’affiche en haut du formulaire.
  • code vous appartient, en majuscules, comme ORDER_NOT_FOUND. Un code qui ressemble à ceux de Formerie devient WEBHOOK_REJECTED.
  • message est du texte simple dans la langue du visiteur, jusqu’à 500 caractères. Les caractères de contrôle et les chevrons sont retirés, et rien ne permet d’envoyer un lien ou une mise en forme. Sans message, le visiteur voit le texte habituel du formulaire.
  • Au plus 50 erreurs sont gardées, et votre réponse peut aller jusqu’à 64 Ko.

Votre réponse peut seulement dire oui ou non. Elle ne peut pas changer une valeur, ajouter un champ ni choisir qui reçoit un courriel.

Le visiteur reçoit un VALIDATION_FAILED normal, comme pour une règle de votre formulaire, et rien n’est conservé.

Seul "ok": false avec un 200 refuse. Un 400 ou une page d’erreur compte comme un serveur qui ne répond pas. Une page brisée ne peut donc pas bloquer vos visiteurs.

Confirmer l’adresse

Pour confirmer l’adresse, Formerie envoie un { "type": "webhook.verify", "challenge": "..." } signé. Répondez 200 avec { "challenge": "la même valeur" }. Changer l’adresse désactive la vérification jusqu’à ce que la nouvelle soit confirmée. Ça montre que l’adresse est prévue pour ça, et personne ne peut diriger Formerie vers le serveur de quelqu’un d’autre.

Exemple

Vérifiez d’abord la signature, puis construisez votre réponse ainsi. Chacun de ces exemples est exécuté avec de vraies requêtes.

Node

JavaScript
const KNOWN_ORDERS = new Set(["A-1009"]);

export function answerFormerie(rawBody) {
  const event = JSON.parse(rawBody);
  if (event.type === "webhook.verify") return { challenge: event.challenge };

  if (KNOWN_ORDERS.has(event.data.fields.order_number)) return { ok: true };

  const french = event.data.submission.language.startsWith("fr");
  return {
    ok: false,
    errors: [{
      path: "order_number",
      code: "ORDER_NOT_FOUND",
      message: french ? "Nous ne trouvons pas cette commande." : "We can't find this order.",
    }],
  };
}

Python

Python
import json

KNOWN_ORDERS = {"A-1009"}


def answer_formerie(raw_body):
    event = json.loads(raw_body)
    if event["type"] == "webhook.verify":
        return {"challenge": event["challenge"]}

    if event["data"]["fields"].get("order_number") in KNOWN_ORDERS:
        return {"ok": True}

    french = event["data"]["submission"]["language"].startswith("fr")
    return {
        "ok": False,
        "errors": [{
            "path": "order_number",
            "code": "ORDER_NOT_FOUND",
            "message": "Nous ne trouvons pas cette commande." if french else "We can't find this order.",
        }],
    }

PHP

PHP
function answerFormerie(string $rawBody): array
{
    $event = json_decode($rawBody, true, 32, JSON_THROW_ON_ERROR);
    if ($event['type'] === 'webhook.verify') {
        return ['challenge' => $event['challenge']];
    }

    if (in_array($event['data']['fields']['order_number'] ?? null, ['A-1009'], true)) {
        return ['ok' => true];
    }

    $french = str_starts_with($event['data']['submission']['language'], 'fr');
    return [
        'ok' => false,
        'errors' => [[
            'path' => 'order_number',
            'code' => 'ORDER_NOT_FOUND',
            'message' => $french ? 'Nous ne trouvons pas cette commande.' : "We can't find this order.",
        ]],
    ];
}

Go

Go
package formerie

import (
	"encoding/json"
	"strings"
)

type event struct {
	Type      string `json:"type"`
	Challenge string `json:"challenge"`
	Data      struct {
		Fields     map[string]any `json:"fields"`
		Submission struct {
			Language string `json:"language"`
		} `json:"submission"`
	} `json:"data"`
}

var knownOrders = map[string]bool{"A-1009": true}

func AnswerFormerie(rawBody []byte) (map[string]any, error) {
	var e event
	if err := json.Unmarshal(rawBody, &e); err != nil {
		return nil, err
	}
	if e.Type == "webhook.verify" {
		return map[string]any{"challenge": e.Challenge}, nil
	}

	if order, _ := e.Data.Fields["order_number"].(string); knownOrders[order] {
		return map[string]any{"ok": true}, nil
	}

	message := "We can't find this order."
	if strings.HasPrefix(e.Data.Submission.Language, "fr") {
		message = "Nous ne trouvons pas cette commande."
	}
	return map[string]any{
		"ok": false,
		"errors": []map[string]string{{
			"path":    "order_number",
			"code":    "ORDER_NOT_FOUND",
			"message": message,
		}},
	}, nil
}

Ruby

Ruby
require "json"

KNOWN_ORDERS = ["A-1009"].freeze

def answer_formerie(raw_body)
  event = JSON.parse(raw_body)
  return { challenge: event["challenge"] } if event["type"] == "webhook.verify"

  return { ok: true } if KNOWN_ORDERS.include?(event.dig("data", "fields", "order_number"))

  french = event.dig("data", "submission", "language").start_with?("fr")
  {
    ok: false,
    errors: [{
      path: "order_number",
      code: "ORDER_NOT_FOUND",
      message: french ? "Nous ne trouvons pas cette commande." : "We can't find this order.",
    }],
  }
end

Restez rapide et en lecture seule

Les visiteurs attendent votre réponse, jusqu’à 3 secondes par défaut. Vous pouvez choisir d’une demi-seconde à 5 secondes. Ce temps couvre tout, y compris la lecture de votre réponse. Formerie appelle une seule fois et ne réessaie jamais pendant qu’une personne attend.

Une vérification devrait seulement lire. Si elle réserve quelque chose, utilisez webhook-id pour reconnaître une répétition : un visiteur qui renvoie le même envoi a le même. Ne tenez pas pour acquis que la réponse sera acceptée ensuite, puisque le formulaire peut être plein. Pour agir sur une réponse acceptée, utilisez un webhook.

Si votre serveur ne répond pas

Prendre trop de temps, un statut d’erreur, une redirection ou tout ce qui n’est pas le JSON ci-dessus comptent comme une absence de réponse. Les redirections ne sont jamais suivies. Vous choisissez ce qui arrive :

  • Accepter la réponse quand même, par défaut. Elle est envoyée comme d’habitude, et l’entrée est marquée Non vérifiée. Le meilleur choix pour la plupart des formulaires.
  • La garder et attendre mon envoi. Le visiteur voit une confirmation normale. Rien ne part, ni courriel ni webhook, avant que vous l’envoyiez depuis En attente d’envoi. L’envoyer ne la vérifie pas de nouveau. Elle garde sa place si le formulaire a une limite.
  • Demander au visiteur de réessayer. Rien n’est conservé, et le visiteur voit VALIDATION_UNAVAILABLE, avec ses réponses toujours dans le formulaire. À utiliser seulement quand accepter sans vérifier serait pire, comme pour un stock limité ou des codes à usage unique, puisqu’une réponse est perdue s’il ne revient pas.

Après 10 échecs de suite sur au moins une minute, Formerie arrête d’interroger votre serveur pendant 5 minutes, pour que les visiteurs n’attendent pas chacun un serveur en panne. Ensuite, il essaie un appel à la fois, en attendant plus longtemps chaque fois, jusqu’à une heure. Les propriétaires et les administrateurs reçoivent un courriel, au plus une fois par jour.

Si votre serveur n’a pas répondu pendant 7 jours et que vous avez choisi d’accepter, la vérification se désactive et vous recevez un courriel. Si vous avez choisi de garder ou de refuser, elle reste activée.

Tests

Un test peut joindre votre vrai serveur. Il dit donc toujours quand il l’a fait :

  • Envoyer un test, à l’écran, envoie des réponses inventées.
  • Un lien d’essai, une clé de test ou l’application envoient ce qui a été saisi.
  • Un test depuis localhost ou un site de test n’appelle pas votre serveur, puisque n’importe qui peut prétendre en être un. Il dit qu’il l’aurait fait.

Chaque appel porte "test": true, signé de la même façon. Si votre vérification change quelque chose, sautez cette étape quand test vaut true. Un test est conservé comme test quoi que fasse votre serveur, sauf s’il refuse, ce qui affiche le refus.

La réponse à un test contient test.validationWebhook, qui dit si votre serveur a été appelé et ce qu’il a répondu.

Voir comment ça va

L’écran montre les dernières 24 heures : combien de demandes, de réponses correctes, refusées et non vérifiées. Il liste aussi les problèmes récents, avec l’heure, la raison et le code de statut. Formerie garde les décomptes 30 jours et les problèmes 14 jours. Il ne garde jamais la réponse de votre serveur ni ce qui a été envoyé.

Si votre serveur refuse plus de la moitié d’au moins 20 réponses en une heure, les propriétaires et les administrateurs reçoivent un courriel, au plus une fois par jour. C’est peut-être voulu, ou c’est peut-être un bogue qui vous coûte des réponses.

Changer votre secret

Changer le secret vous en donne un nouveau, et les deux signent pendant 24 heures, pour que vous puissiez mettre votre serveur à jour sans interruption. S’il a fui, choisissez de mettre fin à l’ancien tout de suite.

Votre avis de confidentialité

Votre formulaire envoie ces réponses à votre serveur avant de les accepter, chiffrées en chemin. Vous choisissez le serveur, et ce qu’il contient est à vous de le protéger. Votre avis de confidentialité devrait le mentionner.

À partir d’un assistant IA (bientôt)

Quand MCP sera ouvert à tous, un assistant connecté par ce moyen pourra lire les réglages d’une vérification et ses problèmes récents, et envoyer un test avec des réponses inventées. Définir l’adresse, l’activer ou la désactiver, choisir ce qui arrive en cas de panne et changer le secret se font dans l’application, puisqu’une adresse est l’endroit où vont les réponses.

Forfaits

Offert sur tous les forfaits pour l’instant.