Formstepdocs
Aller à l'applicationAppli

Demandes

Créer un formulaire et une demande en un seul appel

Un agent au téléphone avec une cliente apprend qu’elle doit encore envoyer deux fiches de paie, payer des frais de 40 EUR et réserver un appel. Il envoie le formulaire et la demande dans le même appel, et Formstep s’occupe du reste.


Quand l’utiliser

Une demande pointe d’ordinaire vers un formulaire que quelqu’un a créé dans l’éditeur et publié. Quand les questions dépendent du dossier, l’appelant les envoie avec la demande : requests.create accepte form à la place de formId. Formstep crée un formulaire ordinaire à partir des blocs, le publie et crée la demande en une seule étape : soit les deux existent, soit aucun. Nous l’appelons un formulaire inline.

Continuez d’utiliser formId pour un formulaire que vous envoyez à beaucoup de personnes sous la même forme et que vous voulez concevoir à la main dans l’éditeur. Utilisez form quand une automatisation décide de ce qu’il faut demander.

L’appel

requests.create avec un formulaire
bash
curl -X POST https://api.formstep.io/api/v1 \
  -H "Authorization: Bearer $FORMSTEP_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"method":"requests.create","params":{
        "form":{
          "name":"Documents and fee for your mortgage application",
          "logo":{"type":"image","url":"https://acme.example/logo.png"},
          "cover":{"type":"color","color":"#0f4c81"},
          "emoji":"🏦",
          "blocks":[
            {"type":"paragraph","content":"Upload the two documents and pay the processing fee."},
            {"type":"file","key":"payslips","title":"Your last two payslips","maxFiles":2},
            {"type":"payment","key":"fee","title":"Processing fee","amount":40,"currency":"EUR"},
            {"type":"scheduleAppointment","key":"call","title":"Pick a time for our call","eventType":"15 min call"}]},
        "recipient":{"email":"ada@example.com","name":"Ada"},
        "delivery":"email",
        "idempotencyKey":"call-8841"}}'

Tout le reste de la demande garde son sens : recipient, prefill, context, readonly, delivery, les rappels, l’expiration, callbackUrl, test et idempotencyKey sont vérifiés par rapport au nouveau formulaire exactement comme pour un formulaire créé à la main. La réponse ajoute le formulaire, avec les clés sous lesquelles ses réponses reviennent :

réponse, abrégée
json
{
  "id": "kd7...",
  "status": "pending",
  "url": "https://form.formstep.io/r/rq_...",
  "form": {
    "id": "j58...",
    "name": "Documents and fee for your mortgage application",
    "reused": false,
    "fields": [
      { "key": "payslips", "type": "file" },
      { "key": "fee", "type": "payment" },
      { "key": "call", "type": "schedule-appointment" }
    ]
  }
}

Pour préremplir une question à choix dans le même appel, envoyez des clés d’option, comme pour tout formulaire. La clé d’une option est dérivée de son libellé : Self-employed devient self_employed. Un libellé à la place d’une clé échoue avec INVALID_PREFILL_VALUE, qui liste les clés valides.

L’objet form

ChampCe que ça fait
nameObligatoire, de 1 à 100 caractères. Le nom affiché dans le tableau de bord et dans l’onglet du navigateur, le même pour chaque destinataire. Personnalisez avec prefill et context, jamais avec le nom.
blocksObligatoire, de 1 à 200 blocs, dans l’ordre où le formulaire les affiche. Les blocs des colonnes d’une ligne et les champs d’un groupe répétable comptent aussi.
languageLa langue de l’interface du formulaire. Par défaut : l’anglais.
logo, cover, emojiVotre identité visuelle, envoyée avec chaque demande. logo : { "type": "icon", "name": "HeartIcon" } ou { "type": "image", "url": "https://…" }. cover : { "type": "color", "color": "#0f4c81" } ou { "type": "image", "url": "https://…", "offsetY": 50 }. emoji : un emoji à côté du nom. Voir plus bas.
themeUne apparence par mode de couleur : { "light": "solarized-light", "dark": "dracula" }. Chaque mode prend l’id d’un préréglage, gratuit sur tous les plans, ou un objet de thème complet tel que le prend l’outil MCP formTheme_set, ce qui demande le plan Pro.
settingsLe comportement du formulaire que règle la page des paramètres : l’identité visuelle, un domaine d’e-mail vérifié (emailDomainId), les e-mails au propriétaire et au répondant avec leur texte, le calendrier des relances, l’e-mail d’invitation, la redirection et la conservation. Champs par clé : respondentNotificationTo est la clé d’une question e-mail, redirectQueryParams associe un paramètre à une clé de champ. Le texte des e-mails est du texte brut avec des repères {{key}}. Aucune barrière de mot de passe ou de connexion, et aucun réglage de lien de partage comme la protection anti-robots ou les modifications après envoi : une demande porte son propre lien. Toute autre clé est refusée.
translationsLe formulaire dans d’autres langues, avec un sélecteur de langue pour le destinataire : { "de": { "Your name": "Ihr Name" } }, chaque texte du formulaire ayant pour clé le texte tel que les blocs l’ont écrit. Voir plus bas.

