formbasedocs
Naar de appApp

Aanvragen

Een aanvraag maken

Twee API-aanroepen: vraag het formulier wat je kunt opgeven, en wijs het dan toe aan één persoon met de waarden die je al kent.


Begin in het deelvenster

Het tabblad Aanvragen op zijn curl-tabblad, met het formulier-id en een kant-en-klare requests.create-aanroep
Het tabblad curl: het formulier-id en een aanroep die al is ingevuld met de veldsleutels van dit formulier.

Open je gepubliceerde formulier, klik op Delen, en kies het tabblad Aanvragen. De kaart daar geeft je alles wat je nodig hebt om de eerste aanroep te doen:

  • Het formulier-id, met een kopieerknop.

  • Een curl-fragment en een MCP-prompt, allebei opgebouwd uit de echte veldsleutels van je formulier — zodat het voorbeeld al gericht is op de velden die dit formulier daadwerkelijk heeft.

  • Een tabblad Handmatig dat met de hand één aanvraag aanmaakt, en Probeer het zelf, dat omzet wat je daar hebt ingevuld naar een aanvraag in testmodus en je de link ervan geeft.

  • Een link naar de pagina Aanvragen, gefilterd op dit formulier.

Stap 1 — De velden ontdekken

fields.list geeft elk veld terug van de huidige gepubliceerde versie van het formulier, met de sleutel om het aan te spreken, de waardevorm die het aanneemt, en tot welk vak het behoort.

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..."}}'
Respons
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 markeert een verborgen veld. De waarde ervan komt in context, nooit in prefill; de sleutel van een verborgen veld in prefill wordt geweigerd met UNKNOWN_FIELD_KEY.

  • calculated: true markeert een berekend veld. Het formulier berekent de waarde zelf, dus niemand kan er een versturen; je leest hem terug onder zijn sleutel in answers.

  • prefillable: false markeert een veld waarvoor niemand een waarde kan opgeven: bestandsupload, handtekening, betaling, afspraak boeken, en Documentenblokken. De ontvanger vult die vragen zelf in. Verborgen velden en berekende velden tonen ook prefillable: false: verborgen velden nemen context aan, berekende velden nemen niets aan.

  • options toont de keuzes voor een keuzevraag. Stuur de sleutel van de optie, niet het label; het label is er zodat je de keuze die je kent kunt koppelen aan de sleutel. Een matrix toont zijn rows en columns op dezelfde manier.

  • Herhaalgroepen komen terug als één item met type: “group”, repeating: true, en een lijst van members.

Stap 2 — De aanvraag aanmaken

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"}}'
Respons
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 is “queued” wanneer formbase de uitnodiging mailt en “not_requested” wanneer je de link zelf aflevert.

Vooraf invullen, vergrendelde velden en context

Drie verschillende dingen kunnen aan een aanvraag worden gekoppeld, en ze door elkaar halen is de meest voorkomende beginnersfout.

Komt inDe ontvanger…Komt terug in de callback
Vooraf invullenprefillZiet het en kan het wijzigenJa, als antwoord
Vergrendeld veldprefill + readonlyZiet het, kan het niet wijzigenJa, als antwoord
ContextcontextKan het niet wijzigen; ziet het alleen waar je het noemtJa, in het aanvraagblok en als antwoord
MetadatametadataZiet het nooit, het formulier ook nietJa, in het aanvraagblok

Vooraf invullen

Beginantwoorden voor de zichtbare vragen, zodat de ontvanger controleert en corrigeert in plaats van vanaf nul te typen. Alles wat je al van hen weet hoort hier thuis — de bedrijfsnaam uit je CRM, het bedrag van de factuur, de antwoorden van vorig jaar.

Vergrendelde velden

Zet een vooraf ingevulde sleutel in readonly en de ontvanger ziet de waarde maar kan hem niet wijzigen. Gebruik dit voor de feiten die ze bevestigen in plaats van aanleveren — het contractnummer, de afgesproken prijs. Vergrendelen is per aanvraag: het formulier zelf blijft onaangeroerd, en hetzelfde veld is bij de volgende aanvraag weer vrij bewerkbaar.

