# Méthodes API

Référence complète de toutes les méthodes de l'API REST avec les paramètres, des exemples et les réponses.

## Méthodes API

Référence complète de toutes les méthodes exposées par l’API REST de Formstep. Chaque méthode présente ses paramètres, des exemples de requêtes et la structure des réponses.

> ℹ️ **Un seul endpoint, de nombreuses méthodes**
> <p>
>     Chaque méthode est <code>POST https://api.formstep.io/api/v1</code> avec un corps JSON{' '}
>     <code>{`{"method": "...", "params": {...}}`}</code> et un en-tête <code>Authorization: Bearer fb_...</code>. Consultez la{' '}
>     <a href="/fr/developers/overview">vue d’ensemble de l’API</a> pour l’authentification et la gestion des erreurs, et{' '}
>     <a href="/fr/developers/api-tokens">les tokens API</a> pour le token lui-même.
>   </p>

<h2 id="conventions">Conventions</h2>

<ul>
  <li>
    <code>params</code> peut être omis ; il vaut alors <code>{`{}`}</code> par défaut. Une méthode inconnue renvoie{' '}
    <code>404 METHOD_NOT_FOUND</code>.
  </li>
  <li>
    Un token est lié à <strong>un seul espace de travail</strong>. Nommer un autre espace de travail, ou un formulaire qui s’y trouve,
    renvoie <code>403 FORBIDDEN</code>, même si vous appartenez aux deux.
  </li>
  <li>
    <strong>Pagination.</strong> Les méthodes de liste renvoient <code>{`{ items, nextCursor, hasMore }`}</code> ; la plupart renvoient
    aussi <code>canPaginate</code>, qui vaut <code>false</code> quand <code>hasMore</code> est vrai mais qu’aucun curseur ne peut reprendre
    (recherche approximative). Renvoyez <code>nextCursor</code> comme <code>cursor</code>. <code>limit</code> va de 1 à 100, 20 par défaut —
    sauf <code>requests.list</code>, dont le défaut est 25.
  </li>
  <li>
    <strong>Limites de débit.</strong> 120 appels par minute et par token, en commun avec le{' '}
    <a href="/fr/developers/mcp-server">serveur MCP</a> ; <code>requests.create</code> a sa propre limite de 60 par minute. L’échec
    d’authentification est limité séparément, 30 par 15 minutes et par IP, au-delà desquelles les tokens invalides reçoivent{' '}
    <code>RATE_LIMITED</code> au lieu de <code>UNAUTHORIZED</code>.
  </li>
  <li>
    <strong>Taille du corps.</strong> 1 Mio. Les corps plus volumineux sont rejetés avec <code>VALIDATION_ERROR</code>.
  </li>
  <li>
    <strong>Versionnage.</strong> Le chemin porte la version. Les changements cassants sont livrés sous <code>/api/v2</code> ; les nouvelles
    méthodes et les nouveaux champs de réponse ne le sont pas.
  </li>
</ul>

{/* ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ */}

<h2 id="forms">Formulaires</h2>

{/* ── forms.list ──────────────────────────────────────────────────────────── */}

Lister les formulaires d’un espace de travail. Prend en charge la pagination par curseur et une recherche par nom approximative.

  
    Identifiant de l’espace de travail.
  
  
    Filtrer par dossier. Passez <code>null</code> pour les formulaires à la racine uniquement. Omettez pour tout lister.
  
  
    Recherche par nom approximative. Les résultats sont limités à <code>limit</code> ; non paginés par curseur.
  
  
    Taille de la page (1–100).
  
  
    Curseur de pagination issu d’une réponse précédente.
  

  
```
curl -X POST https://api.formstep.io/api/v1 \
  -H "Authorization: Bearer fb_..." \
  -H "Content-Type: application/json" \
  -d '{
    "method": "forms.list",
    "params": { "workspaceId": "ws_abc123" }
  }'
```

  
    
```
{
  "ok": true,
  "data": {
    "items": [
      {
        "id": "frm_abc123",
        "name": "Contact Form",
        "folderId": null,
        "workspaceId": "ws_abc123",
        "isPublished": true,
        "publishedAt": 1714041851000,
        "unpublishedAt": null,
        "createdAt": 1714041800000,
        "lastEditedAt": 1714042000000,
        "emoji": null
      }
    ],
    "nextCursor": null,
    "hasMore": false,
    "canPaginate": false
  }
}
```

  

{/* ── forms.get ───────────────────────────────────────────────────────────── */}

Obtenir tous les détails d’un formulaire, notamment les questions, la couverture, le logo et une URL de prévisualisation.

  
    Identifiant du formulaire.
  

  
```
curl -X POST https://api.formstep.io/api/v1 \
  -H "Authorization: Bearer fb_..." \
  -H "Content-Type: application/json" \
  -d '{
    "method": "forms.get",
    "params": { "formId": "frm_abc123" }
  }'
```

  
    
```
{
  "ok": true,
  "data": {
    "id": "frm_abc123",
    "name": "Event Feedback",
    "folderId": null,
    "workspaceId": "ws_abc123",
    "isPublished": true,
    "publishedAt": 1714041851000,
    "unpublishedAt": null,
    "createdAt": 1714041800000,
    "lastEditedAt": 1714042000000,
    "emoji": null,
    "questions": [
      {
        "id": "q_1",
        "type": "email-input",
        "inputType": "email",
        "title": "Your email",
        "metadata": null
      },
      {
        "id": "q_2",
        "type": "rating-input",
        "inputType": "rating",
        "title": "Overall experience",
        "metadata": { "kind": "rating", "maxStars": 5 }
      }
    ],
    "hasContent": true,
    "cover": null,
    "logo": null,
    "isDeleted": false,
    "trashedAt": null,
    "previewUrl": "https://formstep.io/preview/abc..."
  }
}
```

  

{/* ── forms.create ────────────────────────────────────────────────────────── */}

Créer un nouveau formulaire vide. Retourne le formulaire et une URL de prévisualisation.

  
    Nom du formulaire (1–255 caractères).
  
  
    Identifiant de l’espace de travail.
  
  
    Placer le formulaire dans un dossier. Omettez pour créer à la racine de l’espace de travail.
  

  
    
      
```
curl -X POST https://api.formstep.io/api/v1 \
  -H "Authorization: Bearer fb_..." \
  -H "Content-Type: application/json" \
  -d '{
    "method": "forms.create",
    "params": {
      "name": "Contact",
      "workspaceId": "ws_abc123"
    }
  }'
```

    
    
      
```
const res = await fetch('https://api.formstep.io/api/v1', {
  method: 'POST',
  headers: {
    Authorization: `Bearer ${process.env.FORMSTEP_TOKEN}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    method: 'forms.create',
    params: { name: 'Contact', workspaceId: 'ws_abc123' },
  }),
})
const { ok, data } = await res.json()
```

    
    
      
```
import os, requests
res = requests.post(
  "https://api.formstep.io/api/v1",
  headers={"Authorization": f"Bearer {os.environ['FORMSTEP_TOKEN']}"},
  json={
    "method": "forms.create",
    "params": {
      "name": "Contact",
      "workspaceId": "ws_abc123",
    },
  },
)
data = res.json()
```

  

  
    
```
{
  "ok": true,
  "data": {
    "id": "frm_new123",
    "name": "Contact",
    "workspaceId": "ws_abc123",
    "folderId": null,
    "isPublished": false,
    "createdAt": 1714041851000,
    "previewUrl": "https://formstep.io/preview/abc..."
  }
}
```

  

{/* ── forms.update ────────────────────────────────────────────────────────── */}

Mettre à jour les métadonnées d’un formulaire : nom, dossier, emoji, couverture ou logo. Ne modifie pas le contenu du formulaire (utilisez les outils d’édition pour cela).

  
    Identifiant du formulaire.
  
  
    Nouveau nom du formulaire (1–255 caractères).
  
  
    Déplacer le formulaire dans un dossier. Passez <code>null</code> pour le déplacer à la racine de l’espace de travail.
  
  
    Emoji du formulaire (10 caractères max). Passez <code>null</code> pour effacer.
  
  
    Couverture. <code>{`{"type": "color", "color": "#ffffff"}`}</code>,{' '}
    <code>{`{"type": "image", "url": "https://...", "offsetY": 50}`}</code> (<code>offsetY</code> 0–100, 50 par défaut), ou{' '}
    <code>{`{"type": "none"}`}</code> pour supprimer. Les URLs d’image doivent être en <code>http(s)</code> ou une URI{' '}
    <code>data:image</code>.
  
  
    Logo. <code>{`{"type": "icon", "name": "HeartIcon"}`}</code>, <code>{`{"type": "image", "url": "https://..."}`}</code>, ou{' '}
    <code>{`{"type": "none"}`}</code> pour supprimer. Les noms d’icônes sont fixes : <code>QuestionMarkIcon</code>,{' '}
    <code>ListBulletsIcon</code>, <code>ChartBarIcon</code>, <code>ClockCountdownIcon</code>, <code>HeartIcon</code>,{' '}
    <code>LightbulbIcon</code>, <code>CheckCircleIcon</code>, <code>MagnifyingGlassIcon</code>, <code>TrendUpIcon</code>,{' '}
    <code>EnvelopeIcon</code>, <code>PhoneIcon</code>, <code>CalendarIcon</code>, <code>LinkIcon</code>, <code>UsersIcon</code>.
  

<p>
  Passez au moins un des cinq champs modifiables. Cela ne change pas le contenu du formulaire — utilisez les outils MCP d’édition pour cela.
</p>

  
```
curl -X POST https://api.formstep.io/api/v1 \
  -H "Authorization: Bearer fb_..." \
  -H "Content-Type: application/json" \
  -d '{
    "method": "forms.update",
    "params": {
      "formId": "frm_abc123",
      "name": "Updated Name",
      "emoji": "📋"
    }
  }'
