Formstepdocs
Naar de appApp

Aanvragen

Een formulier en een aanvraag in één aanroep maken

Een agent zit met een klant aan de telefoon en hoort dat ze nog twee loonstroken moet uploaden, 40 EUR kosten moet betalen en een gesprek moet inplannen. De agent stuurt het formulier en de aanvraag in dezelfde aanroep, en Formstep neemt het vanaf daar over.


Wanneer je het gebruikt

Een aanvraag verwijst normaal naar een formulier dat iemand in de editor heeft gebouwd en gepubliceerd. Hangen de vragen van de zaak af, dan stuurt de aanroeper ze mee: requests.create neemt form in plaats van formId. Formstep bouwt van de blokken een gewoon formulier, publiceert het en maakt de aanvraag in één stap, dus ze bestaan allebei of geen van beide. Dat noemen we een inline formulier.

Blijf formId gebruiken voor een formulier dat je in dezelfde vorm naar veel mensen stuurt en met de hand in de editor wilt vormgeven. Gebruik form wanneer een automatisering bepaalt wat er gevraagd wordt.

De aanroep

requests.create met een formulier
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"}}'

Al het andere aan de aanvraag houdt zijn betekenis: recipient, prefill, context, readonly, delivery, herinneringen, verloop, callbackUrl, test en idempotencyKey worden tegen het nieuwe formulier gecontroleerd, precies zoals tegen een formulier dat met de hand is gebouwd. De respons bevat ook het formulier, met de sleutels waaronder de antwoorden terugkomen:

Respons, ingekort
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" }
    ]
  }
}

Om een keuzevraag in dezelfde aanroep vooraf in te vullen, stuur je optiesleutels, zoals bij elk formulier. De sleutel van een optie wordt afgeleid van het label, dus Self-employed wordt self_employed. Een label in plaats van een sleutel faalt met INVALID_PREFILL_VALUE, met de geldige sleutels in de foutmelding.

Het formulierobject

VeldWat het doet
nameVerplicht, 1 tot 100 tekens. De naam op het dashboard en in het browsertabblad, voor elke ontvanger dezelfde. Personaliseer via prefill en context, nooit via de naam.
blocksVerplicht, 1 tot 200 blokken, in de volgorde waarin het formulier ze toont. De blokken in de kolommen van een rij en de velden van een herhalende groep tellen ook mee.
languageDe interfacetaal van het formulier. Standaard: Engels.
logo, cover, emojiJe huisstijl, meegestuurd met elke aanvraag. logo: { "type": "icon", "name": "HeartIcon" } of { "type": "image", "url": "https://…" }. cover: { "type": "color", "color": "#0f4c81" } of { "type": "image", "url": "https://…", "offsetY": 50 }. emoji: één emoji naast de naam. Zie hieronder.
themeEen uiterlijk per kleurmodus: { "light": "solarized-light", "dark": "dracula" }. Elke modus neemt een preset-id, gratis op elk abonnement, of een volledig thema-object zoals de MCP-tool formTheme_set dat neemt, waarvoor het Pro-abonnement nodig is.
settingsHet formuliergedrag dat de instellingenpagina regelt: huisstijl, een geverifieerd e-maildomein (emailDomainId), e-mails aan de eigenaar en de invuller met hun tekst, het herinneringsschema, de uitnodigingsmail, de doorverwijzing en de bewaartermijn. Velden per sleutel: respondentNotificationTo is de sleutel van een e-mailvraag, redirectQueryParams koppelt een parameter aan een veldsleutel. E-mailtekst is platte tekst met {{key}}-plaatshouders. Geen wachtwoord- of inlogdrempel en geen deellinkinstelling zoals botbeveiliging of bewerken na verzenden: een aanvraag heeft een eigen link. Elke andere sleutel wordt geweigerd.
translationsHet formulier in meer talen, met een taalkiezer voor de ontvanger: { "de": { "Your name": "Ihr Name" } }, elke tekst van het formulier met als sleutel de tekst zoals de blokken hem schreven. Zie hieronder.

Blokken

Een blok neemt de parameters van de bijbehorende editor_insert*-tool op de MCP-server , zonder formId en after, plus een optionele key. Zonder sleutel wordt die uit de titel afgeleid, dus “Full name” wordt full_name. Een parameter die het blok niet kent, wordt geweigerd in plaats van genegeerd.

