claritos

Formulare bauen

Wie eine Organisation ihre Formulare über die API oder einen KI-Agenten als Ganzes anlegt, prüft, ersetzt und archiviert: ein Baum aus Kopf, Seiten und Elementen in einem Aufruf.

Worum es geht

Ein Formular ist ein Baum: Kopf (Name, Kurzname, Ziel, Anrede, Einwilligungen), Seiten und darauf Elemente (Text, Standardfeld, eigenes Feld, Raster, Wiederholgruppe). Gebaut wird er in einem Aufruf, nicht Element für Element; neue eigene Felder entstehen dabei mit. Ziel ist PROSPECT (Einsendungen werden Interessenten) oder CLIENT_RECORD (Bögen der Akte, nur mit dem Lizenzrecht Praxisbetrieb). Alle Bau-Endpunkte verlangen eine Person mit der Rolle Admin der Organisation; Lesen und Prüflauf das Recht forms:read, Anlegen, Ersetzen, Einstellungen, Lizenz-Logo, Teilen-Link und Archivieren forms:write. Der persönliche Link braucht keine Admin-Rolle, dafür zusätzlich contacts:write.

1. Bausteine holen

GET /forms/building-blocks?targetEntity=PROSPECT nennt die Standardfelder (key, und wie sie in Bedingungen heissen: inConditions), die vorhandenen eigenen Felder der Organisation mit Kurzname (slug), Art und Optionen, die Arten für neue Felder, die Sprache der Bedingungen und den Aufbau von Raster und Wiederholgruppe. Ein vorhandenes eigenes Feld nimmt man über seinen slug, statt es neu anzulegen.

curl
curl "https://www.claritos.io/api/v1/forms/building-blocks?targetEntity=PROSPECT" \
  -H "Authorization: Bearer capi_…"

2. Den Baum beschreiben

pages[].elements[] mit kind: text (content), standardField (field = key aus Schritt 1), field (field = slug; mit new entsteht das Feld: label, type, options), matrix und repeater (key, config). Jedes Feld kann required, question, placeholder und condition tragen; condition ist ein Ausdruck über Felder WEITER OBEN, etwa schlaf == 'schlecht'. Felder mit derselben row stehen nebeneinander, weight teilt die Breite. Einwilligungen: consentMode none, optional oder promotion; consents.marketing.email und .sms mit eigenem Wortlaut oder ORGANIZATION für den Text der Organisation, consents.additional für bis zu zwei weitere Häkchen. Die Anfrage selbst braucht kein Häkchen; das Formular zeigt einen Datenschutzhinweis.

json
{
  "targetEntity": "PROSPECT",
  "name": "Vorab-Check Schlaf",
  "slug": "vorab-check-schlaf",
  "addressForm": "SIE",
  "consentMode": "none",
  "pages": [
    {
      "title": "Vor Ihrem Termin",
      "elements": [
        { "kind": "standardField", "field": "firstName", "required": true, "row": "name" },
        { "kind": "standardField", "field": "lastName", "required": true, "row": "name" },
        { "kind": "standardField", "field": "email", "required": true },
        { "kind": "field", "field": "schlaf", "required": true,
          "new": { "label": "Wie schlafen Sie?", "type": "SELECT", "options": ["gut", "mittel", "schlecht"] } },
        { "kind": "field", "field": "schlaf_notiz", "condition": "schlaf == 'schlecht'",
          "new": { "label": "Was stört Ihren Schlaf?", "type": "TEXTAREA" } }
      ]
    }
  ]
}

3. Probelauf

POST /forms/validate prüft den Baum wie das Anlegen, ohne etwas zu speichern, und antwortet immer 200: valid, errors mit Pfad (etwa body.pages.0.elements.3.field, Zählung ab 0), Code und Satz, dazu notes sowie die Felder, die entstehen (createdFields) oder wiederverwendet werden (reusedFields). Ein schon vergebener Kurzname erscheint hier als Fehler mit dem Code form-slug-taken an body.slug. Grün heisst: Anlegen geht durch.

curl
curl -X POST https://www.claritos.io/api/v1/forms/validate \
  -H "Authorization: Bearer capi_…" -H "Content-Type: application/json" \
  -d @vorab-check.json
json
{ "valid": true, "errors": [], "notes": [], "slug": "vorab-check-schlaf",
  "pages": 1, "elements": 5, "createdFields": ["schlaf", "schlaf_notiz"], "reusedFields": [] }

4. Anlegen

POST /forms legt das Formular an und antwortet 201 mit der Definition wie GET /forms/{id}, dazu build mit createdFields, reusedFields und notes. Fehler im Baum kommen als 422 validation-failed mit denselben Pfaden wie im Probelauf, ein vergebener Kurzname als 409 mit reason form-slug-taken. Ein Idempotency-Key verhindert ein zweites Formular, wenn der Aufruf wiederholt wird.

curl
curl -X POST https://www.claritos.io/api/v1/forms \
  -H "Authorization: Bearer capi_…" -H "Content-Type: application/json" \
  -H "Idempotency-Key: <eindeutig>" -d @vorab-check.json

5. Als Ganzes ersetzen

PUT /forms/{id} nimmt denselben Rumpf und ersetzt den Baum. Was im Rumpf fehlt, gilt als Vorgabe, nicht als bisheriger Wert: immer den ganzen Baum schicken, am besten aus GET /forms/{id} abgeleitet. Was der Rumpf nicht kennt (Freigaben, Links, Abschlussseite, Darstellung), bleibt; das ändert PATCH (Abschnitt 6). Seiten bleiben nach ihrer Position erhalten, Einsendungen bleiben lesbar. Das Ziel lässt sich nicht ändern (422 an body.targetEntity); ein archiviertes Formular antwortet 409 mit reason archived.

