Ontwikkelaars
MCP-server
De Model Context Protocol (MCP)-server laat AI-agents je formbase-formulieren lezen en bewerken via uitgebreide, schema-gestuurde tools.
Wat het is
MCP is een open standaard waarmee AI-tools verbinding kunnen maken met externe diensten. formbase biedt een gehoste MCP-endpoint aan waarmee elke MCP-compatibele client verbinding kan maken, waaronder Claude Code, Claude desktop en Cursor.
Verbinding
URL: https://api.formbase.so/api/mcp (POST, streamable HTTP)
Auth: Bearer <token>Twee soorten bearer token werken:
API-token (
fb_…) — aangemaakt via API-tokens. Het beste voor persoonlijk gebruik en snelle configuratie.OAuth-toegangstoken (
fbo_…) — uitgegeven door de OAuth-flow. Het beste voor apps van derden die namens een gebruiker verbinding maken.
Beide zijn gebonden aan precies één werkruimte en bereiken dezelfde tools. Een tool-aanroep die een andere werkruimte noemt, of een
formulier daarin, faalt met FORBIDDEN. OAuth-tokens dragen ook scopes (mcp:read, mcp:write,
offline_access), maar vandaag wordt geen enkele tool erdoor beperkt — behandel elk token als volledige toegang binnen zijn
werkruimte.
Tool-aanroepen zijn beperkt tot 120 per minuut per token, gedeeld met de API: alleen
tools/call verbruikt het budget, terwijl initialize, tools/list, prompts/ en
gratis zijn. Bij overschrijding geeft de aanroep nog steeds HTTP 200 terug met een mislukt toolresultaat dat
resources/RATE_LIMITED en een retryAfterMs draagt — poll op een timer, nooit in een lus.
Waar je een token krijgt
Open OAuth en API-sleutels in de zijbalk van je workspace om API-tokens aan te maken en verbonden OAuth-apps te bekijken. Zie API-tokens voor een stapsgewijze handleiding.
Kerntools
Elke tool wordt geadverteerd via tools/list wanneer een client verbinding maakt. Clients die schema’s on demand laden, zoals
Claude Code, halen het volledige schema van een tool op zodra een taak hem nodig heeft. De onderstaande tabel toont de kerntools waarmee
de meeste taken beginnen; load_tools (catalogi) en load_skill (domeinhandleidingen) documenteren de rest.
| Tool | Wat het doet |
|---|---|
| form_list | Formulieren in een workspace weergeven. Ondersteunt mapfilter, fuzzy naamzoeken en cursorpaginering. |
| form_get | Volledige details van een formulier ophalen: vragen, omslagafbeelding, logo en een live voorbeeld-URL. |
| form_create | Een nieuw leeg formulier aanmaken in een workspace. Geeft een voorbeeld-URL terug voor live bewerking. |
| form_update | Formuliermetadata bijwerken: naam, map, emoji, omslagafbeelding of logo. |
| form_delete | Een formulier zacht verwijderen (verplaatst naar prullenbak, trekt deellinks in). |
| form_publish | Een formulier publiceren zodat het reacties kan ontvangen. Idempotent. |
| workspace_list | Alle workspaces weergeven die toegankelijk zijn via jouw token. |
| workspaceFolder_list | Mappen in een workspace weergeven. |
| formSubmission_list | Inzendingen voor een formulier weergeven met paginering. Bevat concepten op Pro en Business; Free toont alleen voltooide reacties. |
| fields_list | De veldsleutels weergeven die een aanvraag kan aanspreken op een gepubliceerd formulier, elk met het waardetype, de optiesleutels en een usage-regel. Roep dit aan vóór request_create. |
| request_create | Een gepubliceerd formulier toewijzen aan één met naam genoemde ontvanger: prefill, vergrendelde velden, context, bezorging, verval, en een optionele callback. |
| request_get | Eén aanvraag lezen: status, tijdlijn, en — eenmaal voltooid — antwoorden geordend per veldsleutel, plus display. |
| request_list | Aanvragen weergeven voor een formulier of een hele workspace, gefilterd op status, outcome, of je eigen external id. |
| editor_getDocument | De volledige documentstructuur van een formulier ophalen: alle elementen, hun typen en eigenschappen. |
| editor_updateElement | Een bestaand formulierelement bewerken: de tekst vervangen, eigenschappen wijzigen (titel, verplicht, enz.) of het element verplaatsen. |
| editor_deleteElement | Een element uit het formulier verwijderen. |
| editor_insertTextQuestion | Een korte of lange tekstvraag invoegen. Elk vraagtype heeft zijn eigen invoeg-tool met een nauwkeurig schema. |
| editor_insertContactQuestion | Een e-mail-, telefoonnummer- of website-URL-vraag invoegen. |
| editor_insertNumberQuestion · editor_insertDateQuestion | Een getal- of datumvraag invoegen. |
| editor_insertRadioQuestion · editor_insertCheckboxQuestion · editor_insertSelectQuestion | Enkelvoudige keuze (radio), meervoudige keuze (checkbox) of dropdown-vragen invoegen. |
| editor_insertDecisionQuestion | De beslissingsvraag invoegen: de keuze goedkeuren / afwijzen / wijzigen waarvan het antwoord een aanvraaguitkomst wordt. Een met de hand gebouwde radio-vraag levert er nooit een op. |
| editor_insertRatingQuestion · editor_insertLinearScaleQuestion | Een sterrenbeoordeling of lineale schaalvraag invoegen. |
| editor_insertHeader · editor_insertParagraph · editor_insertImage · editor_insertPageDivider | Niet-vraag-inhoud invoegen: koppen, alinea's, afbeeldingen en pagina-einden. |
| load_tools | Documentatie laden voor een toolcatalogus. Geeft schema's en gebruikspatronen terug voor gegroepeerde tools. |
| load_skill | Een domeinkennis-gids laden (thema's, vraagtypen, logica-regels, enz.). |
Meer invoegvarianten — tijd, bestandsupload, handtekening, betaling, matrix/raster, rangschikking, afbeeldingskeuze, schakelaar, tabel,
lijst, rij, berekend veld, verborgen veld, inline variabele, ingesloten inhoud (editor_insertEmbedded voor YouTube, Google
Maps of iframe-insluitingen) en een conditioneel-logicablok (editor_insertLogic) — staan ook op tools/list. Laad
load_skill(“question-types”) voor de volledige set, elk met zijn toolnaam en velden. Conditionele logica wordt geschreven met
editor_setLogic in de editor-actions catalogus.
Toolcatalogi
Deze tools staan ook op tools/list. Voer load_tools uit met een catalogusnaam voor verrijkte documentatie
(intro, volledige schema’s, gebruikspatronen, randgevallen) voor de gegroepeerde tools, en roep ze daarna direct aan.
| Catalogus | Inbegrepen tools |
|---|---|
| form-data | formAnalytics_get — geaggregeerde statistieken (weergaven, inzendingen, voltooiingspercentage, uitsplitsingen per apparaat, land, browser, bron). Vereist het Pro- of Business-plan; zonder dat mislukt de aanroep met een melding die het plan noemt. |
| form-appearance | formTheme_get, formTheme_set, form_update — thema's per modus (licht en donker), plus de omslagafbeelding en het logo via form_update. |
| form-behavior | formSettings_get, formSettings_update — meldingsemails, omleidingen bij voltooiing, wachtwoord, bewaring, taal, betaling. |
| form-sharing | formShareLink_list, formShareLink_create, formShareLink_update — CRUD voor deellinks met ondersteuning voor aangepaste domeinen. |
| form-translations | translationLanguage_list, translationDraft_get, translationDraft_update, translationDraft_publish, translationLanguage_delete — meertalige concept-dan-publiceer-workflow. Een leeg concept publiceren is de enige manier om een taal te depubliceren. |
| form-lifecycle | form_unpublish, form_restore — levenscyclusoperaties naast de kernacties publiceren en verwijderen. |
| workspace-management | workspaceFolder_create, workspaceFolder_update, workspaceFolder_delete — folder CRUD naast de kern-lijstwerkwoorden. |
| editor-actions | editor_formatText, editor_setLogic, editor_testLogic — tekstopmaak, conditionele-logica-schrijven en logicasimulatie. |
| request-lifecycle | fields_list, request_create, request_get, request_list, request_cancel, request_remind, request_replayCallback, document_create — het hele aanvraagoppervlak: trek een aanvraag in behandeling in, herinner de ontvanger, speel een callback af die nooit aankwam, reserveer een upload voor één aanvraag. |
| editor-inserts | De lange staart van editor_insert*-tools: tijd, schakelaar, bestand, handtekening, documentenblok, matrix, rangschikking, betaling, afspraak, afbeeldingskeuze, ingesloten inhoud, tabel, lijst, rij, berekend veld, verborgen veld, herhalende groep, logica en variabele. |
De catalogus request-lifecycle vermeldt alle acht aanvraagtools omdat de in-app chat een kleinere kernset adverteert. Via dit
endpoint staan alle acht al op tools/list, dus wat de catalogus toevoegt is documentatie.
Skills (domeinkennis)
Skills zijn ingebouwde gidsen die de agent kan laden via load_skill. Ze bieden domeinkennis die de agent helpt betere
beslissingen te nemen — geen toolschema’s, maar ontwerpadvies en veldsemantiek.
| Skill | Wat het behandelt |
|---|---|
| question-types | Elk vraagtype, de bijbehorende velden en wanneer je elk type gebruikt. |
| logic-rules | Conditionele logica: operators, acties, combinatoren en randgevallen. |
| editing-flows | Patronen voor het bouwen van formulieren: volgorde, pagina-einden, piping. |
| form-best-practices | UX-richtlijnen voor effectief formulierontwerp. |
| form-themes | Themastructuur, tokenreferentie en stijlrichtlijnen. |
| form-settings | Instellingenreferentie: meldingen, e-mailsjablonen, variabelen. |
| analytics | Metrieken-definities en hoe je formulieranalytics interpreteert. |
| toon-format | Compact uitvoerformaat voor gestructureerde gegevensweergave. |
| requests | Aanvragen van begin tot eind: veldsleutels, vormen van prefill-waarden, bezorging, aanvraag-specifieke documenten, callbacks, pollen, en foutherstel. |
Aanvragen
Een aanvraag wijst één gepubliceerd formulier toe aan één met naam genoemde ontvanger, met zijn eigen link, zijn eigen vooraf ingevulde antwoorden, en zijn eigen uitkomst. Het is hoe een agent een echt persoon om iets vraagt en te weten komt wat die persoon heeft gezegd.
Een aanvraag maken
Begin altijd met fields_list(formId). Het geeft de aanspreekbare sleutels van de huidige gepubliceerde versie van het
formulier terug, elk met een usage-regel die aangeeft in welk argument de sleutel thuishoort — zichtbare vragen gaan in
prefill, verborgen velden in context. Leid nooit een sleutel af van een vraagtitel, en lees opnieuw na
form_publish.
{
"formId": "j57...",
"recipient": { "email": "ada@acme.com", "name": "Ada" },
"prefill": { "company_name": "Acme", "plan": "pro" },
"readonly": ["company_name"],
"context": { "crm_id": "A-42" },
"metadata": { "run_id": "exec_918" },
"delivery": "email",
"expiresAt": 1780000000000,
"callbackUrl": "https://hooks.acme.com/formbase",
"idempotencyKey": "po-42"
}Het resultaat bevat de link en de klok:
{
"id": "kd7...",
"status": "pending",
"url": "https://form.formbase.so/r/rq_...",
"deliveryStatus": "queued",
"expiresAt": 1780000000000,
"createdAt": 1747000000000,
"deduplicated": false,
"next": "..."
}delivery staat standaard op “none”, wat je url geeft om zelf af te leveren; “email”
verstuurt de uitnodiging en vereist recipient.email op een Pro- of Business-abonnement, of een van de 10 gratis uitnodigingen
van een Free-account. readonly vergrendelt velden die de ontvanger niet mag bewerken, en elke vergrendelde sleutel moet ook
vooraf zijn ingevuld. context accepteert alleen sleutels van verborgen velden, terwijl metadata ondoorzichtige
administratie is die wordt teruggegeven op request_get en in de callback. expiresAt is in epoch-milliseconden,
standaard 30 dagen vooruit en met een maximum van 365 dagen. idempotencyKey is 30 dagen geldig binnen de werkruimte: dezelfde
sleutel met dezelfde body geeft de oorspronkelijke aanvraag terug met deduplicated: true, en een andere body is een conflict.
Elke response bevat ook een next-regel die de agent vertelt wat hij hierna moet doen.
deliveryStatus is not_requested tot een uitnodiging in de wachtrij staat, dan queued →
sent of failed, en bounced zodra de mailprovider een harde bounce of een klacht meldt. Een aanvraag
aanmaken verbruikt één eenheid van de maandelijkse toewijzing van de werkruimte, ongeacht of de ontvanger ooit antwoordt; zodra die op is,
faalt request_create met MONTHLY_ALLOWANCE_REACHED.
Callbacks of pollen
Met een callbackUrl post formbase één keer per eindgebeurtenis — voltooiing, verlopen, annulering — ondertekend met het
request signing secret van de werkruimte. Zie Callbacks & ondertekening voor de
payload en het verificatierecept.
Autonome agents moeten pollen
Het request signing secret verschijnt alleen ooit op de pagina met inloggegevens van je werkruimte — het wordt nooit teruggegeven via
MCP of de API. Een agent die op zichzelf draait, zonder mens om een ontvanger op te zetten en te configureren, kan daarom geen callback
verifiëren. Laat callbackUrl weg en poll in plaats daarvan request_get(requestId), in de orde van minuten in
plaats van seconden, tot status niet meer “pending” is. expiresAt begrenst hoelang dat de moeite
waard is.
Testmodus
Geef test: true op om de hele opzet te repeteren vóór een echte run. De link opent nog steeds en kan worden afgerond, en de
callback vuurt met “test”: true — maar er wordt niets gemaild wat delivery ook zegt, de aanvraag blijft
verborgen voor de pagina Aanvragen en de analytics-funnel, en de inzending telt nergens mee: geen quotum, geen exports, geen integraties.
Testaanvragen verschijnen alleen in request_list als je includeTest: true opgeeft. De link sluit binnen 24 uur,
en op Free mag een workspace 10 testaanvragen per dag aanmaken.
Documenten per aanvraag
Om één ontvanger een bestand te geven — een conceptcontract, hun eigen offerte — heeft het formulier een Documentenblok
nodig, dat een auteur of een agent invoegt met editor_insertDocumentsBlock. fields_list rapporteert het als
type: “documents”. De bytes reizen nooit via een tool:
Roep
document_createaan metformId,name,contentType, en de exactesizein bytes. Je krijgt{ id, name, contentType, size, uploadUrl, expiresAt }terug. Alleen PDF en afbeeldingen (geen Office-documenten), 25 MB per bestand, en 100 MB aan documenten per aanvraag.PUTde ruwe bytes naaruploadUrlbinnen het uur, metContent-Typeingesteld op het type dat je hebt opgegeven.Verwijs ernaar vanuit
request_create:documents: [{ documentId, field?, name? }].fieldis de veldsleutel van het Documentenblok, alleen optioneel wanneer het formulier precies één zo’n blok heeft.nameoverschrijft de weergavenaam voor deze aanvraag.
De aangeleverde documenten van het blok blijven staan en die van jou verschijnen eronder, alleen voor deze ontvanger.
request_create verifieert de upload voordat de aanvraag bestaat, dus DOCUMENT_NOT_UPLOADED betekent dat stap 2
is overgeslagen. Eén upload kan door elk aantal aanvragen worden gerefereerd, en de bytes tellen mee voor je werkruimteopslag.
Aangepaste domeinen
Geef domainId mee aan request_create om de link te genereren op een van de
aangepaste domeinen van de werkruimte. De ids komen van formShareLink_list,
dat ze teruggeeft als availableCustomDomains. Laat je het weg, dan neemt de link het domein waaronder het formulier al is
gepubliceerd.
Resultaten lezen
request_get geeft de hele aanvraag terug. Zodra deze is voltooid, bevat answers de waarden van de ontvanger,
geordend per veldsleutel, bevat display dezelfde sleutels als leesbare tekst, en is outcome — goedkeuren,
afwijzen of wijzigen — hun oordeel wanneer het formulier een beslissingsvraag heeft. Een
callbackFailedAt-tijdstempel betekent dat de bezorging geen pogingen meer over had en niets je endpoint bereikte; herstel de
ontvanger en roep dan request_replayCallback aan, die het oorspronkelijke event-id opnieuw verstuurt zodat jouw ontvanger
dedupliceert in plaats van opnieuw uit te voeren. Nadat het bewaarbeleid van het formulier een aanvraag verwijdert, wordt
dataPurgedAt ingesteld en zijn de antwoorden definitief verdwenen.
request_list haalt er in één keer veel op, gefilterd op status, outcome, externalId,
en includeTest. Pagineer met nextCursor: een pagina kan zelden terugkomen met een lege items en
hasMore: true, wat niet het einde van de lijst is — geef de cursor terug en ga door.
Resources en prompts
Elke skill en toolcatalogus is ook een MCP-resource op skill://<name> — skill://requests,
skill://editor-inserts. Een client die resources/list ondersteunt, kan ze doorbladeren en lezen zonder
load_skill of load_tools aan te roepen. De server serveert ook vier prompts op prompts/list:
identity, capabilities, data_tools, en editor_tools.
Tools die eerst bevestiging vragen
Elke tool draagt de MCP-hints readOnlyHint en destructiveHint, afgeleid van het werkwoord. Deze tools zijn
gemarkeerd als destructief, omdat ze ongedaan maken een extra aanroep vergt of niet mogelijk is: form_delete,
form_unpublish, workspaceFolder_delete, editor_deleteElement,
translationLanguage_delete, en request_cancel. De meeste clients vragen de gebruiker om bevestiging voordat ze
deze uitvoeren, maar die prompt is de keuze van de client, dus controleer de goedkeuringsinstellingen ervan als je een harde stop nodig
hebt.
Beperkingen
Geen binaire uploads via een tool-aanroep. Afbeeldingen worden ingesteld via URL: omslagen, logo’s en afbeeldingsblokken accepteren
http(s)://- ofdata:image-URI’s. Een document per aanvraag is de uitzondering:document_creategeeft een upload-URL terug waar een client met HTTP-toegang het bestand naartoe kanPUTen (zie Documenten per aanvraag). Om een PDF of screenshot om te zetten in een formulier, gebruik je de ingebouwde AI-chat.Geen werkruimte-AI-vaardigheden. Vaardigheden geschreven in formbase zijn alleen beschikbaar in de ingebouwde AI-chat. De eigen skills van de server (
load_skill) zijn wel beschikbaar via MCP.
Verbinden met een API-token
De meeste clients loggen in via OAuth: voeg de URL toe zonder header en volg Een AI-agent met formbase verbinden. Een client die geen browser kan openen, zoals een script, CI-taak of headless agent, stuurt in plaats daarvan een API-token mee als header.
Claude Code, vanaf de command line:
claude mcp add --transport http formbase https://api.formbase.so/api/mcp \
--header "Authorization: Bearer fb_YOUR_TOKEN"Of in het .mcp.json-bestand van een project:
{
"mcpServers": {
"formbase": {
"type": "http",
"url": "https://api.formbase.so/api/mcp",
"headers": {
"Authorization": "Bearer fb_YOUR_TOKEN"
}
}
}
}Cursor, in .cursor/mcp.json of ~/.cursor/mcp.json:
{
"mcpServers": {
"formbase": {
"url": "https://api.formbase.so/api/mcp",
"headers": {
"Authorization": "Bearer fb_YOUR_TOKEN"
}
}
}
}Om in te loggen met OAuth in plaats van met een token, gebruik je
Toevoegen aan Cursor
. Dit voegt de server-URL toe zonder header, en Cursor vraagt je om in te loggen bij formbase.
Andere clients gebruiken dezelfde URL en header; raadpleeg hun documentatie voor waar.
OAuth gebruiken in plaats van API-tokens
Een app van een derde partij die namens een gebruiker verbinding maakt, moet OAuth gebruiken in plaats van om een geplakt token te vragen.
formbase is een OAuth 2.1-autorisatieserver met verplichte PKCE (S256) en ondoorzichtige tokens — geen JWT’s, geen implicit grant. Claude
desktop, Claude Code en de Claude.ai-webconnector ontdekken dit allemaal via het MCP-endpoint, dus alleen de URL plakken zonder header
volstaat: de 401 wijst naar /.well-known/oauth-protected-resource, en de client neemt het vanaf daar over.
De flow, voor een client die je zelf schrijft:
GET /.well-known/oauth-protected-resource, danGET /.well-known/oauth-authorization-servervoor de endpoint-URL’s, scopes en ondersteunde authenticatiemethoden.POST /oauth/registermet jouwredirect_uris(dynamische clientregistratie, geen inloggegevens nodig). Je krijgt eenclient_id, plus eenclient_secretals je iets anders opgaf dantoken_endpoint_auth_method: “none”. Redirect-URI’s moeten HTTPS zijn, of HTTP oplocalhost. Registratie is beperkt tot 20 per uur per IP.Stuur de gebruiker naar
/oauth/authorizemetresponse_type=code, jouwclient_id, de geregistreerderedirect_uri,scope=mcp:read mcp:write offline_access,state, en eencode_challengemetcode_challenge_method=S256. Ze loggen in, kiezen één werkruimte, en autoriseren.Wissel de code in bij
POST /oauth/tokenmetgrant_type=authorization_codeen jouwcode_verifier, binnen 60 seconden. Codes zijn eenmalig te gebruiken.Roep het MCP-endpoint aan met
Authorization: Bearer fbo_…. Toegangstokens gelden 1 uur; refreshtokens gelden 30 dagen en roteren bij elk gebruik. Een gebruikt refreshtoken opnieuw gebruiken verbrandt de hele keten, dus bewaar het nieuwste.
POST /oauth/revoke (RFC 7009) trekt een toegangs- of refreshtoken in. Een gebruiker kan ook de hele app verbreken via
Verbonden apps op de pagina OAuth en API-sleutels, wat elk token van die app voor die werkruimte tenietdoet.
Heb je wel al een token in handen, dan gaat het op dezelfde plek als een API-sleutel:
{
"mcpServers": {
"formbase": {
"type": "http",
"url": "https://api.formbase.so/api/mcp",
"headers": {
"Authorization": "Bearer fbo_USER_OAUTH_TOKEN"
}
}
}
}Verbonden apps
Elke OAuth-verbinding staat vermeld onder Verbonden apps op de pagina OAuth en API-sleutels, met wanneer die tot stand kwam en voor het laatst is gebruikt. Verbindingen zijn persoonlijk: alleen de gebruiker die er een heeft geautoriseerd ziet hem, en werkruimtebeheerders kunnen die van een ander lid niet bekijken of intrekken. Verbreken werkt onmiddellijk. Een gebruiker die de werkruimte verlaat of eruit wordt verwijderd, verliest al zijn verbindingen ermee.