SoortTypen
Vragentext, contact, number, date, time, radio, checkbox, select, switch, rating, linearScale, matrix, ranking, pictureChoice, file, signature, decision
Documentendocuments, om bestanden te tonen die de ontvanger leest of ondertekent
Betalingen en afsprakenpayment, scheduleAppointment
Inhoudheader, paragraph, image (src-URL), embedded, table (cells vult hem), list, row (columns: de blokken van elke kolom, 2 tot 6 kolommen, en widths in procenten). Met een optionele key kan logica het blok tonen of verbergen.
StructuurpageDivider, repeatingGroup (fields: de vragen van de groep als blokken, elk met een eigen key), hiddenField (key verplicht: dat is de contextsleutel), calculatedField (de name is de sleutel), logic

Tekst in header en paragraph, lijstitems en tabelcellen begrijpen bold, italic, struck, <u>underlined</u>, code, $$equation$$, text, <span color=“red” background=“yellow” font=“font-georgia” size=“1.25”>…</span> met de kleuren, lettertypen en groottes van de werkbalk van de editor, en {{key}}, dat de waarde toont van het veld met die sleutel. Markeringen kunnen in elkaar zitten; een markering zonder tegenhanger blijft tekst. Een kolom van een rij bevat elk blok behalve pageDivider, logic, hiddenField, calculatedField, repeatingGroup, row en scheduleAppointment. Een decision-blok neemt zijn title en labels voor goedkeuren, afwijzen en wijzigingen in de taal van het formulier.

Blokken verwijzen naar elkaar via de sleutel, nooit via een element-id: een logic-blok neemt zijn rule zoals editor_insertLogic dat doet, met veldsleutels waar die ids verwacht, optielabels als voorwaardewaarden en paginanamen voor setNextPage.

Betalingen

Een payment-blok neemt amount en currency, of amountFromField: de sleutel van een verborgen veld of een getalvraag. Met een verborgen veld geeft elke aanvraag haar eigen bedrag mee in context, dus één formulier rekent bij elke klant een eigen bedrag af:

Eén formulier, een bedrag per aanvraag
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 }
}

Een getalvraag die het bedrag bepaalt, moet verplicht zijn en een min boven 0 hebben, zodat niemand met een leeg, nul- of negatief bedrag bij de checkout komt.

Het formulier rekent af via het standaard Stripe-account van de werkruimte, of via het account dat connection noemt. Een persoon verbindt Stripe één keer voor de werkruimte; een automatisering kan dat niet. Zonder bruikbaar account mislukt de aanroep en krijg je te horen waar je het verbindt.

Afspraken

Een scheduleAppointment-blok neemt eventType, de titel of het id van een eventtype op de Cal.com-verbinding van de werkruimte, en connection als de werkruimte er meerdere heeft. Formstep zoekt het eventtype op wanneer de aanvraag wordt gemaakt. prefill: { name, email } noemt de vragen waarvan de antwoorden de afspraak invullen.

Je huisstijl

Een automatisering kan je uitstraling niet verzinnen, dus geef je die één keer mee en stuurt ze die met elke aanvraag mee: logo, cover en emoji naast de blokken, en theme voor de kleuren en lettertypen.

VeldNeemt
logoEen icoon uit de builder, { "type": "icon", "name": "HeartIcon" }, of je eigen afbeelding, { "type": "image", "url": "https://…/logo.png" }.
coverEen effen kleur, { "type": "color", "color": "#0f4c81" }, of een afbeelding, { "type": "image", "url": "https://…/cover.jpg", "offsetY": 50 }, waarbij offsetY van 0 tot 100 kiest welk deel van een hoge afbeelding zichtbaar is.
emojiEén emoji, naast de formuliernaam getoond.

Elke afbeelding, ook de src van een afbeeldingsblok, moet een openbare JPEG, PNG of WebP zijn. Formstep haalt elke afbeelding één keer op wanneer het formulier wordt gemaakt en bewaart een eigen kopie, zodat het formulier en de pdf’s van de inzendingen de bron overleven. Een URL die niet op te halen is, laat de aanroep mislukken met INLINE_FORM_IMAGE_UNREACHABLE, met het veld erbij. Zonder huisstijl ziet het formulier eruit als een nieuw formulier vanaf het dashboard.

Vertalingen

translations publiceert het formulier meteen in meer talen, en de ontvanger krijgt de taalkiezer die een vertaald formulier heeft: { "de": { "Your name": "Ihr Name", "Order for **{{customer}}**": "Bestellung für **{{customer}}**" } }. Elke tekst van het formulier moet erin staan, met als sleutel de tekst precies zoals de blokken hem schreven: vraagtitels, opties, plaatshouders, headers, alinea’s, lijstitems, tabelcellen, groepsnamen en knoplabels. Opmaak en {{key}} blijven zoals in de bron. Ontbreekt er een tekst, dan mislukt de aanroep met INLINE_FORM_TRANSLATION_MISSING, die de toe te voegen teksten opsomt. E-mailtekst wordt niet vertaald.