```

  
    
```
{
  "ok": true,
  "data": {
    "id": "frm_abc123",
    "name": "Updated Name",
    "folderId": null,
    "emoji": "📋"
  }
}
```

  

<p>
  <code>cover</code> et <code>logo</code> ne reviennent que si vous les avez envoyés. Un payload identique à l’état actuel sur chaque champ
  scalaire ajoute <code>noChange: true</code>.
</p>

{/* ── forms.publish ───────────────────────────────────────────────────────── */}

Publier un formulaire pour qu’il puisse accepter des réponses, et figer ses [clés de champ](/fr/requests/field-keys) dans un nouveau snapshot.
Idempotent : un formulaire déjà publié renvoie un succès avec `alreadyPublished: true`, et un formulaire dépublié est republié depuis son
dernier snapshot.

  
    ID du formulaire.
  

<p>
  Un formulaire avec des blocs de contenu mais sans question se publie avec un avertissement. Un formulaire sans aucun contenu ne peut pas
  être publié. Publier ne crée pas d’URL publique — appelez <a href="#share-links-create">shareLinks.create</a> pour cela.
</p>

{/* ── forms.unpublish ─────────────────────────────────────────────────────── */}

Mettre un formulaire hors ligne. Les répondants ne peuvent plus l’ouvrir. Idempotent — un formulaire non publié renvoie{' '}
`alreadyUnpublished: true`. Réversible avec `forms.publish`.

  
    ID du formulaire.
  

{/* ── forms.delete ────────────────────────────────────────────────────────── */}

Déplacer un formulaire vers la corbeille. Ses liens de partage actifs sont révoqués, donc leurs URLs publiques cessent de fonctionner.

  
    ID du formulaire.
  

> ⚠️ **La restauration ne ramène pas les liens**
> <p>
>     <code>forms.restore</code> renvoie le formulaire, mais les liens de partage qu’il a révoqués restent révoqués. Créez-en de nouveaux avec{' '}
>     <code>shareLinks.create</code>. Un formulaire déjà dans la corbeille renvoie <code>alreadyTrashed: true</code> et garde sa date de mise
>     à la corbeille d’origine.
>   </p>

{/* ── forms.restore ───────────────────────────────────────────────────────── */}

Restaurer un formulaire depuis la corbeille.

  
    ID du formulaire.
  
  
    Où le restaurer. Omettez pour son dossier d’origine, <code>null</code> pour la racine de l’espace de travail, ou un ID de dossier.
  

<p>
  Un formulaire qui n’est pas dans la corbeille renvoie <code>alreadyRestored: true</code>.
</p>

{/* ── formSettings.get ────────────────────────────────────────────────────── */}

Lire les paramètres de comportement d’un formulaire.

  
    ID du formulaire.
  

<p>
  Renvoie <code>{`{ settings, isDefault, availableEmailDomains, defaultFromAddress, payment }`}</code>. <code>isDefault</code> vaut vrai
  quand le formulaire n’a pas encore de ligne de paramètres enregistrée et que vous voyez les valeurs par défaut.{' '}
  <code>availableEmailDomains</code> contient les ids de domaines vérifiés à passer comme <code>emailDomainId</code>, et{' '}
  <code>payment</code> indique si Stripe est connecté (le connecter est une étape du tableau de bord).
</p>

{/* ── formSettings.update ─────────────────────────────────────────────────── */}

Mettre à jour les paramètres de comportement d’un formulaire. Une mise à jour partielle : seuls les champs envoyés sont écrits.

  
    ID du formulaire.
  
  
    <code>language</code> (BCP-47, <code>"en"</code> par défaut), <code>requireAuthentication</code>, <code>showBranding</code>,{' '}
    <code>captchaEnabled</code>, <code>passwordEnabled</code>, <code>password</code> (4 caractères ou plus ; une chaîne implique{' '}
    <code>passwordEnabled: true</code>, <code>null</code> retire la protection).
  
  
    <code>notifyOnSubmission</code>, <code>notificationEmails</code> (tableau), <code>selfNotificationSubject</code>,{' '}
    <code>selfNotificationEmailBody</code>, <code>pdfGenerationEnabled</code>, <code>showViewSubmissionButton</code>. Les e-mails du
    propriétaire ne sont pas traduisibles — écrivez-les dans la langue voulue.
  
  
    <code>respondentNotificationEnabled</code>, <code>respondentNotificationTo</code> (l’id de champ d’une question e-mail, ou{' '}
    <code>null</code>), <code>respondentNotificationSubject</code>, <code>respondentNotificationBody</code>,{' '}
    <code>respondentNotificationPdfEnabled</code>.
  
  
    <code>respondentReminderEnabled</code>, <code>respondentReminderTo</code>, <code>respondentReminderSubject</code>,{' '}
    <code>respondentReminderBody</code>, <code>respondentReminderRequiredFieldIds</code>, et <code>reminderSteps</code> — des décalages
    d’inactivité comme <code>["1d","3d","1w"]</code>, 5 au maximum, triés et dédupliqués à l’enregistrement, <code>[]</code> pour aucun. Le
    planning s’applique aussi bien aux réponses abandonnées sur lien public qu’aux demandes. Pro.
  
  
    <code>redirectUrl</code> (<code>http(s)</code> ; <code>null</code> ou <code>""</code> efface), <code>redirectQueryParams</code> (
    <code>{`[{ paramName, fieldId }]`}</code>), <code>allowAnotherResponse</code> (mutuellement exclusif avec une redirection),{' '}
    <code>maxSubmissionsPerRespondent</code> (0 = illimité, max 1000), <code>editAfterSubmit</code>, <code>maxEdits</code> (max 3 ; 0
    signifie illimité avec Pro et Business, 3 avec Free).
  
  
    <code>draftRetentionDays</code> et <code>submissionRetentionDays</code> (0–36500, <code>null</code> revient à la valeur par défaut). La
    rétention des soumissions est Business, et la définir efface toute date de suppression fixe configurée dans l’éditeur.
  
  
    Un id de domaine e-mail vérifié depuis <code>formSettings.get</code>, pour une adresse d’expéditeur personnalisée. <code>null</code>{' '}
    réinitialise à l’expéditeur par défaut.
  

<p>
  Les objets et corps sont du texte brut et acceptent les paramètres <code>{`{{variable}}`}</code> ; les sauts de ligne deviennent des
  paragraphes. Personnaliser un objet ou un corps destiné au répondant le rend traduisible, donc ses clés apparaissent immédiatement dans{' '}
  <code>translations.listEntries</code>.
</p>

{/* ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ */}

<h2 id="submissions" class="border-t border-border pt-8">
  Soumissions
</h2>

{/* ── submissions.list ────────────────────────────────────────────────────── */}

Lister les soumissions d’un formulaire, page la plus récente en premier, avec pagination par curseur.

  
    ID du formulaire.
  
  
    Inclure les réponses commencées mais jamais soumises. Les brouillons sont une fonctionnalité Pro : sur Free, seules les soumissions
    terminées sont listées.
  
  
    Joindre les traductions IA stockées des réponses sous <code>items[].translation.display</code>, avec les mêmes clés que{' '}
    <code>display</code>. <code>items[].answers</code> et <code>items[].display</code> restent toujours l’original.
  
  
    Taille de page (1–100).
  
  
    Curseur de pagination issu d’une réponse précédente.
  

  
    
```
{
  "ok": true,
  "data": {
    "formId": "frm_abc123",
    "formName": "Event Feedback",
    "items": [
      {
        "id": "sub_xyz789",
        "submittedAt": "2026-05-18 18:02:28",
        "isCompleted": true,
        "createdAt": "2026-05-18 18:02:19",
        "answers": {
          "email": "user@example.com",
          "plan": "pro",
          "contacts": [{ "name": "Ada" }, { "name": "Grace" }]
        },
        "display": {
          "email": "user@example.com",
          "plan": "Pro",
          "contacts": "Ada, Grace"
        }
      }
    ],
    "nextCursor": null,
    "hasMore": false,
    "canPaginate": false
  }
}
```

  

> ℹ️ **Les mêmes réponses que les webhooks et les callbacks**
> <p>
>     Chaque élément porte <code>answers</code> indexé par <a href="/fr/requests/field-keys">clé de champ</a> et <code>display</code> avec les
>     mêmes clés, en texte lisible — la forme que portent un <a href="/fr/developers/webhooks-reference">payload de webhook</a>, un{' '}
>     <a href="/fr/requests/callbacks">callback de demande</a> et <code>requests.get</code>. Une réponse à choix est sa clé d’option, un
>     groupe répétable un tableau d’instances. Appelez <code>fields.list</code> pour le titre et les libellés d’option de chaque clé. Cette
>     méthode ne renvoie aucun total.
>   </p>

{/* ── submissions.pdf ─────────────────────────────────────────────────────── */}

Obtenir un lien vers le PDF d’une soumission. Conçu pour le connecteur Zapier : renvoie un résultat seulement quand le formulaire a une
intégration Zapier active configurée pour inclure le PDF, et que le PDF a été conservé.

  
    ID du formulaire.
  
  
    ID de la soumission. Doit appartenir à ce formulaire et être complète.
  

  
    
```
{
  "ok": true,
  "data": {
    "url": "https://api.formstep.io/api/storage/...",
    "filename": "formstep-submission-sub_xyz789.pdf",
    "contentType": "application/pdf",
    "byteLength": 148213
  }
}
```

  

{/* ── submissions.sample ──────────────────────────────────────────────────── */}

Construire un payload de soumission d’exemple pour un formulaire, sans aucune donnée réelle. C’est exactement la forme que porte une
livraison de soumission sur lien public, donc les connecteurs s’en servent pour découvrir les champs ; une soumission provenant d’une
demande atteint un abonnement sous forme de <code>request.completed</code> à la place, échantillonnée par{' '}

<a href="#requests-sample">requests.sample</a>. <code>data.form.snapshotId</code> est la version publiée actuelle du formulaire, le même id
que portent les événements en direct, ou <code>null</code> tant que le formulaire n’est pas publié.

  
    Identifiant du formulaire.
  

  
    
```
{
  "ok": true,
  "data": {
    "id": "evt_example000000000000",
    "type": "submission.completed",
    "createdAt": "2026-05-18T19:00:00.000Z",
    "apiVersion": "2026-09-24",
    "test": true,
    "data": {
      "form": { "id": "frm_abc123", "name": "Event Feedback", "snapshotId": "js7abc123" },
      "submission": {
        "id": "sub_example000000000000",
        "respondentEmail": "respondent@example.com",
        "submittedAt": "2026-05-18T19:00:00.000Z",
        "updatedAt": null,
        "editCount": 0,
        "pdfUrl": null,
        "language": "en"
      },
      "answers": { "your_email": "john@example.com" },
      "display": { "your_email": "john@example.com" }
    }
  }
}
```

  

<p>
  La sémantique des champs et du payload est documentée une seule fois, dans la{' '}
  <a href="/fr/developers/webhooks-reference#payload">référence webhooks</a>.
</p>

{/* ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ */}

<h2 id="share-links" class="border-t border-border pt-8">
  Liens de partage
</h2>

{/* ── shareLinks.list ─────────────────────────────────────────────────────── */}

Lister les liens de partage d’un formulaire.

  
    Identifiant du formulaire.
  
  
    Inclure les liens révoqués dans le résultat.
  
  
    Taille de la page (1–100).
  
  
    Curseur de pagination.
  

  
    
```
{
  "ok": true,
  "data": {
    "items": [
      {
        "id": "sl_abc123",
        "code": "RPjNes52",
        "url": "https://formstep.io/RPjNes52",
        "customDomainUrl": null,
        "formId": "frm_abc123",
        "createdAt": 1714041851000,
        "expiresAt": null,
        "maxClaims": null,
        "claimedCount": 7,
        "isRevoked": false,
        "revokedAt": null,
        "customDomainId": null,
        "customSlug": null
      }
    ],
    "availableCustomDomains": [],
    "nextCursor": null,
    "hasMore": false,
    "canPaginate": false
  }
}
```

  

{/* ── shareLinks.create ───────────────────────────────────────────────────── */}

Créer un lien de partage pour un formulaire. Le formulaire doit d’abord être publié.

  
    Identifiant du formulaire. Un formulaire non publié, ou qui a été publié puis dépublié, est rejeté — appelez d’abord{' '}
    <code>forms.publish</code>.
  
  
    Date d’expiration sous forme d’horodatage Unix futur en millisecondes. Contrairement à la mise à jour, <code>0</code> n’est pas accepté
    ici.
  
  
    Nombre maximum d’utilisations de ce lien. Doit être positif ; utilisez <code>shareLinks.update</code> pour le supprimer plus tard.
  

<p>
  La réponse est le lien de partage (même forme qu’un élément de <code>shareLinks.list</code>) plus <code>availableCustomDomains</code>,
  pour que vous puissiez enchaîner avec <code>shareLinks.update</code> et en associer un.
</p>

  
```
curl -X POST https://api.formstep.io/api/v1 \
  -H "Authorization: Bearer fb_..." \
  -H "Content-Type: application/json" \
  -d '{
    "method": "shareLinks.create",
    "params": {
      "formId": "frm_abc123",
      "maxClaims": 100
    }
  }'
