Demandes
Créer une demande
Deux appels API : demandez au formulaire ce qu'on peut lui dire, puis assignez-le à une personne avec les valeurs que vous connaissez déjà.
Commencer dans le panneau Partager

Ouvrez votre formulaire publié, cliquez sur Partager, et choisissez l’onglet Demandes. La carte qui s’y trouve vous donne tout ce dont vous avez besoin pour faire le premier appel :
L’id du formulaire, avec un bouton de copie.
Un extrait curl et une invite MCP, tous deux construits à partir des vraies clés de champ de votre formulaire — l’exemple s’adresse donc déjà aux champs que ce formulaire possède réellement.
Un onglet Manuel qui crée une demande à la main, et Essayez par vous-même, qui transforme ce que vous y avez saisi en une demande en mode test et vous remet son lien.
Un lien vers la page Demandes, filtrée sur ce formulaire.
Publiez d'abord
Un formulaire non publié ne peut pas recevoir de demande, et les extraits restent désactivés jusqu’à ce que vous publiiez. Les clés de
champ sont figées à la première publication — c’est ce qui permet à votre automatisation de continuer à s’adresser à
company_name un an plus tard. Voir Clés de champ.
Étape 1 — Découvrir les champs
fields.list renvoie chaque champ de la version actuellement publiée du formulaire, avec la clé pour l’adresser, la forme de
valeur qu’il attend, et le compartiment auquel il appartient.
curl -X POST https://api.formbase.so/api/v1 \
-H "Authorization: Bearer $FORMBASE_TOKEN" \
-H "Content-Type: application/json" \
-d '{"method":"fields.list","params":{"formId":"j57..."}}'{
"ok": true,
"data": {
"published": true,
"items": [
{ "key": "company_name", "type": "text", "title": "Company", "required": true, "prefillable": true },
{
"key": "company_size", "type": "select", "title": "Company size", "required": false, "prefillable": true,
"options": [
{ "key": "1_50", "label": "1–50" },
{ "key": "51_200", "label": "51–200" }
]
},
{ "key": "case_id", "type": "hidden", "title": "Case", "required": false, "prefillable": false, "context": true },
{ "key": "total", "type": "number", "title": "Total", "required": false, "prefillable": false, "calculated": true },
{ "key": "signature", "type": "signature", "title": "Sign here", "required": true, "prefillable": false }
],
"hasMore": false
}
}context: truemarque un champ caché. Sa valeur va danscontext, jamais dansprefill; la clé d’un champ caché dansprefillest rejetée avecUNKNOWN_FIELD_KEY.calculated: truemarque un champ calculé. Le formulaire détermine sa valeur lui-même, donc rien ne peut en envoyer une ; vous la relisez sous sa clé dansanswers.prefillable: falsemarque un champ pour lequel personne ne peut fournir de valeur : téléversement de fichier, signature, paiement, prise de rendez-vous, et blocs Documents. Le destinataire remplit ces questions. Les champs cachés et les champs calculés affichent eux aussiprefillable: false: les champs cachés reçoivent leur valeur viacontext, les champs calculés n’en reçoivent aucune.optionsliste les choix d’une question à choix. Envoyez la clé de l’option, pas son libellé ; le libellé est là pour vous permettre de faire correspondre le choix que vous connaissez à sa clé. Une question matricielle liste sesrowsetcolumnsde la même façon.Les groupes répétables reviennent comme une seule entrée avec
type: “group”,repeating: trueet une liste demembers.
Étape 2 — Créer la demande
curl -X POST https://api.formbase.so/api/v1 \
-H "Authorization: Bearer $FORMBASE_TOKEN" \
-H "Content-Type: application/json" \
-d '{"method":"requests.create","params":{
"formId":"j57...",
"recipient":{"email":"ada@acme.com","name":"Ada"},
"context":{"case_id":"CASE-9"},
"prefill":{"company_name":"Acme","company_size":"51_200"},
"readonly":["company_name"],
"delivery":"email",
"externalId":"run-42",
"callbackUrl":"https://automation.example/webhook/resume-abc",
"idempotencyKey":"run-42"}}'{
"ok": true,
"data": {
"id": "kd7...",
"status": "pending",
"url": "https://form.formbase.so/r/rq_...",
"deliveryStatus": "queued",
"expiresAt": 1794787200000,
"createdAt": 1789379200000,
"externalId": "run-42",
"deduplicated": false
}
}deliveryStatus vaut “queued” quand formbase envoie l’invitation par e-mail, et “not_requested”
quand vous livrez le lien vous-même.
Préremplissage, champs verrouillés, et contexte
Trois choses différentes peuvent être attachées à une demande, et les confondre est l’erreur la plus fréquente au départ.
| Va dans | Le destinataire… | Revient dans le callback | |
|---|---|---|---|
| Préremplissage | prefill | Le voit et peut le modifier | Oui, comme une réponse |
| Champ verrouillé | prefill + readonly | Le voit, ne peut pas le modifier | Oui, comme une réponse |
| Contexte | context | Ne peut pas le modifier ; ne le voit que là où vous le mentionnez | Oui, dans le bloc request et comme une réponse |
| Métadonnées | metadata | Ne le voit jamais, et le formulaire non plus | Oui, dans le bloc request |
Préremplissage
Des réponses initiales pour les questions visibles, pour que le destinataire relise et corrige plutôt que de tout retaper. Tout ce que vous savez déjà sur lui a sa place ici — le nom de l’entreprise depuis votre CRM, le montant de la facture, les réponses de l’an dernier.
Champs verrouillés
Listez une clé préremplie dans readonly et le destinataire voit la valeur mais ne peut pas la modifier. Utilisez ceci pour
les faits qu’il confirme plutôt qu’il ne fournit — le numéro de contrat, le prix convenu. Le verrouillage est propre à chaque demande : le
formulaire lui-même n’est pas touché, et le même champ reste librement modifiable sur la prochaine demande.
Chaque clé verrouillée doit aussi être préremplie, et un champ verrouillé obligatoire doit être prérempli avec une valeur non vide — sinon le destinataire se retrouverait face à un formulaire qu’il ne pourrait jamais soumettre, et formbase refuse l’appel plutôt que de créer ce piège.
Contexte
Des valeurs de confiance pour les champs cachés du formulaire — un numéro de dossier, un id d’exécution de workflow, un montant. Le contexte alimente les variables, la logique conditionnelle, les champs calculés, et le texte des e-mails, revient inchangé dans le callback, et le destinataire ne peut pas le modifier. C’est la différence avec le fait d’initialiser un champ caché via une URL sur un lien public, où n’importe qui peut modifier la chaîne de requête ; les liens de demande ignorent entièrement les paramètres de requête de l’URL. Les valeurs de contexte doivent être une chaîne, un nombre ou un booléen.
Le contexte n’est pas de forme libre : chaque clé doit être un champ caché sur la version publiée du formulaire, et toute autre clé est
rejetée avec UNKNOWN_FIELD_KEY. Un suivi qui n’a pas de champ caché correspondant, comme un id d’exécution, a sa place dans
metadata.
Les champs cachés ne sont pas affichés sur le formulaire, mais une valeur de contexte n’est pas secrète pour le destinataire. Il la voit
partout où le formulaire ou l’invitation la montre : une mention dans le contenu du
formulaire ou le texte de l’e-mail, ou une question visible qui utilise ce champ caché comme
valeur par défaut. Dans ce dernier cas, le destinataire voit la valeur
de contexte préremplie dans cette question et peut modifier la réponse. La valeur de contexte elle-même reste inchangée. Un
prefill pour la clé propre de cette question l’emporte sur la valeur par défaut.
Métadonnées
Votre propre suivi interne — un id d’exécution, un id d’enregistrement CRM. Cela n’atteint jamais le formulaire, donc ça ne peut pas être intégré dans du texte ni lu par la logique ; ça voyage simplement et revient dans chaque callback et chaque lecture de statut.
Formes de valeur
Envoyez les valeurs dans la forme demandée par type issu de fields.list. Une forme incorrecte revient comme une
erreur de validation nommant la clé, le type attendu, et — pour les questions à choix — les valeurs qui auraient été acceptées.
| Type | Envoyez |
|---|---|
| text, email, phone, url, textarea | Une chaîne de caractères |
| number, rating, scale | Un nombre |
| switch | true ou false |
| date | "2026-03-04" |
| time | "09:30" ou "09:30:00" |
| radio, select | La clé de l'option, pas son libellé |
| checkbox, ranking, picture-choice | Un tableau de clés d'option |
| matrix | Un objet clé de ligne vers clé de colonne : { "row_key": "column_key" } |
| group (repeating) | Un tableau contenant chaque instance, au maximum 100 : [{ "member_key": value }, …] |
| file, signature, payment, schedule-appointment | Rien — le destinataire les fournit lui-même |
| tout champ avec calculated: true | Rien — le formulaire calcule sa valeur |
| documents | Rien dans le préremplissage — utilisez l'option documents ci-dessous |
Documents
Un bloc Documents remet des fichiers au répondant. Ses fichiers créés sont les mêmes pour tout le monde et restent toujours ; une demande ajoute des fichiers pour son seul destinataire en dessous — le propre contrat de bail du client, une copie de pièce d’identité à vérifier. Les octets ne transitent jamais par l’appel API lui-même : téléversez d’abord, puis référencez.
- 1
Réservez le téléversement
Appelez documents.create avec formId, name (1 à 200 caractères), contentType (PDF ou image), la taille exacte en octets, et éventuellement un sha256 du fichier (64 caractères hexadécimaux). Vous récupérez un id et une uploadUrl valide pendant une heure.
- 2
Téléversez les octets
Effectuez un PUT du fichier vers uploadUrl avec le même Content-Type. Rien n'est encore vérifié.
- 3
Référencez-le sur la demande
Passez documents: [{ documentId, name? }] dans requests.create. formbase vérifie l'objet téléversé (taille, signature de fichier, sha256 si vous en avez envoyé un) avant que la demande ne soit créée, et le destinataire voit le fichier dans le bloc.
"documents": [
{ "documentId": "kn7...", "name": "Your lease contract" },
{ "documentId": "kn8..." }
]name remplace le nom d’affichage enregistré avec l’envoi. Si le formulaire compte plusieurs blocs Documents, désignez la
cible avec field, la clé de champ du bloc (fields.list la liste, avec les documents de l’auteur que chaque
répondant reçoit déjà). Les fichiers apparaissent sous ces documents de l’auteur : une demande ajoute des fichiers, elle n’en remplace
jamais. Un envoi peut être référencé par autant de demandes que vous voulez — une liste de prix envoyée une fois sert cinq cents demandes.
Limites : PDF et images uniquement, 25 Mo par document, 100 Mo par demande (DOCUMENTS_TOO_LARGE), et au maximum 20 documents
par bloc en comptant ceux de l’auteur (DOCUMENTS_TOO_MANY). Les fichiers comptent dans le quota de stockage de votre espace
de travail et sont libérés dès que les demandes qui les référencent sortent de la période de conservation du formulaire. La soumission
enregistre la liste que le destinataire a vue sous la clé de champ du bloc, si bien que le callback vous indique exactement quels fichiers
ont été donnés à cette personne.
Le reste des options
| Option | Ce que ça fait |
|---|---|
| language | La langue dans laquelle le formulaire s'ouvre et dans laquelle l'invitation est rédigée ; une des langues publiées du formulaire. Omis, la langue par défaut du formulaire est utilisée. Le destinataire peut quand même changer de langue, comme sur un lien public. |
| delivery | "email" envoie l'invitation pour vous et nécessite un e-mail de destinataire ainsi qu'un plan Pro ou Business, ou l'une des 10 invitations gratuites d'un compte Free ; "none" (par défaut) signifie que vous livrez le lien vous-même. |
| reminders | Remplace le planning de rappel du formulaire pour cette seule demande avec jusqu’à cinq délais d’inactivité tels que ["2d", "12h", "30m"], ou passez une liste vide pour désactiver les rappels. Un planning personnalisé nécessite un e-mail de destinataire et un plan Pro ou Business ; sans e-mail de destinataire, le planning propre du formulaire ne s’exécute tout simplement pas. |
| expiresAt | Le moment où le lien cesse de fonctionner, sous forme d'horodatage Unix en millisecondes. Par défaut 30 jours, 365 jours au maximum. |
| externalId | Votre propre id pour cette demande. Vous pourrez filtrer dessus plus tard. |
| idempotencyKey | Fait qu'une exécution relancée réutilise la demande au lieu d'en créer une seconde. |
| callbackUrl | Où formbase envoie le callback en POST quand la demande se termine. HTTPS uniquement. |
| domainId | Génère le lien sur l'un de vos domaines personnalisés, à la place de celui sur lequel le formulaire est déjà publié. |
| test | Un essai à blanc : rien n'est envoyé par e-mail, le callback indique test, et la soumission ne compte nulle part. Voir ci-dessous. |
Mode test
Passez test: true pour éprouver tout le circuit avant une exécution réelle. Une demande test est réelle sur tout ce qui
compte pour le câblage : le lien s’ouvre et peut être complété, le callback se déclenche comme
d’habitude, et requests.get renvoie les réponses. Ce qu’elle ne fait jamais, c’est atteindre quelqu’un ou quelque chose que
vous devriez ensuite nettoyer :
Aucune invitation ni aucun rappel n’est envoyé, quoi que dise
delivery. Envoyer un rappel est refusé dessus, et elle ne consomme rien de votre allocation mensuelle.Le callback porte
“test”: true, pour que votre flux de travail puisse bifurquer ou ignorer l’événement.La soumission est stockée mais ne compte pas : ni sur votre quota mensuel (un test se complète même quand le quota est épuisé), et elle n’apparaît jamais dans les compteurs de soumissions du formulaire, l’onglet soumissions, les exports, ou vos intégrations. Personne n’est notifié.
La demande est masquée de la page Demandes derrière Afficher les demandes de test, exclue de l’entonnoir de demandes dans Analytiques, et exclue de
requests.listsauf si vous passezincludeTest: true.Le lien se ferme dans les 24 heures, même si
expiresAtdemande plus ; leexpiresAtde la réponse indique quand. Sur Free, un workspace peut créer 10 demandes de test par jour. La suivante échoue avecRATE_LIMITEDet la raisonTEST_REQUEST_LIMIT_REACHED, etretryAfterMsindique quand vous pouvez réessayer. Pro et Business n’ont pas de plafond quotidien.
Essayez par vous-même sur le panneau Partager est ce mode en un seul clic : il reprend le brouillon de l’onglet Manuel — préremplissage, verrous, contexte, callback, expiration — adresse la demande à votre propre compte, n’envoie aucun e-mail, et vous remet le lien à ouvrir vous-même.
Ce que coûte une demande
Chaque plan dispose d’une seule allocation mensuelle partagée par les deux canaux : une soumission par lien de partage
consomme une unité, et chaque demande que vous créez aussi — que le destinataire y réponde, l’ignore, ou que vous l’annuliez. La
soumission qu’une demande recueille est déjà payée et ne compte nulle part. Free inclut 1 000 unités par mois, Pro et Business 50 000 ; le
compteur se réinitialise le 1er de chaque mois, UTC. Une fois la limite atteinte, requests.create échoue avec
UPGRADE_REQUIRED et la raison MONTHLY_ALLOWANCE_REACHED ; les demandes déjà créées restent répondables.
Sur Free, une demande créée avec “delivery”: “email” consomme aussi l’une des
10 invitations gratuites du compte. Elles ne se réinitialisent
jamais ; une fois épuisées, la livraison par e-mail échoue avec UPGRADE_REQUIRED et la raison
FREE_INVITATIONS_USED.
Idempotence
Passez la même idempotencyKey avec le même corps et vous récupérez la demande d’origine, avec deduplicated: true
et le lien d’origine — pas de seconde demande, pas de second e-mail. Réutilisez la clé avec un corps différent et formbase refuse
avec IDEMPOTENCY_CONFLICT plutôt que de deviner ce que vous vouliez dire. Les clés sont propres à l’espace de travail et
valables 30 jours ; passé ce délai, la même clé démarre une nouvelle demande.
Dans un outil de workflow, l’id d’exécution est la clé naturelle : une exécution relancée après un incident réseau retrouve la demande qu’elle avait déjà créée.
Limite de débit
requests.create et documents.create partagent un budget de 60 appels par minute, compté par
jeton API (ou par utilisateur, pour un appel effectué sans jeton). Un arriéré que vous videz devrait s’autoréguler ; une rafale qui
dépasse le budget est refusée et peut être retentée.
Domaines personnalisés
Si le formulaire est déjà publié sur l’un de vos domaines personnalisés, les liens de
demande y sont générés automatiquement — https://forms.votreentreprise.com/r/rq_…. Nommez explicitement domainId
quand le formulaire est publié sur plus d’un domaine. Le domaine doit appartenir au même workspace que le formulaire.
Ce que voit le destinataire
Exactement le formulaire que vous avez conçu — même thème, même logo, même langue — avec ses valeurs en place, les champs verrouillés en lecture seule, et aucun captcha à résoudre. Quand il soumet, il obtient votre page de remerciement. S’il revient sur le lien après coup, il obtient la page de résultat au lieu d’un formulaire vide.
Il n’y a aucun message de votre automatisation sur la page. Tout ce que le destinataire doit savoir a sa place dans le formulaire lui-même, où vous pouvez le personnaliser en mentionnant une valeur de contexte ou un champ prérempli.
Un agent IA exécute les deux mêmes étapes que fields_list et request_create, avec les mêmes options — documents
et domainId compris. Voir Demandes sur le serveur MCP.