Elke vergrendelde sleutel moet ook vooraf ingevuld zijn, en een vergrendeld verplicht veld moet vooraf worden ingevuld met iets niet-leegs — anders zou de ontvanger een formulier krijgen dat ze nooit zouden kunnen indienen, en weigert formbase de aanroep in plaats van die val te creëren.

Context

Vertrouwde waarden voor de verborgen velden van het formulier — een dossiernummer, een workflow-run-id, een bedrag. Context voedt variabelen, voorwaardelijke logica, berekende velden en e-mailtekst, komt onveranderd terug in de callback, en de ontvanger kan het niet aanpassen. Dat laatste is het verschil met het vullen van een verborgen veld via een URL op een openbare link, waar iedereen de querystring kan bewerken; aanvraaglinks negeren URL-queryparameters volledig. Contextwaarden moeten een string, getal of boolean zijn.

Context is niet vrij invoerbaar: elke sleutel moet een verborgen veld zijn op de gepubliceerde versie van het formulier, en elke andere sleutel wordt geweigerd met UNKNOWN_FIELD_KEY. Administratie zonder verborgen veld, zoals een uitvoerings-id, hoort in metadata.

Verborgen velden worden niet op het formulier getoond, maar een contextwaarde is niet geheim voor de ontvanger. Hij ziet hem overal waar het formulier of de uitnodiging hem toont: een vermelding in de forminhoud of e-mailtekst, of een zichtbare vraag die dat verborgen veld als standaardwaarde gebruikt. In dat laatste geval ziet de ontvanger de contextwaarde vooraf ingevuld in die vraag en kan hij het antwoord bewerken. De contextwaarde zelf blijft ongewijzigd. Een prefill voor de eigen sleutel van die vraag heeft voorrang boven de standaardwaarde.

Metadata

Je eigen administratie — een uitvoerings-id, een CRM-record-id. Het bereikt het formulier nooit, dus het kan niet in tekst worden gepiped of door logica worden gelezen; het rijdt gewoon mee en komt terug in elke callback en statusuitlezing.

Waardevormen

Stuur waarden in de vorm die het type uit fields.list vraagt. Een verkeerde vorm komt terug als een validatiefout die de sleutel, het verwachte type, en — voor keuzevragen — de waarden noemt die geaccepteerd zouden zijn.

TypeStuur
text, email, phone, url, textareaEen string
number, rating, scaleEen getal
switchtrue of false
date"2026-03-04"
time"09:30" of "09:30:00"
radio, selectDe optiesleutel, niet het label
checkbox, ranking, picture-choiceEen array van optiesleutels
matrixEen object van rijsleutel naar kolomsleutel: { "row_key": "column_key" }
group (herhalend)Een array van instanties, maximaal 100: [{ "member_key": value }, …]
file, signature, payment, schedule-appointmentNiets — de ontvanger levert deze zelf aan
elk veld met calculated: trueNiets — het formulier berekent het zelf
documentsNiets in prefill — gebruik de optie Documenten hieronder

Documenten

Een Documentenblok geeft bestanden door aan de ontvanger. De aangeleverde bestanden zijn voor iedereen hetzelfde en blijven altijd staan; een aanvraag voegt bestanden toe voor die ene ontvanger, eronder — het eigen huurcontract van de klant, een kopie van een identiteitsbewijs om te controleren. Bytes reizen nooit mee in de API-aanroep zelf: upload eerst, verwijs daarna.

  1. 1

    Reserveer de upload

    Roep documents.create aan met formId, name (1–200 tekens), contentType (PDF of afbeelding), de exacte grootte in bytes, en optioneel een sha256 van het bestand (64 hex-tekens). Je krijgt een id en een uploadUrl terug die één uur geldig is.

  2. 2

    Upload de bytes

    PUT het bestand naar uploadUrl met dezelfde Content-Type. Er wordt nog niets geverifieerd.

  3. 3

    Verwijs ernaar op de aanvraag

    Geef documents: [{ documentId, name? }] mee aan requests.create. formbase controleert het geüploade object (grootte, bestandssignatuur, sha256 als je die hebt meegestuurd) voordat de aanvraag wordt aangemaakt, en de ontvanger ziet het bestand in het blok.