```

{/* ── shareLinks.update ───────────────────────────────────────────────────── */}

Mettre à jour un lien de partage. Permet de modifier l’expiration, le nombre maximum d’utilisations, le domaine personnalisé, le slug, ou de révoquer le lien.

  
    Identifiant du lien de partage.
  
  
    Nouvel horodatage d’expiration en millisecondes. Passez <code>0</code> pour supprimer l’expiration.
  
  
    Nouveau nombre maximum d’utilisations. Passez <code>-1</code> pour supprimer la limite.
  
  
    Associer un domaine personnalisé. Passez <code>null</code> pour dissocier.
  
  
    Slug d’URL personnalisé (3–64 caractères, minuscules alphanumériques et tirets). Requis avec <code>customDomainId</code> ; passez les
    deux à <code>null</code> pour dissocier. <code>login</code>, <code>auth-callback</code>, <code>preview</code>, <code>payment</code>,{' '}
    <code>api</code>, <code>admin</code> et <code>health</code> sont réservés.
  
  
    Définir à <code>true</code> pour révoquer définitivement le lien. Ne peut pas être combiné avec d’autres champs, et ne peut pas être
    annulé — c’est le seul moyen de supprimer un lien de partage.
  

{/* ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ */}

<h2 id="fields" class="border-t border-border pt-8">
  Champs
</h2>

{/* ── fields.list ─────────────────────────────────────────────────────────── */}

Liste chaque champ de la version actuellement publiée d’un formulaire, avec la clé pour l’adresser. Appelez ceci avant `requests.create` au
lieu de coder les clés en dur. Voir [Clés de champ](/fr/requests/field-keys).

  
    ID du formulaire. Un formulaire qui n’a jamais été publié n’a pas encore de clés de champ et répond avec <code>published: false</code>{' '}
    sans éléments.
  

  
```
curl -X POST https://api.formstep.io/api/v1 \
  -H "Authorization: Bearer fb_..." \
  -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": "satisfaction", "type": "matrix", "title": "How did we do?", "required": false, "prefillable": true,
        "rows": [{ "key": "delivery_speed", "label": "Delivery speed" }],
        "columns": [{ "key": "very_good", "label": "Very good" }, { "key": "poor", "label": "Poor" }]
      },
      { "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 }
    ],
    "hasMore": false
  }
}
```

  

> ℹ️ **Lire les indicateurs**
> <p>
>     <code>context: true</code> signale un champ caché — sa valeur va dans <code>context</code>, jamais dans <code>prefill</code>.{' '}
>     <code>prefillable: false</code> signale un champ pour lequel personne ne peut fournir de valeur (fichier, signature, paiement, prise de
>     rendez-vous, documents). Pour une question à choix, envoyez la <strong>clé</strong> de l’option, pas son libellé ; une matrice liste ses{' '}
>     <code>rows</code> et <code>columns</code> de la même façon et prend <code>{'{ "row_key": "column_key" }'}</code>.{' '}
>     <code>calculated: true</code> signale un champ calculé : le formulaire calcule sa valeur, vous la relisez dans <code>answers</code>, et
>     rien ne peut l’envoyer.
>   </p>

<p>
  Un groupe répétable est <code>type: "group"</code> avec <code>repeating: true</code> et un tableau <code>members</code>. Un bloc Documents
  est <code>type: "documents"</code> et porte <code>documents: [{`{ name }`}]</code>, les fichiers déjà rédigés que chaque répondant voit
  déjà.
</p>

{/* ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ */}

<h2 id="requests" class="border-t border-border pt-8">
  Demandes
</h2>

Une demande assigne un formulaire publié à une personne et vous rappelle quand elle se termine. Le guide conceptuel se trouve dans
[Créer une demande](/fr/requests/creating-requests) ; ceci est la liste des paramètres.

{/* ── requests.create ─────────────────────────────────────────────────────── */}

Crée une demande. Consomme une unité de l’allocation mensuelle de l’espace de travail, que le destinataire réponde ou non.

  
    Le formulaire publié à assigner.
  
  
    <code>{`{ email?, name? }`}</code>. Un e-mail est requis quand <code>delivery</code> vaut <code>"email"</code> ; sinon il identifie
    simplement la personne sur la page Demandes et sur ses réponses.
  
  
    Réponses initiales par clé de champ. Le destinataire les voit et peut les modifier.
  
  
    Clés préremplies que le destinataire ne peut pas modifier. Chaque clé ici doit aussi apparaître dans <code>prefill</code>, et un champ
    verrouillé obligatoire doit être prérempli avec une valeur non vide.
  
  
    Valeurs pour les champs cachés du formulaire, par clé de champ. Fiables, immuables, et renvoyées dans le callback. Une clé inconnue est
    rejetée avec <code>UNKNOWN_FIELD_KEY</code>.
  
  
    Votre propre suivi. N’atteint jamais le formulaire ; revient dans les callbacks et les lectures.
  
  
    L’une des langues publiées du formulaire. Par défaut, celle du formulaire.
  
  
    <code>"email"</code> pour que Formstep envoie l’invitation (nécessite un e-mail de destinataire, et Pro ou Business ou l’une des 10
    invitations gratuites d’un compte Free), ou <code>"none"</code> pour livrer le lien vous-même.
  
  
    Remplace le planning de rappel du formulaire pour cette demande. Un tableau vide désactive les rappels.
  
  
    Millisecondes epoch. Par défaut 30 jours, 365 jours au maximum.
  
  
    Où Formstep envoie le callback en POST quand la demande se termine. HTTPS uniquement, et l’hôte doit résoudre vers une adresse publique.
  
  
    Votre propre id pour cette demande. Filtrable dans <code>requests.list</code>.
  
  
    Le répéter avec le même corps renvoie la demande d’origine avec <code>deduplicated: true</code>. Un corps différent est rejeté. Les clés
    vivent 30 jours.
  
  
    Génère le lien sur l’un de vos domaines personnalisés. API REST uniquement.
  
  
    <code>{`[{ documentId, field?, name? }]`}</code> — fichiers remis à ce seul destinataire, téléversés au préalable avec{' '}
    <a href="#documents-create">documents.create</a>.
  
  
    Un essai à blanc : rien n’est envoyé par e-mail, le callback porte <code>"test": true</code>, et la soumission ne compte nulle part. Le
    lien se ferme dans les 24 heures, et sur Free un workspace peut créer 10 demandes de test par jour.
  

  
```
curl -X POST https://api.formstep.io/api/v1 \
  -H "Authorization: Bearer fb_..." \
  -H "Content-Type: application/json" \
  -d '{
    "method": "requests.create",
    "params": {
      "formId": "j57...",
      "recipient": { "email": "ada@acme.com", "name": "Ada" },
      "prefill": { "company_name": "Acme" },
      "readonly": ["company_name"],
      "context": { "case_id": "CASE-9" },
      "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.formstep.io/r/rq_...",
    "deliveryStatus": "queued",
    "expiresAt": 1794787200000,
    "createdAt": 1789379200000,
    "externalId": "run-42",
    "deduplicated": false
  }
}
```

  

> ⚠️ **Conservez l’url**
> <p>
>     <code>url</code> porte le token à usage unique. <code>requests.get</code> peut généralement le reconstruire, mais il revient{' '}
>     <code>null</code> pour une demande créée avant que le déploiement n’ait de clé de token de demande. Si vous livrez le lien vous-même,
>     stockez-le à la création.
>   </p>

<p>
  <code>deliveryStatus</code> vaut <code>not_requested</code> jusqu’à ce qu’une invitation soit mise en file, puis <code>queued</code> →{' '}
  <code>sent</code> ou <code>failed</code>, et <code>bounced</code> une fois que le fournisseur d’e-mail signale un rejet définitif ou une
  plainte.
</p>

{/* ── requests.get ────────────────────────────────────────────────────────── */}

Récupère une demande en entier : statut, résultat, ce qui a été prérempli, sa chronologie, et — une fois terminée — <code>answers</code> et <code>display</code> classées par clé de champ, les deux mêmes maps que porte le callback.

  
    ID de la demande.
  

  
    
```
{
  "ok": true,
  "data": {
    "id": "kd7...",
    "formId": "j57...",
    "status": "completed",
    "outcome": "approve",
    "isTest": false,
    "recipient": { "email": "ada@acme.com", "name": "Ada" },
    "language": "en",
    "externalId": "run-42",
    "metadata": null,
    "context": { "case_id": "CASE-9" },
    "prefill": { "company_name": "Acme" },
    "readonlyKeys": ["company_name"],
    "delivery": "email",
    "deliveryStatus": "sent",
    "hasCallback": true,
    "callbackFailedAt": null,
    "submissionId": "kp2...",
    "url": "https://form.formstep.io/r/rq_...",
    "answers": { "company_name": "Acme", "decision": "approve" },
    "display": { "company_name": "Acme", "decision": "Approve" },
    "timeline": [
      { "id": "kd7...:created", "type": "created", "at": 1789379200000 },
      { "id": "kd7...:completed", "type": "completed", "at": 1789465600000 }
    ],
    "expiresAt": 1794787200000,
    "completedAt": 1789465600000
  }
}
```

  

> ℹ️ **outcome vs status**
> <p>
>     <code>status</code> indique si la demande s’est terminée ; <code>outcome</code> indique ce que le destinataire a décidé —{' '}
>     <code>approve</code>, <code>decline</code>, <code>changes</code>, ou <code>null</code> pour tout sauf une demande terminée dont le
>     destinataire a choisi l’une des trois — y compris un formulaire sans{' '}
>     <a href="/fr/requests/decisions-and-approvals">question de décision</a>. L’URL de callback elle-même n’est jamais renvoyée ;{' '}
>     <code>hasCallback</code> indique seulement si l’une est définie.
>   </p>

<p>
  L’exemple ci-dessus est réduit. Une réponse complète porte aussi <code>workspaceId</code>, <code>formSnapshotId</code>,{' '}
  <code>createdVia</code>, <code>documents</code>, <code>reminderStep</code>, <code>remindersSent</code>, <code>reminderDueAt</code>,{' '}
  <code>dataPurgedAt</code>, et le reste des horodatages (<code>updatedAt</code>, <code>openedAt</code>, <code>startedAt</code>,{' '}
  <code>lastActivityAt</code>, <code>expiredAt</code>, <code>canceledAt</code>, <code>canceledBy</code>, <code>cancelReason</code>).
</p>
<p>
  Deux champs indiquent quand la copie sous vos yeux est la seule copie. <code>callbackFailedAt</code> est défini tant que le callback de
  cette demande a épuisé ses tentatives, et effacé une fois qu’un passe ou que vous le rejouez. <code>dataPurgedAt</code> est défini une
  fois que la rétention a dépouillé la demande : <code>context</code>, <code>prefill</code> et <code>metadata</code> reviennent vides,{' '}
  <code>readonlyKeys</code> et <code>documents</code> valent <code>[]</code>, et <code>submissionId</code>, <code>answers</code> et{' '}
  <code>display</code> valent <code>null</code>.
</p>
<p>
  <code>timeline</code> est dérivée, du plus ancien au plus récent. Chaque entrée a un <code>id</code>, un <code>at</code>, et un{' '}
  <code>type</code> — <code>created</code>, <code>invitation</code>, <code>reminder</code>, <code>opened</code>, <code>started</code>,{' '}
  <code>completed</code>, <code>expired</code>, <code>canceled</code>, <code>callback</code>. Les entrées de livraison ajoutent{' '}
  <code>deliveryStatus</code> et <code>attemptCount</code>, et les callbacks ajoutent <code>eventType</code>. Les lignes de livraison sont
  conservées 30 jours, donc les chronologies plus anciennes s’amincissent jusqu’aux horodatages.
</p>

{/* ── requests.list ───────────────────────────────────────────────────────── */}

Liste les demandes d’un espace de travail ou d’un formulaire, les plus récentes en premier. Les demandes de test sont exclues sauf si vous
les demandez.

  
    Limite à un espace de travail. Fournissez ceci ou <code>formId</code>.
  
  
    Limite à un formulaire.
  
  
    <code>pending</code>, <code>completed</code>, <code>expired</code>, ou <code>canceled</code>.
  
  
    <code>approve</code>, <code>decline</code>, ou <code>changes</code>. Implique les demandes terminées uniquement.
  
  
    Votre propre id, pour retrouver la demande créée par une exécution.
  
  
    Inclut les demandes créées avec <code>test: true</code>.
  
  
    Taille de page (1–100).
  
  
    Curseur de pagination d’une réponse précédente.
  

  
    
```
{
  "ok": true,
  "data": {
    "items": [
      {
        "id": "kd7...",
        "formId": "j57...",
        "status": "pending",
        "outcome": null,
        "recipient": { "email": "ada@acme.com", "name": "Ada" },
        "externalId": "run-42",
        "deliveryStatus": "sent",
        "expiresAt": 1794787200000,
        "createdAt": 1789379200000
      }
    ],
    "nextCursor": null,
    "hasMore": false
  }
}
```

  

<p>
  Les éléments de la liste portent les mêmes champs que <code>requests.get</code> moins <code>url</code>, <code>answers</code>,{' '}
  <code>display</code>, et <code>timeline</code>, et chacun porte <code>isTest</code>. Fournissez <code>workspaceId</code> ou{' '}
  <code>formId</code> — n’en fournir aucun renvoie <code>400 VALIDATION_ERROR</code> avec la raison <code>SCOPE_REQUIRED</code>.{' '}
  <code>outcome</code> prime sur <code>status</code>, puisque seule une demande terminée a un verdict.
</p>

{/* ── requests.cancel ─────────────────────────────────────────────────────── */}

Retire une demande en attente. Le lien cesse de fonctionner, le destinataire voit un avis de retrait, et un callback `request.canceled` se
déclenche.

  
    ID de la demande.
  
  
    Votre note expliquant pourquoi, conservée sur la demande et envoyée dans le callback.
  

{/* ── requests.remind ─────────────────────────────────────────────────────── */}

Envoie un e-mail au destinataire maintenant, sans toucher au planning de rappel. Nécessite un e-mail de destinataire et un plan Pro ou
Business.

  
    ID de la demande. Doit encore être en attente, et ne pas être une demande de test.
  

<p>
  Deux planchers s’appliquent : au moins 10 minutes entre deux rappels manuels, et 8 rappels au maximum par demande au total, manuels et
  programmés confondus. Le planning automatique n’est pas touché — <code>reminderStep</code> et <code>reminderDueAt</code> restent où ils
  étaient.
</p>

{/* ── requests.replayCallback ─────────────────────────────────────────────── */}

Renvoie le callback qu’une demande a déclenché à sa fin — même charge utile, même id d’événement, pour qu’un récepteur qui l’a déjà traité
puisse dédupliquer. À utiliser après avoir réparé un point de terminaison défaillant.

  
    ID de la demande. Doit être terminée, expirée, ou annulée.
  

  
    
```
{
  "ok": true,
  "data": { "dispatchId": "kf9...", "eventId": "evt_kf9..." }
}
```

  

{/* ── requests.sample ─────────────────────────────────────────────────────── */}

Construire un événement de demande d’exemple pour un formulaire, sans aucune demande réelle. C’est exactement l’enveloppe que reçoit un
abonnement <code>request\_\*</code> créé avec <a href="#webhooks-create">webhooks.create</a>, donc les connecteurs s’en servent pour
découvrir les champs. Un exemple terminé porte les mêmes réponses d’exemple que montre{' '}

<a href="#submissions-sample">submissions.sample</a> ; un exemple expiré ou annulé porte seulement le bloc request.

  
    Identifiant du formulaire.
  
  
    La fin à illustrer par un exemple, dans l’orthographe de <code>webhooks.create</code>. Le <code>type</code> de l’enveloppe est la forme
    avec point.
  

  
    
```
{
  "ok": true,
  "data": {
    "id": "evt_example000000000000",
    "type": "request.completed",
    "createdAt": "2026-05-18T19:00:00.000Z",
    "apiVersion": "2026-09-24",
    "test": true,
    "data": {
      "request": {
        "id": "req_example000000000000",
        "externalId": "order-1234",
        "status": "completed",
        "language": "en",
        "recipient": { "email": "recipient@example.com", "name": "Sample Recipient" },
        "metadata": { "source": "sample" },
        "context": {},
        "createdAt": "2026-05-18T18:00:00.000Z",
        "completedAt": "2026-05-18T19:00:00.000Z"
      },
      "form": { "id": "frm_abc123", "name": "Event Feedback", "snapshotId": "js7abc123" },
      "submission": {
        "id": "sub_example000000000000",
        "respondentEmail": "respondent@example.com",
        "submittedAt": "2026-05-18T19:00:00.000Z",
        "updatedAt": null,
        "editCount": 0,
        "pdfUrl": null,
        "language": "en"
      },
      "answers": { "your_email": "john@example.com" },
      "display": { "your_email": "john@example.com" }
    }
  }
}
```

  

<p>
  Le bloc request et le résultat sont documentés sur la <a href="/fr/requests/callbacks#payload">page des callbacks</a> ; le volet
  soumission, sur la <a href="/fr/developers/webhooks-reference#payload">référence webhooks</a>. Les ids d’exemple sont les valeurs fixes
  indiquées ci-dessus et <code>test</code> vaut <code>true</code>, afin qu’un récepteur puisse distinguer un exemple d’un événement réel.
</p>

{/* ── documents.create ────────────────────────────────────────────────────── */}

Réserver un téléversement pour un fichier que vous remettrez à un destinataire via le [bloc Documents](/fr/building-forms/documents-block) du
formulaire. Les octets ne transitent jamais par cette API : vous obtenez un `PUT` présigné, vous téléversez, et `requests.create` vérifie
l’objet avant que la demande n’existe.

  
    Le formulaire dont le bloc Documents affichera le fichier. Limite le téléversement à cet espace de travail.
  
  
    Nom d’affichage vu par le destinataire (1–200 caractères). Remplaçable par demande.
  
  
    <code>application/pdf</code> ou un type d’image : <code>image/png</code>, <code>image/jpeg</code>, <code>image/webp</code>,{' '}
    <code>image/gif</code>, <code>image/svg+xml</code>, <code>image/avif</code>, <code>image/bmp</code>, <code>image/tiff</code>. Les
    documents Office ne sont pas acceptés.
  
  
    Longueur exacte en octets. Maximum 25 Mo (26 214 400).
  
  
    Digest hexadécimal des octets. Vérifié après le téléversement quand il est fourni.
  

  
    
```
{
  "ok": true,
  "data": {
    "id": "kn7...",
    "name": "Lease contract draft",
    "contentType": "application/pdf",
    "size": 412000,
    "uploadUrl": "https://...",
    "expiresAt": 1792198800000
  }
}
```

  

<p>
  Faites un <code>PUT</code> des octets bruts vers <code>uploadUrl</code> dans l’heure, avec <code>Content-Type</code> réglé sur le type
  déclaré, puis référencez l’id depuis <code>requests.create</code> :
</p>

```
{
  "method": "requests.create",
  "params": {
    "formId": "j57...",
    "documents": [
      { "documentId": "kn7...", "name": "Your lease contract" },
      { "documentId": "kn8...", "field": "attachments" }
    ]
  }
}
```

<ul>
  <li>
    <code>field</code> est la clé de champ du bloc Documents. Optionnelle quand le formulaire a exactement un bloc ; requise avec deux ou
    plus.
  </li>
  <li>Les documents rédigés du bloc restent ; les vôtres apparaissent en dessous, pour ce seul destinataire.</li>
  <li>Plafonds : 25 Mo par document, 100 Mo de documents par demande, 20 documents affichés par bloc, documents rédigés compris.</li>
  <li>
    Un téléversement peut être référencé par un nombre quelconque de demandes. Un téléversement que rien ne référence expire. Les octets
    comptent dans le stockage du propriétaire de l’espace de travail jusqu’à ce que la dernière demande qui les référence soit dépouillée
    par la rétention.
  </li>
</ul>

<p>
  Chaque échec ici est <code>400 VALIDATION_ERROR</code> avec un <code>details.reason</code> : <code>DOCUMENT_TYPE_NOT_ALLOWED</code>,{' '}
  <code>DOCUMENT_TOO_LARGE</code>, <code>INVALID_DOCUMENT_NAME</code> ou <code>INVALID_DOCUMENT_SHA256</code> pour cette méthode, et{' '}
  <code>DOCUMENT_NOT_FOUND</code>, <code>DOCUMENT_NOT_UPLOADED</code> (vous avez sauté le <code>PUT</code>), <code>DOCUMENT_INVALID</code>,{' '}
  <code>INVALID_DOCUMENT_TARGET</code>, <code>DOCUMENTS_TOO_LARGE</code> ou <code>DOCUMENTS_TOO_MANY</code> pour{' '}
  <code>requests.create</code>.
</p>

{/* ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ */}

<h2 id="webhooks" class="border-t border-border pt-8">
  Webhooks
</h2>

{/* ── webhooks.list ───────────────────────────────────────────────────────── */}

Lister les abonnements webhook d’un formulaire.

  
    Identifiant du formulaire.
  

  
    
```
{
  "ok": true,
  "data": {
    "items": [
      {
        "subscriptionId": "int_abc123",
        "formId": "frm_abc123",
        "provider": "zapier",
        "targetUrl": "https://hooks.zapier.com/...",
        "eventType": "submission_created",
        "status": "active",
        "createdAt": 1714041851000
      }
    ],
    "hasMore": false
  }
}
```

  

{/* ── webhooks.create ─────────────────────────────────────────────────────── */}

Abonner une URL aux événements d’un formulaire : soumissions nouvelles ou abandonnées, ou demandes du formulaire qui se terminent. L’URL
doit utiliser HTTPS.

  
    Identifiant du formulaire.
  
  
    URL HTTPS destinée à recevoir les charges utiles du webhook.
  
  
    À quel outil appartient l’abonnement. C’est une étiquette pour votre propre suivi — il n’y a aucune application de marketplace à
    installer, et tous les fournisseurs se comportent de la même façon.
  
  
    Type d’événement auquel s’abonner. Les trois types <code>submission_*</code> livrent le payload de soumission :{' '}
    <code>submission_created</code> une première soumission, <code>submission_updated</code> une modification par le répondant, et{' '}
    <code>submission_abandoned</code> un brouillon inactif. Les trois types <code>request_*</code> livrent l’
    <a href="/fr/requests/callbacks#payload">événement de demande</a> correspondant chaque fois qu’une demande du formulaire se termine de
    cette façon, signé avec le secret de cet abonnement ; les demandes de test n’atteignent aucun abonnement.
  
  
    Requis quand <code>eventType</code> vaut <code>submission_abandoned</code> ; rejeté pour tout autre type.
  
  
    Secret de signature HMAC optionnel, 32 à 255 caractères. Quand il est fourni, les livraisons incluent <code>X-Formstep-Signature</code>.
    Le secret est stocké mais jamais renvoyé par l’API.
  

  
```
curl -X POST https://api.formstep.io/api/v1 \
  -H "Authorization: Bearer fb_..." \
  -H "Content-Type: application/json" \
  -d '{
    "method": "webhooks.create",
    "params": {
      "formId": "frm_abc123",
      "targetUrl": "https://hooks.zapier.com/hooks/catch/...",
      "provider": "zapier",
      "eventType": "submission_created",
      "signingSecret": "whsec_0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"
    }
  }'
