Anfragen
Einen Request erstellen
Zwei API-Aufrufe: frag das Formular, was ihm mitgeteilt werden kann, und weise es dann mit den Werten, die du bereits kennst, einer Person zu.
Starte im Share-Sheet

Öffne dein veröffentlichtes Formular, klicke auf Teilen und wähle den Tab Anfragen. Die Karte dort gibt dir alles, was du für den ersten Aufruf brauchst:
Die Formular-ID, mit Kopieren-Button.
Ein curl-Snippet und einen MCP-Prompt, beide aus den echten Feldschlüsseln deines Formulars gebaut — das Beispiel spricht also bereits die Felder an, die dieses Formular tatsächlich hat.
Einen Tab Manuell, der von Hand einen Request erstellt, und Selbst ausprobieren, das aus dem, was du dort ausgefüllt hast, einen Request im Testmodus macht und dir dessen Link übergibt.
Einen Link zur Requests-Seite, gefiltert auf dieses Formular.
Erst veröffentlichen
Ein unveröffentlichtes Formular kann nicht per Request versendet werden, und die Snippets bleiben deaktiviert, bis du veröffentlichst.
Feldschlüssel frieren bei der ersten Veröffentlichung ein — deshalb kann deine Automation company_name auch noch ein Jahr
später ansprechen. Siehe Feldschlüssel.
Schritt 1 — Die Felder entdecken
fields.list liefert jedes Feld der aktuell veröffentlichten Version des Formulars, mit dem Schlüssel, über den es
angesprochen wird, der Wertform, die es erwartet, und dem Bucket, zu dem es gehört.
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..."}}'{
"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: truemarkiert ein verstecktes Feld. Sein Wert gehört incontext, niemals inprefill; der Schlüssel eines versteckten Felds inprefillwird mitUNKNOWN_FIELD_KEYabgelehnt.calculated: truemarkiert ein berechnetes Feld. Das Formular ermittelt seinen Wert selbst, also kann niemand einen liefern; du liest ihn unter seinem Schlüssel inanswerszurück.prefillable: falsemarkiert ein Feld, für das niemand einen Wert liefern kann: Datei-Upload, Signatur, Zahlung, Terminbuchung und Dokumente-Blöcke. Die Fragen füllt die empfangende Person selbst aus. Versteckte Felder und berechnete Felder zeigen ebenfallsprefillable: false: versteckte Felder übernehmencontext, berechnete Felder übernehmen nichts.optionslistet die Auswahlmöglichkeiten einer Auswahlfrage. Sende den Schlüssel der Option, nicht ihr Label; das Label ist nur dazu da, damit du die dir bekannte Option auf ihren Schlüssel abbilden kannst. Eine Matrix listet ihrerowsundcolumnsauf dieselbe Weise auf.Wiederholungsgruppen kommen als ein Eintrag mit
type: “group”,repeating: trueund einer Liste vonmemberszurück.
Schritt 2 — Den Request erstellen
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"}}'{
"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 ist “queued”, wenn formbase die Einladung per E-Mail versendet, und “not_requested”,
wenn du den Link selbst zustellst.
Prefill, gesperrte Felder und Kontext
Drei verschiedene Dinge lassen sich an einen Request anhängen, und sie zu verwechseln ist der häufigste erste Fehler.
| Gehört in | Die empfangende Person … | Kommt im Callback zurück | |
|---|---|---|---|
| Prefill | prefill | Sieht ihn und kann ihn ändern | Ja, als Antwort |
| Gesperrtes Feld | prefill + readonly | Sieht ihn, kann ihn nicht ändern | Ja, als Antwort |
| Kontext | context | Kann ihn nicht ändern; sieht ihn nur, wo du ihn erwähnst | Ja, im Request-Block und als Antwort |
| Metadata | metadata | Sieht ihn nie, das Formular auch nicht | Ja, im Request-Block |
Prefill
Anfangsantworten für die sichtbaren Fragen, sodass die empfangende Person prüft und korrigiert, statt von vorne zu tippen. Alles, was du bereits über sie weißt, gehört hierher — der Firmenname aus deinem CRM, der Betrag aus der Rechnung, die Antworten vom letzten Jahr.
Gesperrte Felder
Liste einen vorausgefüllten Schlüssel in readonly auf, und die empfangende Person sieht den Wert, kann ihn aber nicht ändern.
Nutze das für Fakten, die bestätigt statt geliefert werden — die Vertragsnummer, den vereinbarten Preis. Die Sperrung gilt pro Request:
Das Formular selbst bleibt unberührt, und dasselbe Feld ist beim nächsten Request wieder frei bearbeitbar.
Jeder gesperrte Schlüssel muss auch vorausgefüllt sein, und ein gesperrtes Pflichtfeld muss mit etwas Nicht-Leerem vorausgefüllt sein — sonst stünde die empfangende Person vor einem Formular, das sie nie absenden könnte, und formbase lehnt den Aufruf ab, statt diese Falle zu erstellen.
Kontext
Vertrauenswürdige Werte für die versteckten Felder des Formulars — eine Fallnummer, eine Workflow-Run-ID, ein Betrag. Kontext speist Variablen, bedingte Logik, berechnete Felder und E-Mail-Texte, kommt unverändert im Callback zurück, und die empfangende Person kann ihn nicht ändern. Das ist der Unterschied zum Befüllen eines versteckten Felds über eine URL bei einem öffentlichen Link, wo jeder die Query-String bearbeiten kann; Request-Links ignorieren URL-Query-Parameter vollständig. Kontextwerte müssen ein String, eine Zahl oder ein Boolean sein.
Kontext ist nicht frei formulierbar: Jeder Schlüssel muss ein verstecktes Feld auf der veröffentlichten Version des Formulars sein, und
jeder andere Schlüssel wird mit UNKNOWN_FIELD_KEY abgelehnt. Buchhaltung ohne passendes verstecktes Feld, wie eine
Ausführungs-ID, gehört in metadata.
Versteckte Felder werden im Formular nicht angezeigt, aber ein Kontextwert ist vor der empfangenden Person nicht geheim. Sie sieht ihn
überall dort, wo das Formular oder die Einladung ihn zeigt: als Erwähnung im Formularinhalt
oder im E-Mail-Text, oder als sichtbare Frage, die dieses versteckte Feld als
Standardwert verwendet. In diesem letzten Fall sieht die empfangende
Person den Kontextwert in dieser Frage vorausgefüllt und kann die Antwort bearbeiten. Der Kontextwert selbst bleibt unverändert. Ein
prefill für den eigenen Schlüssel dieser Frage hat Vorrang vor dem Standardwert.
Metadata
Deine eigene Buchhaltung — eine Ausführungs-ID, eine CRM-Datensatz-ID. Sie erreicht das Formular gar nicht, kann also nicht in Texte eingespeist oder von Logik gelesen werden; sie fährt einfach mit und kommt in jedem Callback und jeder Statusabfrage zurück.
Wertformen
Sende Werte in der Form, die der type aus fields.list verlangt. Eine falsche Form kommt als Validierungsfehler
zurück, der den Schlüssel, den erwarteten Typ und — bei Auswahlfragen — die akzeptierten Werte nennt.
| Typ | Sende |
|---|---|
| text, email, phone, url, textarea | Einen String |
| number, rating, scale | Eine Zahl |
| switch | true oder false |
| date | "2026-03-04" |
| time | "09:30" oder "09:30:00" |
| radio, select | Den Optionsschlüssel, nicht sein Label |
| checkbox, ranking, picture-choice | Ein Array von Optionsschlüsseln |
| matrix | Ein Objekt von Zeilenschlüssel zu Spaltenschlüssel: { "row_key": "column_key" } |
| group (Wiederholung) | Ein Array von Instanzen, höchstens 100: [{ "member_key": value }, …] |
| file, signature, payment, schedule-appointment | Nichts — die empfangende Person liefert diese selbst |
| jedes Feld mit calculated: true | Nichts — das Formular ermittelt den Wert selbst |
| documents | Nichts in prefill — nutze die Dokumente-Option weiter unten |
Dokumente
Ein Dokumentenblock reicht der ausfüllenden Person Dateien. Seine hinterlegten Dateien sind für alle gleich und bleiben immer erhalten; ein Request fügt Dateien für seine eine empfangende Person darunter hinzu — den eigenen Mietvertrag der Kundin, eine Ausweiskopie zur Prüfung. Bytes wandern nie durch den API-Aufruf selbst: erst hochladen, dann referenzieren.
- 1
Den Upload reservieren
Rufe documents.create auf mit formId, name (1–200 Zeichen), contentType (PDF oder Bild), der exakten Größe in Bytes und optional einem sha256 der Datei (64 Hex-Zeichen). Du bekommst eine id und eine uploadUrl zurück, die eine Stunde lang gültig ist.
- 2
Die Bytes hochladen
Sende die Datei per PUT an uploadUrl mit demselben Content-Type. Noch wird nichts überprüft.
- 3
Auf dem Request referenzieren
Übergib documents: [{ documentId, name? }] an requests.create. formbase prüft das hochgeladene Objekt (Größe, Dateisignatur, sha256, falls du eine gesendet hast), bevor der Request erstellt wird, und die empfangende Person sieht die Datei im Block.
"documents": [
{ "documentId": "kn7...", "name": "Your lease contract" },
{ "documentId": "kn8..." }
]name überschreibt den auf dem Upload gespeicherten Anzeigenamen. Hat das Formular mehr als einen Dokumente-Block, benennst du
das Ziel mit field, dem Feldschlüssel des Blocks (fields.list führt ihn auf, zusammen mit den hinterlegten
Dokumenten, die alle Antwortenden ohnehin erhalten). Die Dateien erscheinen unterhalb dieser hinterlegten Dokumente — ein Request fügt
Dateien hinzu, er ersetzt nie eine. Ein Upload kann von so vielen Requests referenziert werden, wie du willst — eine einmal hochgeladene
Preisliste bedient fünfhundert Requests.
Limits: nur PDF und Bilder, 25 MB pro Dokument, 100 MB pro Request (DOCUMENTS_TOO_LARGE), und höchstens 20 Dokumente pro
Block einschließlich der hinterlegten (DOCUMENTS_TOO_MANY). Die Dateien werden auf dein Workspace-Speicherkontingent
angerechnet und werden freigegeben, sobald die Requests, die auf sie verweisen, aus dem Aufbewahrungszeitraum des Formulars fallen. Die
Einreichung protokolliert die Liste, die die empfangende Person unter dem Feldschlüssel des Blocks gesehen hat, sodass der Callback dir
genau sagt, welche Dateien dieser Person gegeben wurden.
Die übrigen Optionen
| Option | Was sie bewirkt |
|---|---|
| language | Die Sprache, in der sich das Formular öffnet und in der die Einladung verfasst ist; eine der veröffentlichten Sprachen des Formulars. Weggelassen, wird die Standardsprache des Formulars verwendet. Die empfangende Person kann die Sprache trotzdem wechseln, wie bei einem öffentlichen Link. |
| delivery | "email" versendet die Einladung für dich und benötigt eine Empfänger-E-Mail sowie einen Pro- oder Business-Plan, oder eine der 10 kostenlosen Einladungen eines Free-Kontos; "none" (Standard) bedeutet, du lieferst den Link selbst aus. |
| reminders | Überschreibt den Erinnerungsplan des Formulars für genau diesen Request mit bis zu fünf Idle-Offsets wie ["2d", "12h", "30m"], oder eine leere Liste schaltet Erinnerungen für diesen Request aus. Ein individueller Zeitplan benötigt eine Empfänger-E-Mail und einen Pro- oder Business-Plan; ohne Empfänger-E-Mail läuft einfach der eigene Zeitplan des Formulars nicht. |
| expiresAt | Wann der Link aufhört zu funktionieren, als Unix-Zeitstempel in Millisekunden. Standard: 30 Tage; 365 Tage sind das Maximum. |
| externalId | Deine eigene ID für diesen Request. Du kannst später danach filtern. |
| idempotencyKey | Sorgt dafür, dass ein wiederholter Lauf den Request wiederverwendet, statt einen zweiten zu erstellen. |
| callbackUrl | Wohin formbase den Callback per POST sendet, wenn der Request endet. Nur HTTPS. |
| domainId | Erstellt den Link auf einer deiner benutzerdefinierten Domains, anstelle der, unter der das Formular bereits veröffentlicht ist. |
| test | Ein Testlauf: Es wird nichts versendet, der Callback meldet test, und die Einreichung zählt nirgends. Siehe unten. |
Testmodus
Übergib test: true, um die gesamte Verkabelung vor einem echten Lauf durchzuspielen. Eine Testanfrage ist in jeder Hinsicht,
die für die Verkabelung zählt, echt: Der Link öffnet sich und lässt sich abschließen, der Callback
feuert wie gewohnt, und requests.get liefert die Antworten. Was sie nie tut, ist jemanden oder etwas zu erreichen, das du
danach wieder aufräumen müsstest:
Es wird weder eine Einladung noch eine Erinnerung versendet, egal was
deliverysagt. Erinnerung senden wird dafür verweigert, und sie verbraucht nichts von deinem monatlichen Kontingent.Der Callback trägt
“test”: true, sodass deine Automation danach verzweigen oder das Ereignis ignorieren kann.Die Einreichung wird gespeichert, zählt aber nicht: nicht gegen dein monatliches Kontingent (ein Test wird auch abgeschlossen, wenn das Kontingent aufgebraucht ist), und sie erscheint nie in den Einreichungszahlen des Formulars, im Einreichungen-Tab, in Exporten oder in deinen Integrationen. Niemand wird benachrichtigt.
Der Request wird auf der Requests-Seite hinter Testanfragen anzeigen versteckt, im Request-Funnel in Analytics ausgelassen, und aus
requests.listausgelassen, sofern du nichtincludeTest: trueübergibst.Der Link schließt sich innerhalb von 24 Stunden, auch wenn
expiresAtmehr verlangt; dasexpiresAtin der Antwort nennt den Zeitpunkt. Auf Free darf ein Workspace 10 Testanfragen pro Tag erstellen. Die nächste schlägt mitRATE_LIMITEDund dem GrundTEST_REQUEST_LIMIT_REACHEDfehl, undretryAfterMssagt, wann du es erneut versuchen kannst. Pro und Business haben kein Tageslimit.
Selbst ausprobieren im Share-Sheet ist dieser Modus per Knopfdruck: Er übernimmt den Entwurf aus dem Tab Manuell — Prefill, Sperren, Kontext, Callback, Ablauf —, adressiert den Request an dein eigenes Konto, verschickt keine E-Mail und übergibt dir den Link zum selbst Öffnen.
Was ein Request kostet
Jeder Plan hat ein monatliches Kontingent, das sich beide Kanäle teilen: Eine Einreichung über einen Freigabelink
verbraucht eine Einheit, und jeder Request, den du erstellst, ebenfalls — egal ob die empfangende Person antwortet, ihn ignoriert, oder du
ihn stornierst. Die Einreichung, die ein Request einsammelt, ist bereits bezahlt und zählt nirgends. Free enthält 1.000 Einheiten pro
Monat, Pro und Business 50.000; der Zähler wird am 1. jedes Monats zurückgesetzt, UTC. Am Limit schlägt requests.create mit
UPGRADE_REQUIRED und dem Grund MONTHLY_ALLOWANCE_REACHED fehl; bereits erstellte Requests bleiben beantwortbar.
Im Free-Plan verbraucht ein Request, der mit “delivery”: “email” erstellt wird, zusätzlich eine der
10 kostenlosen Einladungen des Kontos. Sie werden nie zurückgesetzt;
sind sie aufgebraucht, schlägt die E-Mail-Zustellung mit UPGRADE_REQUIRED und dem Grund FREE_INVITATIONS_USED
fehl.
Idempotenz
Sende denselben idempotencyKey mit demselben Body und du bekommst den ursprünglichen Request zurück, mit
deduplicated: true und dem ursprünglichen Link — kein zweiter Request, keine zweite E-Mail. Verwendest du den Schlüssel mit
einem anderen Body erneut, lehnt formbase mit IDEMPOTENCY_CONFLICT ab, statt zu raten, welchen du meintest.
Schlüssel sind an den Workspace gebunden und gelten 30 Tage; danach startet derselbe Schlüssel einen neuen Request.
In einem Workflow-Tool ist die Ausführungs-ID der natürliche Schlüssel: Ein Lauf, der nach einem Netzwerkaussetzer wiederholt wird, greift den Request auf, den er bereits erstellt hat.
Ratenbegrenzung
requests.create und documents.create teilen sich ein Budget von 60 Aufrufen pro Minute, gezählt
pro API-Token (oder pro Nutzer, bei einem Aufruf ohne Token). Ein Rückstand, den du abarbeitest, sollte sich selbst drosseln; ein Schub
über das Budget hinaus wird abgelehnt und kann wiederholt werden.
Benutzerdefinierte Domains
Ist das Formular bereits auf einer deiner benutzerdefinierten Domains veröffentlicht,
werden Request-Links dort automatisch erstellt — https://forms.deinunternehmen.com/r/rq_…. Nenne domainId
explizit, wenn das Formular auf mehr als einer veröffentlicht ist. Die Domain muss zum selben Workspace wie das Formular gehören.
Was die empfangende Person sieht
Genau das Formular, das du erstellt hast — gleiches Theme, gleiches Logo, gleiche Sprache — mit ihren Werten an Ort und Stelle, gesperrten Feldern schreibgeschützt, und ohne ein Captcha lösen zu müssen. Beim Absenden bekommt sie deine Dankesseite. Kehrt sie danach zum Link zurück, bekommt sie die Ergebnisseite statt eines leeren Formulars.
Auf der Seite steht keine Nachricht von deiner Automation. Alles, was der empfangenden Person mitgeteilt werden muss, gehört ins Formular selbst, wo du es personalisieren kannst, indem du einen Kontextwert oder ein vorausgefülltes Feld erwähnst.
Ein KI-Agent führt dieselben zwei Schritte aus wie fields_list und request_create, mit denselben Optionen —
einschließlich documents und domainId. Siehe Requests auf dem MCP-Server.