requests.create → documents
json
"documents": [
  { "documentId": "kn7...", "name": "Your lease contract" },
  { "documentId": "kn8..." }
]

name overschrijft de weergavenaam die bij de upload is opgeslagen. Heeft het formulier meer dan één documentenblok, dan wijs je het doel aan met field, de veldsleutel van het blok (fields.list toont die, samen met de vaste documenten die elke respondent al krijgt). De bestanden komen onder die vaste documenten te staan — een aanvraag voegt bestanden toe, hij vervangt er nooit een. Eén upload kan door zoveel aanvragen worden aangehaald als je wilt — een prijslijst die je één keer uploadt, bedient vijfhonderd aanvragen.

Limieten: alleen PDF en afbeeldingen, 25 MB per document, 100 MB per aanvraag (DOCUMENTS_TOO_LARGE), en ten hoogste 20 documenten per blok, de vaste documenten meegeteld (DOCUMENTS_TOO_MANY). De bestanden tellen mee voor je werkruimte-opslag en komen vrij zodra de aanvragen die ernaar verwijzen buiten het bewaarvenster van het formulier vallen. De inzending registreert de lijst die de ontvanger onder de veldsleutel van het blok te zien kreeg, dus de callback vertelt je precies welke bestanden deze persoon heeft gekregen.

De rest van de opties

OptieWat het doet
languageDe taal waarin het formulier opent en waarin de uitnodiging is geschreven; een van de gepubliceerde talen van het formulier. Weggelaten, dan wordt de standaardtaal van het formulier gebruikt. De ontvanger kan nog steeds van taal wisselen, net als bij een openbare link.
delivery"email" verstuurt de uitnodiging voor je en vereist een e-mailadres van de ontvanger en een Pro- of Business-abonnement, of een van de 10 gratis uitnodigingen van een Free-account; "none" (de standaard) betekent dat je de link zelf aflevert.
remindersOverschrijft het herinneringsschema van het formulier voor deze ene aanvraag met maximaal vijf idle-offsets zoals ["2d", "12h", "30m"], of geef een lege lijst op om herinneringen uit te zetten. Een aangepast schema vereist een e-mailadres van de ontvanger en een Pro- of Business-abonnement; zonder e-mailadres van de ontvanger draait het eigen schema van het formulier gewoon niet.
expiresAtWanneer de link stopt met werken, als Unix-tijdstempel in milliseconden. Standaard 30 dagen; 365 dagen is het maximum.
externalIdJe eigen id voor deze aanvraag. Je kunt er later op filteren.
idempotencyKeyZorgt dat een herhaalde run de bestaande aanvraag hergebruikt in plaats van een tweede aan te maken.
callbackUrlWaar formbase de callback naartoe post zodra de aanvraag eindigt. Alleen HTTPS.
domainIdGenereer de link op een van je aangepaste domeinen, in plaats van het domein waarop het formulier al gepubliceerd is.
testEen droogloop: er wordt niets gemaild, de callback meldt test, en de inzending telt nergens mee. Zie hieronder.

Testmodus

Geef test: true op om de hele bedrading uit te proberen vóór een echte run. Een testaanvraag is echt in alles wat telt voor de bedrading: de link opent en kan worden afgerond, de callback vuurt zoals gewoonlijk, en requests.get geeft de antwoorden terug. Wat hij nooit doet, is iemand of iets bereiken dat je achteraf zou moeten opruimen:

  • Er wordt geen uitnodiging en geen herinnering verstuurd, wat delivery ook zegt. Herinnering versturen wordt er geweigerd, en hij verbruikt niets van je maandelijkse quotum.

  • De callback draagt “test”: true, zodat je workflow kan vertakken of het event kan negeren.

  • De inzending wordt opgeslagen maar telt niet mee: niet tegen je maandelijkse quotum (een test wordt afgerond, ook als het quotum op is), en hij verschijnt nooit in de inzendingstelling van het formulier, het tabblad Inzendingen, exports, of je integraties. Niemand wordt op de hoogte gesteld.

  • De aanvraag is verborgen op de pagina Aanvragen achter Testaanvragen weergeven, weggelaten uit de aanvraagfunnel in Analytics, en weggelaten uit requests.list tenzij je includeTest: true opgeeft.

  • De link sluit binnen 24 uur, ook als expiresAt om langer vraagt; de expiresAt in het antwoord zegt wanneer. Op Free mag een workspace 10 testaanvragen per dag aanmaken. De volgende mislukt met RATE_LIMITED en reden TEST_REQUEST_LIMIT_REACHED, en retryAfterMs zegt wanneer je het opnieuw kunt proberen. Pro en Business hebben geen dagelijkse limiet.

