# Een aanvraag maken

Ontdek de veldsleutels van een formulier en maak vervolgens een aanvraag met vooraf ingevulde waarden, vergrendelde velden en context.

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

<h2 id="start-in-the-share-sheet">Begin in het deelvenster</h2>

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

<ul>
  <li>
    Het <strong>formulier-id</strong>, met een kopieerknop.
  </li>
  <li>
    Een <strong>curl</strong>-fragment en een <strong>MCP</strong>-prompt, allebei opgebouwd uit de echte veldsleutels van je formulier —
    zodat het voorbeeld al gericht is op de velden die dit formulier daadwerkelijk heeft.
  </li>
  <li>
    Een tabblad <strong>Handmatig</strong> dat met de hand één aanvraag aanmaakt, en <strong>Probeer het zelf</strong>, dat omzet wat je
    daar hebt ingevuld naar een aanvraag in <a href="#test-mode">testmodus</a> en je de link ervan geeft.
  </li>
  <li>
    Een link naar de <a href="/nl/requests/managing-requests">pagina Aanvragen</a>, gefilterd op dit formulier.
  </li>
</ul>

> ⚠️ **Publiceer eerst**
> <p>
>     Een niet-gepubliceerd formulier kan niet worden aangevraagd, en de fragmenten blijven uitgeschakeld totdat je publiceert. Veldsleutels
>     worden bevroren bij de eerste publicatie — dat is wat je automatisering in staat stelt <code>company_name</code> een jaar later nog
>     steeds aan te spreken. Zie <a href="/nl/requests/field-keys">Veldsleutels</a>.
>   </p>

<h2 id="discover-fields">Stap 1 — De velden ontdekken</h2>

<p>
  <code>fields.list</code> 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.
</p>

```
curl -X POST https://api.formstep.io/api/v1 \
  -H "Authorization: Bearer $FORMSTEP_TOKEN" \
  -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": "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
  }
}
```

<ul>
  <li>
    <code>context: true</code> markeert een verborgen veld. De waarde ervan komt in <code>context</code>, nooit in <code>prefill</code>; de
    sleutel van een verborgen veld in <code>prefill</code> wordt geweigerd met <code>UNKNOWN_FIELD_KEY</code>.
  </li>
  <li>
    <code>calculated: true</code> markeert een berekend veld. Het formulier berekent de waarde zelf, dus niemand kan er een versturen; je
    leest hem terug onder zijn sleutel in <code>answers</code>.
  </li>
  <li>
    <code>prefillable: false</code> 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{' '}
    <code>prefillable: false</code>: verborgen velden nemen <code>context</code> aan, berekende velden nemen niets aan.
  </li>
  <li>
    <code>options</code> toont de keuzes voor een keuzevraag. Stuur de <strong>sleutel</strong> 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 <code>rows</code> en <code>columns</code> op
    dezelfde manier.
  </li>
  <li>
    Herhaalgroepen komen terug als één item met <code>type: "group"</code>, <code>repeating: true</code>, en een lijst van{' '}
    <code>members</code>.
  </li>
</ul>

<h2 id="create">Stap 2 — De aanvraag aanmaken</h2>

```
curl -X POST https://api.formstep.io/api/v1 \
  -H "Authorization: Bearer $FORMSTEP_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"}}'
```

```
{
  "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
  }
}
```

<p>
  <code>deliveryStatus</code> is <code>"queued"</code> wanneer Formstep de uitnodiging mailt en <code>"not_requested"</code> wanneer je de
  link zelf aflevert.
</p>

<h2 id="three-buckets">Vooraf invullen, vergrendelde velden en context</h2>

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

<h3 id="prefill">Vooraf invullen</h3>

<p>
  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.
</p>

<h3 id="locked-fields">Vergrendelde velden</h3>

<p>
  Zet een vooraf ingevulde sleutel in <code>readonly</code> 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.
</p>

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

<h3 id="context">Context</h3>

<p>
  Vertrouwde waarden voor de <a href="/nl/building-forms/hidden-fields">verborgen velden</a> 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.
</p>

<p>
  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 <code>UNKNOWN_FIELD_KEY</code>. Administratie zonder verborgen veld, zoals een uitvoerings-id, hoort in{' '}
  <a href="#metadata">metadata</a>.
</p>

<p>
  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 <a href="/nl/building-forms/answer-piping">vermelding</a> in de forminhoud of e-mailtekst,
  of een zichtbare vraag die dat verborgen veld als <a href="/nl/building-forms/field-configuration#default-values">standaardwaarde</a>{' '}
  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 <code>prefill</code> voor de eigen sleutel van die vraag heeft voorrang boven de
  standaardwaarde.
</p>

<h3 id="metadata">Metadata</h3>

<p>
  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.
</p>

<h2 id="value-shapes">Waardevormen</h2>

<p>
  Stuur waarden in de vorm die het <code>type</code> uit <code>fields.list</code> 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.
</p>

<h2 id="documents">Documenten</h2>

<p>
  Een <a href="/nl/building-forms/documents-block">Documentenblok</a> 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.
</p>

```
"documents": [
  { "documentId": "kn7...", "name": "Your lease contract" },
  { "documentId": "kn8..." }
]
```

