formbasedocs
Zur AppApp

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

Der Tab Anfragen auf seinem curl-Tab, mit der Formular-ID und einem fertigen requests.create-Aufruf
Der curl-Tab: die Formular-ID und ein Aufruf, bereits mit den Feldschlüsseln dieses Formulars ausgefüllt.

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

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.

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..."}}'
Antwort
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 markiert ein verstecktes Feld. Sein Wert gehört in context, niemals in prefill; der Schlüssel eines versteckten Felds in prefill wird mit UNKNOWN_FIELD_KEY abgelehnt.

  • calculated: true markiert ein berechnetes Feld. Das Formular ermittelt seinen Wert selbst, also kann niemand einen liefern; du liest ihn unter seinem Schlüssel in answers zurück.

  • prefillable: false markiert 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 ebenfalls prefillable: false: versteckte Felder übernehmen context, berechnete Felder übernehmen nichts.

  • options listet 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 ihre rows und columns auf dieselbe Weise auf.

  • Wiederholungsgruppen kommen als ein Eintrag mit type: “group”, repeating: true und einer Liste von members zurück.

Schritt 2 — Den Request erstellen

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"}}'
Antwort
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 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 inDie empfangende Person …Kommt im Callback zurück
PrefillprefillSieht ihn und kann ihn ändernJa, als Antwort
Gesperrtes Feldprefill + readonlySieht ihn, kann ihn nicht ändernJa, als Antwort
KontextcontextKann ihn nicht ändern; sieht ihn nur, wo du ihn erwähnstJa, im Request-Block und als Antwort
MetadatametadataSieht ihn nie, das Formular auch nichtJa, 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.

TypSende
text, email, phone, url, textareaEinen String
number, rating, scaleEine Zahl
switchtrue oder false
date"2026-03-04"
time"09:30" oder "09:30:00"
radio, selectDen Optionsschlüssel, nicht sein Label
checkbox, ranking, picture-choiceEin Array von Optionsschlüsseln
matrixEin 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-appointmentNichts — die empfangende Person liefert diese selbst
jedes Feld mit calculated: trueNichts — das Formular ermittelt den Wert selbst
documentsNichts 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. 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. 2

    Die Bytes hochladen

    Sende die Datei per PUT an uploadUrl mit demselben Content-Type. Noch wird nichts überprüft.

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

requests.create → documents
json
"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

OptionWas sie bewirkt
languageDie 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.
expiresAtWann der Link aufhört zu funktionieren, als Unix-Zeitstempel in Millisekunden. Standard: 30 Tage; 365 Tage sind das Maximum.
externalIdDeine eigene ID für diesen Request. Du kannst später danach filtern.
idempotencyKeySorgt dafür, dass ein wiederholter Lauf den Request wiederverwendet, statt einen zweiten zu erstellen.
callbackUrlWohin formbase den Callback per POST sendet, wenn der Request endet. Nur HTTPS.
domainIdErstellt den Link auf einer deiner benutzerdefinierten Domains, anstelle der, unter der das Formular bereits veröffentlicht ist.
testEin 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 delivery sagt. 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.list ausgelassen, sofern du nicht includeTest: true übergibst.

  • Der Link schließt sich innerhalb von 24 Stunden, auch wenn expiresAt mehr verlangt; das expiresAt in der Antwort nennt den Zeitpunkt. Auf Free darf ein Workspace 10 Testanfragen pro Tag erstellen. Die nächste schlägt mit RATE_LIMITED und dem Grund TEST_REQUEST_LIMIT_REACHED fehl, und retryAfterMs sagt, 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.