# Formular und Request in einem Aufruf erstellen

Senden Sie die Blöcke mit dem Request: Formstep baut das Formular, veröffentlicht es und gibt den Link zurück, ganz ohne Editor dazwischen.

## Wann Sie es nutzen

Ein Request verweist normalerweise auf ein Formular, das jemand im Editor gebaut und veröffentlicht hat. Hängen die Fragen vom Einzelfall ab, schickt der Aufrufer sie stattdessen mit: `requests.create` nimmt `form` anstelle von `formId`. Formstep baut aus den Blöcken ein gewöhnliches Formular, veröffentlicht es und erstellt den Request in einem Schritt, sodass entweder beides existiert oder keines von beiden. Wir nennen das ein **Inline-Formular**.

Nutzen Sie weiterhin `formId` für ein Formular, das Sie vielen Personen in derselben Form senden und von Hand im Editor gestalten möchten. Nutzen Sie `form`, wenn eine Automation entscheidet, was gefragt wird.

## Der Aufruf

```bash
curl -X POST https://api.formstep.io/api/v1 \
  -H "Authorization: Bearer $FORMSTEP_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"method":"requests.create","params":{
        "form":{
          "name":"Documents and fee for your mortgage application",
          "logo":{"type":"image","url":"https://acme.example/logo.png"},
          "cover":{"type":"color","color":"#0f4c81"},
          "emoji":"🏦",
          "blocks":[
            {"type":"paragraph","content":"Upload the two documents and pay the processing fee."},
            {"type":"file","key":"payslips","title":"Your last two payslips","maxFiles":2},
            {"type":"payment","key":"fee","title":"Processing fee","amount":40,"currency":"EUR"},
            {"type":"scheduleAppointment","key":"call","title":"Pick a time for our call","eventType":"15 min call"}]},
        "recipient":{"email":"ada@example.com","name":"Ada"},
        "delivery":"email",
        "idempotencyKey":"call-8841"}}'
```

Alles andere am Request behält seine Bedeutung: `recipient`, `prefill`, `context`, `readonly`, `delivery`, Erinnerungen, Ablauf, `callbackUrl`, `test` und `idempotencyKey` werden gegen das neue Formular genauso geprüft wie gegen eines, das von Hand gebaut wurde. Die Antwort enthält zusätzlich das Formular, mit den Schlüsseln, unter denen seine Antworten zurückkommen:

```json
{
  "id": "kd7...",
  "status": "pending",
  "url": "https://form.formstep.io/r/rq_...",
  "form": {
    "id": "j58...",
    "name": "Documents and fee for your mortgage application",
    "reused": false,
    "fields": [
      { "key": "payslips", "type": "file" },
      { "key": "fee", "type": "payment" },
      { "key": "call", "type": "schedule-appointment" }
    ]
  }
}
```

Um eine Auswahlfrage im selben Aufruf vorauszufüllen, senden Sie Optionsschlüssel, wie bei jedem Formular. Der Schlüssel einer Option wird aus ihrem Label abgeleitet, aus `Self-employed` wird also `self_employed`. Ein Label statt eines Schlüssels schlägt mit `INVALID_PREFILL_VALUE` fehl, und der Fehler listet die gültigen Schlüssel.

## Das Formularobjekt

| Feld | Was es tut |
| - | - |
| name | Erforderlich, 1 bis 100 Zeichen. Der Name im Dashboard und im Browser-Tab, für jede empfangende Person gleich. Personalisieren Sie über prefill und context, nie über den Namen. |
| blocks | Erforderlich, 1 bis 200 Blöcke, in der Reihenfolge, in der das Formular sie zeigt. Die Blöcke in den Spalten einer Zeile und die Felder einer wiederholenden Gruppe zählen mit. |
| language | Die Oberflächensprache des Formulars. Standard: Englisch. |
| logo, cover, emoji | Ihre Marke, mit jedem Request gesendet. logo: { "type": "icon", "name": "HeartIcon" } oder { "type": "image", "url": "https\://…" }. cover: { "type": "color", "color": "#0f4c81" } oder { "type": "image", "url": "https\://…", "offsetY": 50 }. emoji: ein Emoji neben dem Namen. Siehe unten. |
| theme | Ein Aussehen pro Farbmodus: { "light": "solarized-light", "dark": "dracula" }. Jeder Modus nimmt eine Preset-ID, in jedem Plan kostenlos, oder ein ganzes Theme-Objekt, wie es das MCP-Tool formTheme\_set nimmt; das braucht den Pro-Plan. |
| settings | Das Formularverhalten, das die Einstellungsseite festlegt: Branding, eine verifizierte E-Mail-Domain (emailDomainId), E-Mails an die Inhaber und an die antwortende Person samt Text, der Erinnerungsplan, die Einladungs-E-Mail, die Weiterleitung und die Aufbewahrung. Felder per Schlüssel: respondentNotificationTo ist der Schlüssel einer E-Mail-Frage, redirectQueryParams ordnet einem Parameter einen Feldschlüssel zu. E-Mail-Text ist reiner Text mit {{key}}-Platzhaltern. Keine Sperre per Passwort oder Anmeldung und keine Einstellung des Freigabelinks wie Bot-Schutz oder Bearbeiten nach dem Absenden: Eine Anfrage bringt ihren eigenen Link mit. Jeder andere Schlüssel wird abgelehnt. |
| translations | Das Formular in weiteren Sprachen, mit einer Sprachauswahl für die empfangende Person: { "de": { "Your name": "Ihr Name" } }, jeder Text des Formulars unter dem Text, wie die Blöcke ihn geschrieben haben. Siehe unten. |