<p>
  <code>name</code> overschrijft de weergavenaam die bij de upload is opgeslagen. Heeft het formulier meer dan één documentenblok, dan wijs
  je het doel aan met <code>field</code>, de veldsleutel van het blok (<code>fields.list</code> 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.
</p>

<p>
  Limieten: alleen PDF en afbeeldingen, 25 MB per document, 100 MB per aanvraag (<code>DOCUMENTS_TOO_LARGE</code>), en ten hoogste 20
  documenten per blok, de vaste documenten meegeteld (<code>DOCUMENTS_TOO_MANY</code>). 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.
</p>

<h2 id="options">De rest van de opties</h2>

<h3 id="test-mode">Testmodus</h3>

<p>
  Geef <code>test: true</code> 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 <a href="/nl/requests/callbacks">callback</a> vuurt zoals gewoonlijk, en{' '}
  <code>requests.get</code> geeft de antwoorden terug. Wat hij nooit doet, is iemand of iets bereiken dat je achteraf zou moeten opruimen:
</p>

<ul>
  <li>
    Er wordt geen uitnodiging en geen herinnering verstuurd, wat <code>delivery</code> ook zegt. <strong>Herinnering versturen</strong>{' '}
    wordt er geweigerd, en hij verbruikt niets van je maandelijkse quotum.
  </li>
  <li>
    De callback draagt <code>"test": true</code>, zodat je workflow kan vertakken of het event kan negeren.
  </li>
  <li>
    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.
  </li>
  <li>
    De aanvraag is verborgen op de <a href="/nl/requests/managing-requests">pagina Aanvragen</a> achter{' '}
    <strong>Testaanvragen weergeven</strong>, weggelaten uit de aanvraagfunnel in Analytics, en weggelaten uit <code>requests.list</code>{' '}
    tenzij je <code>includeTest: true</code> opgeeft.
  </li>
  <li>
    De link sluit binnen 24 uur, ook als <code>expiresAt</code> om langer vraagt; de <code>expiresAt</code> in het antwoord zegt wanneer. Op
    Free mag een workspace 10 testaanvragen per dag aanmaken. De volgende mislukt met <code>RATE_LIMITED</code> en reden{' '}
    <code>TEST_REQUEST_LIMIT_REACHED</code>, en <code>retryAfterMs</code> zegt wanneer je het opnieuw kunt proberen. Pro en Business hebben
    geen dagelijkse limiet.
  </li>
</ul>

<p>
  <strong>Probeer het zelf</strong> 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.
</p>

<h3 id="allowance">Wat een aanvraag kost</h3>

<p>
  Elk abonnement heeft één <strong>maandelijks quotum</strong> 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 <code>requests.create</code> met{' '}
  <code>UPGRADE_REQUIRED</code> en reden <code>MONTHLY_ALLOWANCE_REACHED</code>; aanvragen die je al hebt aangemaakt blijven beantwoordbaar.
</p>

<p>
  Op Free verbruikt een aanvraag die met <code>"delivery": "email"</code> wordt aangemaakt ook een van de{' '}
  <a href="/nl/subscription-billing/limits-quotas#free-invitations">10 gratis uitnodigingen</a> van het account. Ze worden nooit gereset;
  zijn ze op, dan mislukt e-mailbezorging met <code>UPGRADE_REQUIRED</code> en reden <code>FREE_INVITATIONS_USED</code>.
</p>

<h3 id="idempotency">Idempotentie</h3>

<p>
  Geef dezelfde <code>idempotencyKey</code> op met dezelfde inhoud en je krijgt de oorspronkelijke aanvraag terug, met{' '}
  <code>deduplicated: true</code> en de oorspronkelijke link — geen tweede aanvraag, geen tweede e-mail. Hergebruik de sleutel met een{' '}
  <em>andere</em> inhoud en Formstep weigert met <code>IDEMPOTENCY_CONFLICT</code> 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.
</p>

<p>
  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.
</p>

<h3 id="rate-limit">Snelheidslimiet</h3>

<p>
  <code>requests.create</code> en <code>documents.create</code> delen een budget van <strong>60 aanroepen per minuut</strong>, 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.
</p>

<h3 id="custom-domains">Aangepaste domeinen</h3>

<p>
  Als het formulier al is gepubliceerd op een van je <a href="/nl/branding-domains/custom-domains">aangepaste domeinen</a>, worden
  aanvraaglinks daar automatisch gegenereerd — <code>https://forms.jouwbedrijf.com/r/rq_…</code>. Geef <code>domainId</code> expliciet op
  wanneer het formulier op meer dan één domein is gepubliceerd. Het domein moet tot dezelfde werkruimte behoren als het formulier.
</p>

<h2 id="what-the-recipient-sees">Wat de ontvanger ziet</h2>

<p>
  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.
</p>

<p>
  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 <a href="/nl/building-forms/answer-piping">noemen</a>.
</p>

<p>
  Een AI-agent doorloopt dezelfde twee stappen als <code>fields_list</code> en <code>request_create</code>, met dezelfde opties — documenten
  en <code>domainId</code> inbegrepen. Zie <a href="/nl/developers/mcp-server#requests">Aanvragen op de MCP-server</a>.
</p>

<div class="not-prose grid gap-3 sm:grid-cols-2">
  - [Veldsleutels](/nl/requests/field-keys) — Waar die sleutels vandaan komen en hoe je ze stabiel houdt.
  - [Callbacks & ondertekening](/nl/requests/callbacks) — Wat er binnenkomt zodra de ontvanger klaar is.
  - [Problemen oplossen](/nl/requests/troubleshooting) — Elke afwijzingsreden en wat je eraan kunt doen.
  - [API-referentie](/nl/developers/rest-api) — Volledige parameterlijst voor elke aanvraagmethode.
</div>