```

  
    
```
{
  "ok": true,
  "data": {
    "subscriptionId": "int_new123",
    "formId": "frm_abc123",
    "provider": "zapier",
    "targetUrl": "https://hooks.zapier.com/hooks/catch/...",
    "eventType": "submission_created"
  }
}
```

  

<p>
  Les abonnements de soumission abandonnée renvoient l’<code>idleWindow</code> choisi, à la fois depuis <code>webhooks.create</code> et{' '}
  <code>webhooks.list</code>. Tout autre abonnement l’omet.
</p>

<p>
  Un abonnement de demande entend les mêmes événements qu’un <a href="/fr/requests/callbacks">callback</a>, mais comme sa propre livraison :
  son propre id d’événement, sa propre signature, et son propre budget de cinq tentatives, après quoi l’abonnement se met en pause. Une
  demande créée avec un <code>callbackUrl</code> sur un formulaire ayant un abonnement <code>request_completed</code> se déclenche donc deux
  fois, une fois vers chaque récepteur. <code>requests.replayCallback</code> ne renvoie que le callback. Utilisez{' '}
  <a href="#requests-sample">requests.sample</a> pour voir le payload avant qu’une demande ne se soit terminée.
</p>

{/* ── webhooks.delete ─────────────────────────────────────────────────────── */}

Supprimer un abonnement webhook.

  
    Identifiant d’abonnement issu de <code>webhooks.list</code> ou <code>webhooks.create</code>.
  

  
    
```
{
  "ok": true,
  "data": {
    "subscriptionId": "int_abc123",
    "deleted": true
  }
}
```

  

{/* ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ */}

<h2 id="analytics" class="border-t border-border pt-8">
  Analytiques
</h2>

{/* ── analytics.get ───────────────────────────────────────────────────────── */}

Obtenir les métriques analytiques agrégées d’un formulaire. Prend en charge les filtres par plage de dates, appareil, source de trafic et pays.

Les analyses sont une fonctionnalité Pro, et la règle suit le forfait du **propriétaire** de l’espace de travail, comme l’onglet Analyses du tableau de bord. Si le propriétaire n’est pas sur Pro, l’appel renvoie `UPGRADE_REQUIRED` — y compris pour l’historique enregistré quand il l’était. Un membre Free dans l’espace d’un propriétaire Pro reçoit bien les données.

  
    Identifiant du formulaire.
  
  
    Début de la plage de dates sous forme d’horodatage Unix en millisecondes. Doit être inférieur ou égal à <code>to</code> quand les deux
    sont définis.
  
  
    Fin de la plage de dates sous forme d’horodatage Unix en millisecondes. Omettez les deux pour tout l’historique — <code>period</code>{' '}
    revient alors comme <code>{`{ "from": null, "to": null }`}</code>.
  
  
    Filtrer par type d’appareil.
  
  
    Filtrer par source de trafic (ex. <code>"Direct"</code>, <code>"Google"</code>).
  
  
    Filtrer par code pays à 2 lettres (ex. <code>"US"</code>, <code>"DE"</code>).
  
  
    Renvoyer aussi les événements analytiques assainis derrière les métriques, pour votre propre analyse. Aucun id de visiteur.
  

<p>
  Les taux sont des nombres de 0 à 100, les compteurs sont des entiers, et <code>totalEvents</code> est le nombre brut de lignes
  d’événements avant déduplication en visiteurs uniques.
</p>

  
    
```
{
  "ok": true,
  "data": {
    "formId": "frm_abc123",
    "period": { "from": null, "to": null },
    "totalEvents": 17,
    "metrics": {
      "views": 7,
      "uniqueVisitors": 7,
      "engaged": 6,
      "submissions": 4,
      "engagementRate": 86,
      "completionRate": 57,
      "bounceRate": 14
    },
    "breakdown": {
      "byBrowser": { "Chrome": 17 },
      "byCountry": { "US": 10, "DE": 7 },
      "byDevice": { "desktop": 14, "mobile": 3 },
      "bySource": { "Direct": 12, "Google": 5 }
    }
  }
}
```

  

{/* ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ */}

<h2 id="workspaces" class="border-t border-border pt-8">
  Espaces de travail
</h2>

{/* ── workspaces.list ─────────────────────────────────────────────────────── */}

Lister les espaces de travail que votre token peut atteindre. Aucun paramètre.

Un token API est lié à un seul espace de travail, donc ceci renvoie exactement celui-là — même quand votre compte appartient à plusieurs.

  
    
```
{
  "ok": true,
  "data": {
    "items": [
      {
        "id": "ws_abc123",
        "name": "Acme Inc",
        "createdAt": 1714041800000,
        "role": "owner"
      }
    ],
    "nextCursor": null,
    "hasMore": false,
    "canPaginate": false
  }
}
```

  

{/* ── workspaces.createInvite ─────────────────────────────────────────────── */}

Créer un lien d’invitation pour un espace de travail.

  
    Identifiant de l’espace de travail.
  
  
    Expiration sous forme d’horodatage Unix futur en millisecondes.
  
  
    Nombre maximum d’utilisations de l’invitation.
  

  
    
```
{
  "ok": true,
  "data": {
    "id": "inv_abc123",
    "workspaceId": "ws_abc123",
    "code": "aBcDeFgH",
    "expiresAt": null,
    "maxUses": null,
    "uses": 0,
    "createdAt": 1714041851000
  }
}
```

  

{/* ── workspaces.getInvite ────────────────────────────────────────────────── */}

Obtenir une invitation à un espace de travail. Renvoie la même forme que `workspaces.createInvite`.

  
    Identifiant de l’invitation.
  

{/* ── workspaces.updateInvite ─────────────────────────────────────────────── */}

Mettre à jour une invitation existante à un espace de travail. Fournissez au moins l’un des champs <code>expiresAt</code> ou{' '}

<code>maxUses</code>, sinon l’appel est rejeté. Renvoie l’invitation mise à jour.

  
    Identifiant de l’invitation.
  
  
    Nouvel horodatage d’expiration en millisecondes.
  
  
    Nouvelle limite d’utilisations.
  

{/* ── workspaces.revokeInvite ─────────────────────────────────────────────── */}

Révoquer définitivement une invitation à un espace de travail.

  
    Identifiant de l’invitation.
  

  
    
```
{
  "ok": true,
  "data": {
    "inviteId": "inv_abc123",
    "revoked": true
  }
}
```

  

{/* ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ */}

<h2 id="folders" class="border-t border-border pt-8">
  Dossiers
</h2>

{/* ── folders.list ────────────────────────────────────────────────────────── */}

Lister les dossiers d’un espace de travail.

  
    Identifiant de l’espace de travail.
  
  
    Taille de la page (1–100).
  
  
    Curseur de pagination.
  

  
    
```
{
  "ok": true,
  "data": {
    "items": [
      {
        "id": "fld_abc123",
        "name": "Customer Feedback",
        "workspaceId": "ws_abc123",
        "parentId": null,
        "createdAt": 1714041800000
      }
    ],
    "nextCursor": null,
    "hasMore": false,
    "canPaginate": false
  }
}
```

  

{/* ── folders.create ──────────────────────────────────────────────────────── */}

Créer un dossier dans un espace de travail. Idempotent — retourne le dossier existant si un dossier portant le même nom existe déjà.

  
    Identifiant de l’espace de travail.
  
  
    Nom du dossier (1–255 caractères).
  
  
    Identifiant du dossier parent pour l’imbrication. Omettez pour la racine.
  

  
    
```
{
  "ok": true,
  "data": {
    "id": "fld_new123",
    "name": "Customer Feedback",
    "workspaceId": "ws_abc123",
    "parentId": null,
    "createdAt": 1714041851000,
    "alreadyExisted": false
  }
}
```

  

{/* ── folders.update ──────────────────────────────────────────────────────── */}

Renommer un dossier ou le déplacer dans un autre parent.

  
    Identifiant du dossier.
  
  
    Nouveau nom du dossier (1–255 caractères).
  
  
    Nouveau dossier parent. Passez <code>null</code> pour déplacer à la racine.
  

{/* ── folders.delete ──────────────────────────────────────────────────────── */}

Supprimer définitivement un dossier et tout son contenu (sous-dossiers et formulaires).

  
    Identifiant du dossier.
  

> ⚠️ **Opération destructive**
> <p>Cette action supprime définitivement tous les sous-dossiers et formulaires à l’intérieur du dossier. Elle est irréversible.</p>

  
    
```
{
  "ok": true,
  "data": {
    "folderId": "fld_abc123",
    "deletedFolderIds": ["fld_abc123"],
    "deletedFormIds": ["frm_in_folder"],
    "message": "Folder and 1 form deleted."
  }
}
```

  

<h2 id="translations" class="border-t border-border pt-8">
  Traductions
</h2>

{/* ── translations.listLanguages ──────────────────────────────────────────── */}

Lister toutes les langues configurées sur un formulaire.

  
    Identifiant du formulaire.
  

  
    
```
{
  "ok": true,
  "data": {
    "formId": "frm_abc123",
    "items": [
      {
        "language": "es",
        "completion": { "total": 15, "current": 12, "outdated": 2, "missing": 1, "suggested": 0 },
        "lastUpdatedAt": 1714041851000
      }
    ],
    "nextCursor": null,
    "hasMore": false,
    "canPaginate": false
  }
}
```

  

{/* ── translations.addLanguage ────────────────────────────────────────────── */}

Enregistrer une langue sur un formulaire. Toute autre méthode de traduction échoue avec `404 NOT_FOUND` tant que ce n’est pas fait.

  
    Identifiant du formulaire.
  
  
    Balise de langue BCP-47 (ex. <code>"es"</code>, <code>"pt-BR"</code>).
  

  
    
```
{
  "ok": true,
  "data": {
    "formId": "frm_abc123",
    "language": "es",
    "rowId": "tl_new123"
  }
}
```

  

{/* ── translations.removeLanguage ─────────────────────────────────────────── */}

Supprimer une langue et toutes ses traductions d’un formulaire.

  
    Identifiant du formulaire.
  
  
    Balise de langue BCP-47.
  

{/* ── translations.listEntries ────────────────────────────────────────────── */}

Lister chaque clé source pour une langue sur un formulaire, avec son état actuel. C’est ainsi que vous découvrez les valeurs de `key` que
prend `translations.setEntry`.

  
    Identifiant du formulaire.
  
  
    Balise de langue BCP-47. Doit déjà être sur le formulaire.
  

  
    
```
{
  "ok": true,
  "data": {
    "formId": "frm_abc123",
    "language": "es",
    "completion": { "total": 15, "current": 12, "outdated": 2, "missing": 1, "suggested": 0 },
    "items": [
      {
        "key": "block_q_1.title",
        "status": "current",
        "value": "[{\"text\":\"Tu correo electrónico\"}]",
        "sourceFragment": "[{\"text\":\"Your email\"}]",
        "updatedAt": 1714041851000,
        "block": { "id": "q_1", "type": "email-input", "label": "Your email" }
      }
    ],
    "hasMore": false
  }
}
```

  

<p>
  <code>status</code> vaut <code>missing</code> (rien de stocké), <code>outdated</code> (la source a changé depuis), <code>current</code>,
  ou <code>suggested</code> (une suggestion IA en attente, non acceptée). Les clés couvrent le contenu du formulaire (
  <code>block_&lt;id&gt;.*</code>) et, une fois qu’un auteur les a personnalisés, les e-mails de confirmation et de rappel au répondant (
  <code>email.confirmation.*</code>, <code>email.reminder.*</code>).
</p>

{/* ── translations.setEntry ───────────────────────────────────────────────── */}

Définir une entrée de traduction. La langue doit avoir été ajoutée via <code>translations.addLanguage</code> au préalable.

  
    Identifiant du formulaire.
  
  
    Balise de langue BCP-47.
  
  
    Une clé issue de <code>translations.listEntries</code>. N’en construisez pas une à la main.
  
  
    Le fragment traduit, sérialisé en JSON. Sa structure de marques doit correspondre au fragment source.
  

  
```
curl -X POST https://api.formstep.io/api/v1 \
  -H "Authorization: Bearer fb_..." \
  -H "Content-Type: application/json" \
  -d '{
    "method": "translations.setEntry",
    "params": {
      "formId": "frm_abc123",
      "language": "es",
      "key": "block_q_1.title",
      "value": "[{\"text\":\"Tu correo electrónico\"}]"
    }
  }'