## Blöcke

Ein Block nimmt die Parameter des passenden `editor_insert*`-Tools auf dem [MCP-Server](https://docs.formstep.io/de/developers/mcp-server/), ohne `formId` und `after`, plus einen optionalen `key`. Ohne ihn wird der Schlüssel aus dem Titel abgeleitet, aus „Full name” wird also `full_name`. Einen Parameter, den der Block nicht kennt, lehnt Formstep ab, statt ihn stillschweigend zu verwerfen.

| Art | Typen |
| - | - |
| Fragen | text, contact, number, date, time, radio, checkbox, select, switch, rating, linearScale, matrix, ranking, pictureChoice, file, signature, decision |
| Dokumente | documents, um Dateien zu zeigen, die die empfangende Person liest oder unterschreibt |
| Zahlungen und Buchungen | payment, scheduleAppointment |
| Inhalt | header, paragraph, image (src-URL), embedded, table (cells füllt sie), list, row (columns: die Blöcke jeder Spalte, 2 bis 6 Spalten, und widths in Prozent). Mit einem optionalen key kann Logik den Block ein- oder ausblenden. |
| Struktur | pageDivider, repeatingGroup (fields: die zugehörigen Fragen als Blöcke, jede mit ihrem key), hiddenField (key erforderlich: er ist der context-Schlüssel), calculatedField (sein name ist sein Schlüssel), logic |

Text in header und paragraph, Listenpunkte und Tabellenzellen verstehen `bold`, `italic`, `struck`, `<u>underlined</u>`, `code`, `$$equation$$`, `text`, `<span color=“red” background=“yellow” font=“font-georgia” size=“1.25”>…</span>` mit den Farben, Schriften und Größen der Editor-Werkzeugleiste, und `{{key}}`, das den Wert des Felds mit diesem Schlüssel zeigt. Auszeichnungen lassen sich verschachteln; ein Zeichen ohne Gegenstück bleibt Text. Eine Spalte einer Zeile nimmt jeden Block außer pageDivider, logic, hiddenField, calculatedField, repeatingGroup, row und scheduleAppointment. Ein `decision`-Block nimmt seinen `title` und seine `labels` für Zustimmen, Ablehnen und Änderungen in der Sprache des Formulars.

Blöcke verweisen über den Schlüssel aufeinander, nie über die Element-ID: Ein `logic`-Block nimmt seine `rule` so wie `editor_insertLogic`, mit Feldschlüsseln dort, wo IDs erwartet werden, Optionslabels als Bedingungswerten und Seitennamen für `setNextPage`.

### Zahlungen

Ein `payment`-Block nimmt `amount` und `currency`, oder `amountFromField`: den Schlüssel eines versteckten Felds oder einer Zahlenfrage. Mit einem versteckten Feld übergibt jeder Request seinen eigenen Betrag in `context`, sodass ein einziges Formular jeder Person ihren eigenen Betrag berechnet:

```json
{
  "form": {
    "name": "Pay your invoice",
    "blocks": [
      { "type": "hiddenField", "key": "invoice_total" },
      { "type": "payment", "key": "invoice", "title": "Invoice", "currency": "EUR", "amountFromField": "invoice_total" }
    ]
  },
  "recipient": { "email": "ada@example.com" },
  "context": { "invoice_total": 1200 }
}
```

Eine Zahlenfrage, die den Betrag festlegt, muss ein Pflichtfeld sein und ein `min` über 0 haben, damit niemand mit einem leeren, null oder negativen Betrag in den Checkout gelangt.

Das Formular rechnet über das Standard-Stripe-Konto des Workspace ab, oder über das Konto, das `connection` nennt. Eine Person [verbindet Stripe einmal für den Workspace](https://docs.formstep.io/de/building-forms/payment-collection/#connect-stripe); eine Automation kann das nicht. Ohne nutzbares Konto schlägt der Aufruf fehl und sagt Ihnen, wo Sie es verbinden.

### Buchungen

Ein `scheduleAppointment`-Block nimmt `eventType`, den Titel oder die ID eines Ereignistyps der [Cal.com](https://docs.formstep.io/de/integrations/cal-com/)-Verbindung des Workspace, und `connection`, wenn der Workspace mehrere hat. Formstep sucht den Ereignistyp heraus, wenn der Request erstellt wird. `prefill: { name, email }` nennt die Fragen, deren Antworten die Buchung ausfüllen.

## Ihre Marke

Eine Automation kann Ihren Auftritt nicht erfinden, also geben Sie ihn ihr einmal mit, und sie sendet ihn mit jedem Request: `logo`, `cover` und `emoji` neben den Blöcken, und `theme` für Farben und Schrift.

| Feld | Nimmt |
| - | - |
| logo | Ein Icon aus dem Builder, { "type": "icon", "name": "HeartIcon" }, oder Ihr eigenes Bild, { "type": "image", "url": "https\://…/logo.png" }. |
| cover | Eine Vollfarbe, { "type": "color", "color": "#0f4c81" }, oder ein Bild, { "type": "image", "url": "https\://…/cover.jpg", "offsetY": 50 }, wobei offsetY von 0 bis 100 den sichtbaren Teil eines hohen Bildes wählt. |
| emoji | Ein Emoji, angezeigt neben dem Formularnamen. |

Jedes Bild, einschließlich des `src` eines image-Blocks, muss ein öffentliches JPEG, PNG oder WebP sein. Formstep lädt jedes einmal, wenn das Formular erstellt wird, und behält eine eigene Kopie, sodass das Formular und die PDFs seiner Einreichungen die Quelle überdauern. Eine URL, die sich nicht laden lässt, lässt den Aufruf mit `INLINE_FORM_IMAGE_UNREACHABLE` fehlschlagen und nennt das Feld. Ohne Marke sieht das Formular aus wie ein neues Formular aus dem Dashboard.

## Übersetzungen

`translations` veröffentlicht das Formular sofort in weiteren Sprachen, und die empfangende Person erhält die Sprachauswahl eines übersetzten Formulars: `{ "de": { "Your name": "Ihr Name", "Order for **{{customer}}**": "Bestellung für **{{customer}}**" } }`. Jeder Text des Formulars muss enthalten sein, unter dem Text genau so, wie die Blöcke ihn geschrieben haben: Fragetitel, Optionen, Platzhalter, Überschriften, Absätze, Listenpunkte, Tabellenzellen, Gruppennamen und Schaltflächenbeschriftungen. Auszeichnungen und `{{key}}` bleiben wie in der Quelle. Fehlt ein Text, schlägt der Aufruf mit `INLINE_FORM_TRANSLATION_MISSING` fehl, das die fehlenden Texte auflistet. E-Mail-Texte werden nicht übersetzt.

## Eine Definition, ein Formular

Ein späterer Aufruf mit demselben `form`-Objekt nutzt dasselbe Formular erneut, und die Antwort enthält `“reused”: true`. Hundert Requests mit einer Definition ergeben ein Formular, mit einem Satz Antworten zum Exportieren. Ändern Sie einen Block, den Namen, die Marke oder eine Übersetzung, entsteht ein neues Formular; bereits gesendete Requests behalten das Formular, mit dem sie erstellt wurden. Ein Formular, das Sie für Einreichungen schließen oder zurückziehen, wird nicht erneut genutzt: Der nächste Aufruf mit derselben Definition erstellt ein neues.

> **Personalisieren Sie über prefill und context**
>
> Setzen Sie den Namen der Kundin oder des Kunden, die Rechnungsnummer oder den Betrag in `prefill` und `context`, nie in den Formularnamen, einen Titel oder einen festen Betrag. Eine Definition, die sich pro Kunde ändert, erzeugt pro Kunde ein neues Formular. Setzen Sie `key` bei jeder Frage: Ein abgeleiteter Schlüssel folgt dem Titel, eine umbenannte Frage antwortet also unter einem neuen Schlüssel. Ein Workspace erzeugt höchstens 300 neue Inline-Formulare pro Tag; danach scheitert ein Aufruf, der ein neues Formular erzeugen würde, mit `RATE_LIMITED`, während Aufrufe, die ein Formular wiederverwenden, weiter funktionieren.

Erzeugt ein Aufruf ein neues Formular, obwohl ein offenes Inline-Formular dieselben Blocktypen in derselben Reihenfolge hat, nennt das Ergebnis des Aufrufs dieses Formular in `form.similarForm`. Meist war ein geänderter Name oder Titel der Grund. Um alle Antworten auf ein Inline-Formular zu lesen, listen Sie seine Einreichungen auf, statt jede Anfrage einzeln zu lesen.

## Im Dashboard

Ein Inline-Formular gehört zu seiner Anfrage, deshalb steht es auf der Seite der Anfrage statt in der Formularliste. Die Seite listet die Antworten nach Feldschlüssel, und **Vorschau** öffnet das Formular so, wie die empfangende Person es gesehen hat, ausgefüllt mit ihren Antworten. Ein Link zum Editor oder zu den Einreichungen eines Inline-Formulars öffnet stattdessen die Anfragen-Seite, gefiltert auf dieses Formular. Agenten sehen Inline-Formulare weiterhin in `form_list`, und die API lehnt Änderungen an ihnen ab.

Ein Inline-Formular hat keinen Freigabelink, und Sie können keinen hinzufügen: Seine Requests sind der einzige Zugang, und der Callback trägt jede Antwort. Integrationen hat es ebenfalls keine.

Da es keine eigene Seite hat, wird ein Inline-Formular nie von Hand gelöscht. Es geht mit seinem letzten Request: Wenn Sie [einen Request löschen](https://docs.formstep.io/de/requests/managing-requests/#delete) oder der Aufbewahrungszeitraum des Formulars ihn entfernt und kein anderer Request das Formular noch verwendet, wird auch das Formular gelöscht.

## Wenn der Aufruf abgelehnt wird

Jede Ablehnung ist ein `VALIDATION_ERROR`, auf dessen `details.reason` Ihre Automation verzweigen kann. Es wird nichts erstellt.

| Grund | Was zu tun ist |
| - | - |
| FORM\_SOURCE\_MISSING / FORM\_SOURCE\_AMBIGUOUS | Senden Sie genau eines von formId und form. |
| INLINE\_FORM\_INVALID | Ein Block oder ein Feld von form verletzt sein Schema. details.issues nennt jedes davon. |
| INLINE\_FORM\_DUPLICATE\_KEY | Zwei Blöcke ergeben denselben Schlüssel. Geben Sie einem davon einen eigenen key. |
| INLINE\_FORM\_UNKNOWN\_KEY | Eine Logikregel, amountFromField oder ein Buchungs-prefill nennt einen Schlüssel, den kein Block hat. |
| INLINE\_FORM\_AMOUNT\_MISSING | Übergeben Sie in context eine positive Zahl für das Betragsfeld. |
| INLINE\_FORM\_CONNECTION\_MISSING | Stripe oder Cal.com ist nicht verbunden. details.connectUrl ist die Seite, die eine Person einmal öffnet, um es zu verbinden. |
| INLINE\_FORM\_EVENT\_TYPE\_NOT\_FOUND | Kein Cal.com-Ereignistyp passt. Der Fehler listet die vorhandenen auf. |
| INLINE\_FORM\_IMAGE\_UNREACHABLE | Eine Bild-URL ist kein öffentliches JPEG, PNG oder WebP. details.field nennt sie. Nutzen Sie eine URL, die jeder öffnen kann, oder lassen Sie das Bild weg. |
| INLINE\_FORM\_TRANSLATION\_MISSING | Einer Übersetzung fehlen Texte, die das Formular hat. details.missing listet sie auf, so wie sie in den Blöcken stehen. |
| INLINE\_FORM\_PUBLISH\_ISSUES | Die Veröffentlichung hat das Formular abgelehnt, zum Beispiel wegen eines Ereignistyps, der sich nicht aus einem Formular buchen lässt. |

## Aus einem Agenten

Auf dem MCP-Server nimmt `request_create` dasselbe `form`. Bevor ein Agent ein Formular mit einer Zahlung oder einer Buchung baut, liest er `workspaceConnection_list`: Es zeigt die verbundenen Stripe- und Cal.com-Konten und, wenn eines fehlt, den Link, den eine Person einmal öffnen muss. `scheduler_listEventTypes` listet die Ereignistypen, die eine Buchung nennen kann, und `paymentConnection_setDefault` ändert das Standard-Stripe-Konto. Über die REST-API heißen dieselben drei `connections.list`, `scheduler.listEventTypes` und `paymentConnections.setDefault`.

[Einen Request erstellen](https://docs.formstep.io/de/requests/creating-requests/) — Prefill, Sperren und Kontext für jedes Formular.

[Feldschlüssel](https://docs.formstep.io/de/requests/field-keys/) — Wie Schlüssel abgeleitet werden und stabil bleiben.

[Callbacks & Signierung](https://docs.formstep.io/de/requests/callbacks/) — Erhalten Sie die Antworten, wenn der Request endet.

[Zahlungserfassung](https://docs.formstep.io/de/building-forms/payment-collection/) — Verbinden Sie Stripe für den Workspace.