curl
curl -X PUT https://www.claritos.io/api/v1/forms/<FORMULAR> \
  -H "Authorization: Bearer capi_…" -H "Content-Type: application/json" \
  -d @vorab-check.json

6. Einstellungen und Teilen-Link

PATCH /forms/{id} ändert nur Einstellungen, nicht den Baum: name, addressForm, allowMultiple, publicAccessEnabled (öffentlicher Link), availableInPortal, consentMode, die Anzeige (showProgress, showSummary, showFieldLabels, showClientHeader), reminderService, recognitionCard, translateResponses, thirdPartyMode, addressFinderMode und addressFinderPlaceholder, licenseLabel und completion. Was fehlt, bleibt; die Namen sind dieselben wie in GET /forms/{id}, und die Antwort ist die Definition. completion ersetzt den ganzen Abschluss, null stellt den Standardtext wieder her. Kurzname, Ziel, Seiten und Einwilligungen setzt nur PUT; mitgeschickt ergeben sie 422 invalid_combination. Den Link der Organisation zum Teilen liest GET /forms/{id}/share-link (url ist null, solange keiner angelegt oder der öffentliche Link aus ist). POST /forms/{id}/share-link legt ihn an und liefert bei jedem weiteren Aufruf denselben; ist der öffentliche Link aus, antwortet er 409 mit reason public-access-disabled. Links von Vermittlern erscheinen hier nie: Über sie würde jede Einsendung dem Vermittler zugeschrieben. Das Lizenz-Logo kommt in zwei Schritten: POST /forms/{id}/license-logo/upload liefert uploadUrl, uploadHeaders und storageKey; die Datei (PNG, JPEG, WebP oder SVG, höchstens 2 MB) geht per PUT an uploadUrl; danach bestätigt POST /forms/{id}/license-logo/confirm mit dem storageKey. Erst dann zeigt der Bogen das Logo (licenseLogoUrl in GET /forms/{id}); ist die Datei noch nicht da, antwortet confirm 422 an body.storageKey. DELETE /forms/{id}/license-logo entfernt es.

curl
curl -X PATCH https://www.claritos.io/api/v1/forms/<FORMULAR> \
  -H "Authorization: Bearer capi_…" -H "Content-Type: application/json" \
  -d '{ "publicAccessEnabled": true, "showSummary": true, "completion": { "title": "Danke!" } }'
curl
curl -X POST https://www.claritos.io/api/v1/forms/<FORMULAR>/share-link \
  -H "Authorization: Bearer capi_…"

7. Persönlicher Link

POST /forms/{id}/personal-links stellt einen Link für eine bestimmte Person aus, etwa nach einer Visitenkarte am Messestand. values trägt ihre Angaben mit std:-Schlüsseln wie beim Einsenden; nötig ist mindestens std:email, std:firstName oder std:lastName, sonst 422 invalid_combination an body.values. Der Kontakt entsteht dabei oder wird gefunden, nach derselben Dublettenregel wie beim Einsenden; leere Felder werden ergänzt, nichts wird überschrieben. Der Bogen zeigt nur die mitgeschickten Werte, nichts, was am Kontakt schon steht. Antwort 201 mit url, contactId und validUntil (sieben Tage); jeder Aufruf ist ein neuer Link. documentId hängt ein Dokument der Organisation (etwa das Foto der Karte) an den Kontakt, wenn es noch keinem zugeordnet ist; ein unbekanntes Dokument ergibt 422 invalid_reference. Nur für Formulare mit dem Ziel PROSPECT, sonst 409 mit reason target-not-supported. Rechte forms:write und contacts:write, keine Admin-Rolle.

curl
curl -X POST https://www.claritos.io/api/v1/forms/<FORMULAR>/personal-links \
  -H "Authorization: Bearer capi_…" -H "Content-Type: application/json" \
  -d '{ "values": { "std:firstName": "Anna", "std:lastName": "Muster", "std:email": "anna@example.com" } }'
json
{ "url": "https://www.claritos.io/f/Zk3q…", "contactId": "cmf2contact00000000000001", "validUntil": "2026-11-03T10:00:00.000Z" }

8. Archivieren und zurückholen

POST /forms/{id}/archive und POST /forms/{id}/unarchive antworten 200 mit dem Formular; ein zweiter Aufruf ändert nichts. Ein archiviertes Formular nimmt keine Einsendungen an; Ersetzen, Einstellungen, Lizenz-Logo, Teilen-Link und persönlicher Link antworten 409 mit reason archived, bis es zurückgeholt ist.

Mit einem KI-Agenten

Über den API-MCP derselbe Weg: zuerst diesen Leitfaden, dann die Bausteine, den Probelauf so lange wiederholen, bis valid true ist, und erst dann anlegen. Für Änderungen am Baum ihn aus der Definition neu bauen und als Ganzes ersetzen; nur Einstellungen ändert forms.updateSettings, ohne Baum.

json
{ "name": "guide", "arguments": { "topic": "form-building" } }
{ "name": "call_endpoint", "arguments": { "id": "forms.buildingBlocks", "query": { "targetEntity": "PROSPECT" } } }
{ "name": "call_endpoint", "arguments": { "id": "forms.validate", "body": { … } } }
{ "name": "call_endpoint", "arguments": { "id": "forms.create", "body": { … }, "idempotencyKey": "…" } }