```

> ⚠️ **Les écritures ici sont immédiates**
> <p>
>     L’API n’a pas d’étape brouillon-puis-publication : un <code>setEntry</code> ou <code>deleteEntry</code> atteint les répondants
>     immédiatement. Le tableau de bord et les outils de traduction MCP utilisent un brouillon à la place.
>   </p>

<p>
  Renvoie <code>{`{ formId, language, key }`}</code>.
</p>

{/* ── translations.deleteEntry ────────────────────────────────────────────── */}

Supprimer une entrée de traduction, ce qui fait revenir cette clé à la langue par défaut du formulaire. Idempotent. Quand la dernière
entrée d’une langue disparaît, la langue quitte les langues publiées du formulaire.

  
    Identifiant du formulaire.
  
  
    Balise de langue BCP-47.
  
  
    Clé de traduction à supprimer.
  

{/* ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ */}

<h2 id="me" class="border-t border-border pt-8">
  Compte
</h2>

{/* ── me.get ──────────────────────────────────────────────────────────────── */}

Obtenir des informations sur l’utilisateur authentifié.

  
    
```
{
  "ok": true,
  "data": {
    "id": "usr_abc123",
    "email": "you@example.com",
    "name": "Jane Doe"
  }
}
```

  

{/* ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ */}

<h2 id="meta" class="border-t border-border pt-8">
  Méta
</h2>

Lister chaque nom de méthode que ce déploiement sert, trié. La réponse qui fait foi quand cette page et le serveur ne sont pas d’accord.

  
    
```
{
  "ok": true,
  "data": {
    "methods": [
      "methods.list",
      "analytics.get",
      "folders.create",
      "folders.delete",
      "folders.list",
      "folders.update",
      "formSettings.get",
      "formSettings.update",
      "forms.create",
      "forms.delete",
      "forms.get",
      "forms.list",
      "..."
    ]
  }
}
```

  

{/* ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ */}

<h2 id="error-reference" class="border-t border-border pt-8">
  Référence des erreurs
</h2>

Chaque réponse d’erreur a la même structure. L’ensemble des `code` de premier niveau est fermé exprès : un nouveau mode de défaillance
n’ajoute jamais un code, il ajoute une `reason`. Branchez-vous sur `code` pour le résultat au niveau HTTP et sur `details.reason` pour la
correction.

```
{
  "ok": false,
  "error": {
    "code": "VALIDATION_ERROR",
    "message": "No field with key \"company\" on this form.",
    "details": { "reason": "UNKNOWN_FIELD_KEY", "field": "prefill.company", "validKeys": ["company_name", "contacts"] }
  }
}
```

<p>
  <code>details</code> est présent chaque fois que le serveur peut nommer la cause. Outre <code>reason</code>, il peut porter{' '}
  <code>field</code> (le paramètre en cause, pointé pour l’imbrication), <code>validKeys</code>, <code>validValues</code> (les{' '}
  <strong>valeurs</strong> d’option qu’une question à choix accepte), <code>expectedType</code>, <code>feature</code> (sur{' '}
  <code>UPGRADE_REQUIRED</code>), et <code>retryAfterMs</code> (sur un appel throttlé). Les raisons propres à la surface d’une requête sont
  listées avec chaque méthode ci-dessus.
</p>

<p>Voici tous les codes :</p>

  
    Paramètres invalides ou manquants dans la requête.
  
  
    Token API absent ou invalide.
  
  
    Le token n’a pas accès à la ressource demandée.
  
  
    La ressource n’existe pas.
  
  
    Nom de méthode inconnu. Utilisez <code>methods.list</code> pour voir les méthodes disponibles.
  
  
    La ressource n’est pas dans un état qui permet cet appel — une demande qui n’est plus en attente, une clé d’idempotence réutilisée avec
    un corps différent.
  
  
    Plus de 120 appels par minute sur ce token, plus de 60 appels à <code>requests.create</code> par minute, ou trop d’échecs
    d’authentification depuis cette IP.
  
  
    La fonctionnalité nécessite un niveau d’abonnement supérieur, l’espace de travail a épuisé son allocation mensuelle (raison{' '}
    <code>MONTHLY_ALLOWANCE_REACHED</code>), ou un compte Free a épuisé ses 10 invitations gratuites (raison{' '}
    <code>FREE_INVITATIONS_USED</code>).
  
  
    Erreur serveur inattendue. Réessayez plus tard.
  

<h2 id="next-steps">Étapes suivantes</h2>
<div class="not-prose grid gap-3 sm:grid-cols-2">
  - [Tokens API](/fr/developers/api-tokens) — Créer et gérer les tokens
  - [Serveur MCP](/fr/developers/mcp-server) — Utiliser Formstep depuis des agents IA
  - [Référence des webhooks](/fr/developers/webhooks-reference) — Schéma de charge utile et signature
</div>