Probeer het zelf in het deelvenster is deze modus met één klik: hij neemt het concept van het tabblad Handmatig over — vooraf invullen, vergrendelingen, context, callback, verloop — richt de aanvraag aan je eigen account, verstuurt geen e-mail, en geeft je de link om zelf te openen.

Wat een aanvraag kost

Elk abonnement heeft één maandelijks quotum dat door beide kanalen wordt gedeeld: een inzending via een deellink verbruikt één eenheid, en dat doet ook elke aanvraag die je aanmaakt — of de ontvanger nu antwoordt, hem negeert, of je hem annuleert. De inzending die een aanvraag oplevert is al betaald en telt nergens mee. Free bevat 1.000 eenheden per maand, Pro en Business 50.000; de telling wordt gereset op de 1e van elke maand, UTC. Bij het bereiken van het quotum mislukt requests.create met UPGRADE_REQUIRED en reden MONTHLY_ALLOWANCE_REACHED; aanvragen die je al hebt aangemaakt blijven beantwoordbaar.

Op Free verbruikt een aanvraag die met “delivery”: “email” wordt aangemaakt ook een van de 10 gratis uitnodigingen van het account. Ze worden nooit gereset; zijn ze op, dan mislukt e-mailbezorging met UPGRADE_REQUIRED en reden FREE_INVITATIONS_USED.

Idempotentie

Geef dezelfde idempotencyKey op met dezelfde inhoud en je krijgt de oorspronkelijke aanvraag terug, met deduplicated: true en de oorspronkelijke link — geen tweede aanvraag, geen tweede e-mail. Hergebruik de sleutel met een andere inhoud en formbase weigert met IDEMPOTENCY_CONFLICT in plaats van te gokken welke je bedoelde. Sleutels zijn gebonden aan de werkruimte en blijven 30 dagen geldig; daarna start dezelfde sleutel een nieuwe aanvraag.

In een workflowtool is de uitvoerings-id de natuurlijke sleutel: een run die opnieuw wordt geprobeerd na een netwerkstoring pakt de aanvraag op die al was aangemaakt.

Snelheidslimiet

requests.create en documents.create delen een budget van 60 aanroepen per minuut, geteld per API-token (of per gebruiker, voor een aanroep zonder token). Een achterstand die je wegwerkt, moet zichzelf temporiseren; een piek boven het budget wordt geweigerd en kan opnieuw worden geprobeerd.

Aangepaste domeinen

Als het formulier al is gepubliceerd op een van je aangepaste domeinen, worden aanvraaglinks daar automatisch gegenereerd — https://forms.jouwbedrijf.com/r/rq_…. Geef domainId expliciet op wanneer het formulier op meer dan één domein is gepubliceerd. Het domein moet tot dezelfde werkruimte behoren als het formulier.

Wat de ontvanger ziet

Precies het formulier dat jij hebt gemaakt — zelfde thema, zelfde logo, zelfde taal — met hun waarden ingevuld, vergrendelde velden alleen-lezen, en geen captcha om op te lossen. Wanneer ze indienen, krijgen ze jouw bedankpagina. Als ze later terugkomen op de link, krijgen ze de uitkomstpagina in plaats van een leeg formulier.

Er staat geen bericht van je automatisering op de pagina. Alles wat de ontvanger moet weten hoort in het formulier zelf, waar je het kunt personaliseren door een contextwaarde of een vooraf ingevuld veld te noemen.

Een AI-agent doorloopt dezelfde twee stappen als fields_list en request_create, met dezelfde opties — documenten en domainId inbegrepen. Zie Aanvragen op de MCP-server.