Les blocs

Un bloc prend les paramètres de l’outil editor_insert* correspondant du serveur MCP, sans formId ni after, plus une key facultative. Sans elle, la clé est dérivée du titre : « Full name » devient full_name. Un paramètre que le bloc ne connaît pas est refusé plutôt qu’ignoré.

CatégorieTypes
Questionstext, contact, number, date, time, radio, checkbox, select, switch, rating, linearScale, matrix, ranking, pictureChoice, file, signature, decision
Documentsdocuments, pour montrer des fichiers que le destinataire lit ou signe
Paiements et rendez-vouspayment, scheduleAppointment
Contenuheader, paragraph, image (URL dans src), embedded, table (cells la remplit), list, row (columns : les blocs de chaque colonne, de 2 à 6 colonnes, et widths en pourcentage). Une key facultative permet à la logique d’afficher ou de masquer le bloc.
StructurepageDivider, repeatingGroup (fields : les questions membres sous forme de blocs, chacune avec sa clé), hiddenField (key obligatoire : c’est la clé de context), calculatedField (son name est sa clé), logic

Le texte de header et paragraph, les éléments de liste et les cellules de tableau acceptent bold, italic, struck, <u>underlined</u>, code, $$equation$$, text, <span color=“red” background=“yellow” font=“font-georgia” size=“1.25”>…</span> avec les couleurs, polices et tailles de la barre d’outils de l’éditeur, et {{key}}, qui affiche la valeur du champ portant cette clé. Les marques s’imbriquent ; un marqueur sans son pendant reste du texte. Une colonne d’une ligne accepte tout bloc sauf pageDivider, logic, hiddenField, calculatedField, repeatingGroup, row et scheduleAppointment. Un bloc decision prend son title et ses labels pour approuver, refuser et demander des modifications, dans la langue du formulaire.

Les blocs se désignent entre eux par leur clé, jamais par un id d’élément : un bloc logic prend sa rule comme editor_insertLogic, avec des clés de champ là où il attend des ids, des libellés d’option comme valeurs de condition et des noms de page pour setNextPage.

Paiements

Un bloc payment prend amount et currency, ou amountFromField : la clé d’un champ caché ou d’une question numérique. Avec un champ caché, chaque demande passe son propre montant dans context, si bien qu’un seul formulaire facture à chaque client son propre montant :

un formulaire, un montant par demande
json
{
  "form": {
    "name": "Pay your invoice",
    "blocks": [
      { "type": "hiddenField", "key": "invoice_total" },
      { "type": "payment", "key": "invoice", "title": "Invoice", "currency": "EUR", "amountFromField": "invoice_total" }
    ]
  },
  "recipient": { "email": "ada@example.com" },
  "context": { "invoice_total": 1200 }
}

Une question numérique qui fixe le montant doit être obligatoire et avoir un min supérieur à 0, pour que personne n’arrive au checkout avec un montant vide, nul ou négatif.

Le formulaire encaisse par le compte Stripe par défaut de l’espace de travail, ou par celui que connection désigne. Une personne connecte Stripe une fois pour l’espace de travail ; une automatisation ne le peut pas. Sans compte utilisable, l’appel échoue et vous indique où le connecter.

Rendez-vous

Un bloc scheduleAppointment prend eventType, le titre ou l’id d’un type d’événement de la connexion Cal.com de l’espace de travail, et connection quand l’espace de travail en a plusieurs. Formstep recherche le type d’événement au moment où la demande est créée. prefill: { name, email } désigne les questions dont les réponses remplissent la réservation.

Votre identité visuelle

Une automatisation ne peut pas inventer votre identité visuelle, alors vous la lui donnez une fois et elle l’envoie avec chaque demande : logo, cover et emoji à côté des blocs, et theme pour les couleurs et la typographie.

ChampValeur
logoUne icône de l’éditeur, { "type": "icon", "name": "HeartIcon" }, ou votre propre image, { "type": "image", "url": "https://…/logo.png" }.
coverUne couleur unie, { "type": "color", "color": "#0f4c81" }, ou une image, { "type": "image", "url": "https://…/cover.jpg", "offsetY": 50 }, où offsetY, de 0 à 100, choisit la partie visible d’une image haute.
emojiUn emoji, affiché à côté du nom du formulaire.

Chaque image, y compris le src d’un bloc image, doit être un JPEG, PNG ou WebP public. Formstep récupère chacune une fois à la création du formulaire et en garde sa propre copie : le formulaire et les PDF de ses soumissions survivent ainsi à la source. Une URL impossible à récupérer fait échouer l’appel avec INLINE_FORM_IMAGE_UNREACHABLE, qui nomme le champ. Sans identité visuelle, le formulaire ressemble à un nouveau formulaire créé depuis le tableau de bord.

Traductions