Eén definitie, één formulier

Een latere aanroep met hetzelfde form-object hergebruikt hetzelfde formulier, en de respons zegt “reused”: true. Honderd aanvragen met één definitie leveren één formulier op, met één set antwoorden om te exporteren. Verander je een blok, de naam, de huisstijl of een vertaling, dan ontstaat er een nieuw formulier; aanvragen die al verstuurd zijn, houden het formulier waarmee ze zijn gemaakt. Een formulier dat je sluit of depubliceert, wordt niet hergebruikt: de volgende aanroep met dezelfde definitie maakt een nieuw formulier.

Maakt een aanroep een nieuw formulier terwijl een open inline formulier dezelfde bloktypen in dezelfde volgorde heeft, dan noemt het antwoord dat formulier in form.similarForm. Meestal kwam dat door een gewijzigde naam of titel. Wil je alle antwoorden op één inline formulier lezen, vraag dan de inzendingen op in plaats van elke aanvraag te lezen.

Op het dashboard

Een inline formulier hoort bij zijn aanvraag en staat daarom op de pagina van de aanvraag in plaats van op de formulierenpagina. De pagina toont de antwoorden per veldsleutel, en Voorbeeld opent het formulier zoals de ontvanger het zag, ingevuld met hun antwoorden. Een link naar de editor of de inzendingen van een inline formulier opent in plaats daarvan de pagina Aanvragen, gefilterd op dat formulier. Agents zien inline formulieren nog steeds in form_list, en de API weigert ze te wijzigen.

Een inline formulier heeft geen deellink, en je kunt er geen toevoegen: de aanvragen zijn de enige weg naar binnen, en de callback bevat elk antwoord. Het heeft ook geen integraties.

Omdat het geen eigen pagina heeft, verwijder je een inline formulier nooit met de hand. Het verdwijnt met zijn laatste aanvraag: als je een aanvraag verwijdert, of de bewaartermijn van het formulier die opruimt, en geen andere aanvraag het formulier nog gebruikt, wordt het formulier ook verwijderd.

Wanneer de aanroep wordt geweigerd

Elke weigering is een VALIDATION_ERROR met een details.reason waarop je automatisering kan vertakken. Er wordt niets aangemaakt.

RedenWat je doet
FORM_SOURCE_MISSING / FORM_SOURCE_AMBIGUOUSStuur precies één van formId en form.
INLINE_FORM_INVALIDEen blok of een veld van form voldoet niet aan het schema. details.issues noemt elk ervan.
INLINE_FORM_DUPLICATE_KEYTwee blokken komen op dezelfde sleutel uit. Geef een van beide een eigen key.
INLINE_FORM_UNKNOWN_KEYEen logicaregel, amountFromField of een prefill van een afspraak noemt een sleutel die geen enkel blok heeft.
INLINE_FORM_AMOUNT_MISSINGGeef in context een positief getal mee voor het bedragveld.
INLINE_FORM_CONNECTION_MISSINGStripe of Cal.com is niet verbonden. details.connectUrl is de pagina die een persoon één keer opent om het te verbinden.
INLINE_FORM_EVENT_TYPE_NOT_FOUNDGeen enkel Cal.com-eventtype komt overeen. De fout noemt de eventtypen die er wel zijn.
INLINE_FORM_IMAGE_UNREACHABLEEen afbeeldings-URL is geen openbare JPEG, PNG of WebP. details.field noemt hem. Gebruik een URL die iedereen kan openen, of laat de afbeelding weg.
INLINE_FORM_TRANSLATION_MISSINGEen vertaling mist teksten die het formulier heeft. details.missing somt ze op, zoals ze in de blokken staan.
INLINE_FORM_PUBLISH_ISSUESPubliceren weigerde het formulier, bijvoorbeeld door een eventtype dat niet vanuit een formulier te boeken is.

Vanuit een agent

Op de MCP-server neemt request_create hetzelfde form. Voordat een agent een formulier met een betaling of een afspraak bouwt, leest die workspaceConnection_list: die toont de verbonden Stripe- en Cal.com-accounts en, als er een ontbreekt, de link die een persoon één keer moet openen. scheduler_listEventTypes somt de eventtypen op die een afspraak kan noemen, en paymentConnection_setDefault wijzigt het standaard Stripe-account. Via de REST API heten dezelfde drie connections.list, scheduler.listEventTypes en paymentConnections.setDefault.