formbasedocs
Aller à l'applicationAppli

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

L'onglet Demandes sur son onglet curl, montrant l'id du formulaire et un appel requests.create déjà prêt
L'onglet curl : l'id du formulaire et un appel déjà rempli avec les clés de champ de ce formulaire.

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.

É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.

fields.list
bash
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..."}}'
Response
json
{
  "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: true marque un champ caché. Sa valeur va dans context, jamais dans prefill ; la clé d’un champ caché dans prefill est rejetée avec UNKNOWN_FIELD_KEY.

  • calculated: true marque 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é dans answers.

  • prefillable: false marque 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 aussi prefillable: false : les champs cachés reçoivent leur valeur via context, les champs calculés n’en reçoivent aucune.

  • options liste 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 ses rows et columns de la même façon.

  • Les groupes répétables reviennent comme une seule entrée avec type: “group”, repeating: true et une liste de members.

Étape 2 — Créer la demande

requests.create
bash
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"}}'
Response
json
{
  "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 dansLe destinataire…Revient dans le callback
PréremplissageprefillLe voit et peut le modifierOui, comme une réponse
Champ verrouilléprefill + readonlyLe voit, ne peut pas le modifierOui, comme une réponse
ContextecontextNe peut pas le modifier ; ne le voit que là où vous le mentionnezOui, dans le bloc request et comme une réponse
MétadonnéesmetadataNe le voit jamais, et le formulaire non plusOui, 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.

TypeEnvoyez
text, email, phone, url, textareaUne chaîne de caractères
number, rating, scaleUn nombre
switchtrue ou false
date"2026-03-04"
time"09:30" ou "09:30:00"
radio, selectLa clé de l'option, pas son libellé
checkbox, ranking, picture-choiceUn tableau de clés d'option
matrixUn 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-appointmentRien — le destinataire les fournit lui-même
tout champ avec calculated: trueRien — le formulaire calcule sa valeur
documentsRien 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. 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. 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. 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.

requests.create → documents
json
"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

OptionCe que ça fait
languageLa 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.
remindersRemplace 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.
expiresAtLe moment où le lien cesse de fonctionner, sous forme d'horodatage Unix en millisecondes. Par défaut 30 jours, 365 jours au maximum.
externalIdVotre propre id pour cette demande. Vous pourrez filtrer dessus plus tard.
idempotencyKeyFait qu'une exécution relancée réutilise la demande au lieu d'en créer une seconde.
callbackUrlOù formbase envoie le callback en POST quand la demande se termine. HTTPS uniquement.
domainIdGénère le lien sur l'un de vos domaines personnalisés, à la place de celui sur lequel le formulaire est déjà publié.
testUn 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.list sauf si vous passez includeTest: true.

  • Le lien se ferme dans les 24 heures, même si expiresAt demande plus ; le expiresAt de la réponse indique quand. Sur Free, un workspace peut créer 10 demandes de test par jour. La suivante échoue avec RATE_LIMITED et la raison TEST_REQUEST_LIMIT_REACHED, et retryAfterMs indique 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.