translations publie le formulaire dans d’autres langues en une fois, et le destinataire obtient le sélecteur de langue d’un formulaire traduit : { "de": { "Your name": "Ihr Name", "Order for **{{customer}}**": "Bestellung für **{{customer}}**" } }. Chaque texte du formulaire doit y figurer, avec pour clé le texte exactement tel que les blocs l’ont écrit : titres de question, options, textes indicatifs, en-têtes, paragraphes, éléments de liste, cellules de tableau, noms de groupe et libellés de bouton. Les marques et {{key}} restent comme dans la source. Un texte manquant fait échouer l’appel avec INLINE_FORM_TRANSLATION_MISSING, qui liste les textes à ajouter. Le texte des e-mails n’est pas traduit.

Une définition, un formulaire

Un appel ultérieur avec le même objet form réutilise le même formulaire, et la réponse indique “reused”: true. Cent demandes avec une même définition donnent un seul formulaire, avec un seul ensemble de réponses à exporter. Modifier un bloc, le nom, l’identité visuelle ou une traduction crée un nouveau formulaire ; les demandes déjà envoyées gardent le formulaire avec lequel elles ont été créées. Un formulaire fermé ou dépublié n’est pas réutilisé : l’appel suivant avec la même définition en crée un nouveau.

Quand un appel crée un nouveau formulaire alors qu’un formulaire inline ouvert a les mêmes types de blocs dans le même ordre, la réponse nomme ce formulaire dans form.similarForm. Le plus souvent, un nom ou un titre modifié en est la cause. Pour lire toutes les réponses à un formulaire inline, listez ses soumissions au lieu de lire chaque demande.

Dans le tableau de bord

Un formulaire inline appartient à sa demande : il vit donc sur la page de la demande plutôt que sur la page Formulaires. La page liste les réponses par clé de champ, et Aperçu ouvre le formulaire tel que le destinataire l’a vu, rempli avec ses réponses. Un lien vers l’éditeur ou les réponses d’un formulaire inline ouvre à la place la page Demandes filtrée sur ce formulaire. Les agents voient toujours les formulaires inline dans form_list, et l’API refuse de les modifier.

Un formulaire inline n’a pas de lien de partage, et on ne peut pas en ajouter : ses demandes sont le seul moyen d’y accéder, et le callback transporte toutes les réponses. Il n’a pas non plus d’intégrations.

Comme il n’a pas de page propre, un formulaire inline n’est jamais supprimé à la main. Il disparaît avec sa dernière demande : quand vous supprimez une demande, ou que la période de conservation du formulaire la supprime, et qu’aucune autre demande n’utilise encore le formulaire, le formulaire est supprimé lui aussi.

Quand l’appel est refusé

Chaque refus est une VALIDATION_ERROR dont votre automatisation peut lire le details.reason pour choisir la suite. Rien n’est créé.

MotifQue faire
FORM_SOURCE_MISSING / FORM_SOURCE_AMBIGUOUSEnvoyez exactement l’un des deux : formId ou form.
INLINE_FORM_INVALIDUn bloc ou un champ de form ne respecte pas son schéma. details.issues les nomme un par un.
INLINE_FORM_DUPLICATE_KEYDeux blocs aboutissent à la même clé. Donnez à l’un d’eux sa propre clé.
INLINE_FORM_UNKNOWN_KEYUne règle de logique, amountFromField ou un prefill de rendez-vous désigne une clé qu’aucun bloc ne porte.
INLINE_FORM_AMOUNT_MISSINGPassez un nombre positif pour le champ du montant dans context.
INLINE_FORM_CONNECTION_MISSINGStripe ou Cal.com n’est pas connecté. details.connectUrl est la page qu’une personne ouvre une fois pour le connecter.
INLINE_FORM_EVENT_TYPE_NOT_FOUNDAucun type d’événement Cal.com ne correspond. L’erreur liste ceux qui existent.
INLINE_FORM_IMAGE_UNREACHABLEUne URL d’image n’est pas un JPEG, PNG ou WebP public. details.field la nomme. Utilisez une URL que tout le monde peut ouvrir, ou retirez l’image.
INLINE_FORM_TRANSLATION_MISSINGUne traduction omet des textes du formulaire. details.missing les liste, tels qu’écrits dans les blocs.
INLINE_FORM_PUBLISH_ISSUESLa publication a refusé le formulaire, par exemple à cause d’un type d’événement qu’on ne peut pas réserver depuis un formulaire.

Depuis un agent

Sur le serveur MCP, request_create accepte le même form. Avant de créer un formulaire avec un paiement ou un rendez-vous, un agent lit workspaceConnection_list : l’outil montre les comptes Stripe et Cal.com connectés et, quand l’un manque, le lien qu’une personne doit ouvrir une fois. scheduler_listEventTypes liste les types d’événement qu’un rendez-vous peut désigner, et paymentConnection_setDefault change le compte Stripe par défaut. Avec l’API REST, ces trois outils s’appellent connections.list, scheduler.listEventTypes et paymentConnections.setDefault.