Die Dokumenten-API

Authentifizierung, der Erzeugungs-Endpunkt, das Eingabeformat, Fehlersemantik und was berechnet wird.

Die Dokumenten-API erzeugt Dokumente von außerhalb der App: Ihr System liefert Werte, Final Wording wendet sie auf die verwaltete Vorlage an. Sie ist bewusst klein – ein PDF rendern, ein Dokument aus einer Vorlage auflösen oder anlegen, und das Schema, das die Eingaben beschreibt.

Authentifizierung

Authorization: Bearer <workspace_api_token>

Ein Token gehört zu genau einem Arbeitsbereich. Behandeln Sie es als arbeitsbereichsweites Lese- und Render-Recht: Wer es hält, kann jede Dokumentversion dieses Arbeitsbereichs finden und rendern und beliebige Eingabewerte liefern. Bewahren Sie es entsprechend auf – es gehört in einen Secrets-Manager, nicht in ein Repository.

Administratoren des Arbeitsbereichs stellen Tokens unter Arbeitsbereichseinstellungen → API-Tokens aus: Erstellen Sie ein benanntes Token – benennen Sie es nach der Integration, die es verwenden wird – und kopieren Sie das Secret. Es wird genau einmal angezeigt und nur als Hash gespeichert. Dieselbe Liste zeigt zu jedem Token, wer es erstellt hat, wann es zuletzt verwendet wurde und was es erzeugt hat, und widerruft ein Token mit sofortiger Wirkung. Rotation ist gewollte Überlappung: Erst den Ersatz erstellen, dann die Aufrufer umstellen, dann das alte Token widerrufen – zwei aktive Tokens während des Wechsels sind vorgesehen.

Ein fehlendes, ungültiges oder widerrufenes Token ist ein 401; eine Version außerhalb des Arbeitsbereichs, zu dem das Token gehört, ein 404 – ununterscheidbar von einer Version, die es nicht gibt.

Ein PDF erzeugen

POST /api/v1/document-generations/pdf
Authorization: Bearer <workspace_api_token>
Idempotency-Key: <optional>
Content-Type: application/json

{
  "document_version_key": "<document_version_key>",
  "inputs": { "fields": { "customer_name": { "textValue": "Grace Hopper" } } }
}

Ein erfolgreicher Render liefert das PDF selbst:

200 OK
Content-Type: application/pdf
Content-Disposition: attachment; filename="<Dokumentname>.pdf"

inputs ist optional. Fehlt es, rendert die Version aus den auf ihr gespeicherten Werten. Ist es vorhanden, ersetzt es die gespeicherten Werte für genau diesen Render: Nichts wird ins Dokument zurückgeschrieben, und es gibt keine Teil-Zusammenführung – ein vorhandenes inputs-Objekt ist der vollständige Wertesatz, ein Pflichtfeld, das auf der Version gespeichert ist, muss also erneut mitgeliefert werden.

Das Eingabeformat

inputs ist ein Objekt mit einer einzigen fields-Map, deren Schlüssel die Feldnamen der Vorlage sind. Jeder Wert nennt seinen Typ explizit:

Feldtyp der Vorlage Wert
Text {"textValue": "…"}
Ja/Nein {"booleanValue": true}
Dezimalzahl {"decimalValue": "1200.50"} – exakt, als String
Datum {"dateValue": "2026-07-28"} – ISO-Datum, ohne Zone
Objekt {"objectValue": {"fields": { … }}}
Liste {"listValue": {"items": [ … ]}}
null (optional) {"nullValue": true}

Der maßgebliche Vertrag ist maschinenlesbar – erzeugen Sie Ihre Typen daraus, statt Feldnamen hart zu kodieren:

GET /api/v1/document-versions/{document_version_key}/input-schema
Authorization: Bearer <workspace_api_token>

liefert ein JSON Schema (Draft 2020-12), das exakt die Felder dieser Version beschreibt, einschließlich Pflichtfeldern und Typisierung.

Ein Dokument aus einer Vorlage anlegen

Zwei Endpunkte arbeiten mit einer angehefteten Vorlagenversion und natürlichen bindings – schlichte JSON-Werte je Feldname, ohne die typisierten Hüllen:

POST /api/v1/document-versions/{document_version_key}/resolve
POST /api/v1/document-versions/{document_version_key}/documents

resolve wendet Ihre Bindings an und meldet jede Diagnose, ohne etwas anzulegen – der Probelauf, gegen den eine Integration testet. documents legt ein gewöhnliches bearbeitbares Dokument (samt Erstversion) in einem ausdrücklich benannten Projekt an; project_key und ein nicht leerer name sind Pflicht, ebenso ein Idempotency-Key – denn doppelt anlegen ist schlimmer als doppelt rendern. Im angelegten Dokument sind Bedingungen und Wiederholungen vollständig ausmaterialisiert: Es ist von da an ein normales Dokument und durchläuft Kommentare, Freigaben und Erzeugung wie jedes andere.

Das Anlegen hat eine Grenze, die die Render-Endpunkte nicht haben: Der mit dem Token verknüpfte Benutzer muss im Zielprojekt aktuell mindestens Bearbeiter-Zugriff halten; fällt dieser Zugriff weg, schlägt das Anlegen mit 403 fehl – bewusst geschlossen.

Fehler bedeuten zweierlei

  • 400, 401, 404, 405 – die Anfrage selbst ist falsch: kaputtes JSON, unbekannte Felder, ein schlechtes Token, eine falsche URL. Korrigieren Sie den Aufrufer.
  • 422 Unprocessable Entity – die Anfrage war in Ordnung, aber das Dokument lässt sich mit diesen Werten nicht auflösen: ein fehlendes Pflichtfeld, ein missratenes Datum, ein Wert der falschen Art. Der Body listet jeden auffindbaren Fehler als Diagnose mit stabilem code und dem Pfad des betroffenen Werts. Ein 422 heißt „Eingaben oder Dokument korrigieren“, nie „erneut versuchen“ – dieselbe Anfrage scheitert genauso wieder.
  • 500 – ein unerwarteter Render-Fehler auf unserer Seite. Ein erneuter Versuch ist vernünftig.

Eine Feinheit, die man kennen sollte: Eine weggelassene optionale Liste ist eine gültige leere Sammlung – die Wiederholung erzeugt null Vorkommen. Ein explizites null ist eine andere Aussage und wird als Wiederholungsquelle abgelehnt.

Abrechnung und Wiederholungen

Berechnet wird ein Render erst nach Erfolg: einmal je erzeugtem Dokument plus einmal je gerenderter Seite, gezählt ab Seite eins. Ein gescheiterter Render – jedes 4xx oder 5xx – wird nie berechnet.

Senden Sie einen Idempotency-Key bei Anfragen, die Sie wiederholen könnten. Ein wiederholter Key rendert erneut und liefert das PDF wieder aus, erzeugt aber keine zweite Belastung – eine Timeout-und-Retry-Schleife kann nicht doppelt abrechnen.

Was die API bewusst nicht tut

  • Sie wartet nicht auf Freigaben. Eine Version mit offenen verlangten Freigaben rendert trotzdem – mit demselben DRAFT · UNAPPROVED-Aufdruck, den auch der Export in der App trüge.
  • Sie nimmt keine Auflösungs-Limits vom Aufrufer entgegen: Wiederholungstiefe, Listengrößen und Ausgabegröße begrenzt allein der Server; ein limits-Schlüssel wird als unbekannt abgelehnt. Der Request-Body ist auf 4 MiB begrenzt.
  • Rendern schreibt keine Werte ins Dokument zurück – gelieferte inputs ändern einen Render, nicht die Aufzeichnung. Ein Dokument anzulegen ist ein eigener, ausdrücklicher Endpunkt und nie Nebenwirkung des Renderns.

Eine Anfrage, die durchgehend funktioniert

curl -sS -X POST https://app.finalwording.com/api/v1/document-generations/pdf \
  -H "Authorization: Bearer $FW_API_TOKEN" \
  -H "Idempotency-Key: rechnung-2026-08-04-0001" \
  -H "Content-Type: application/json" \
  -d '{
    "document_version_key": "'"$VERSION_KEY"'",
    "inputs": { "fields": {
      "customer_name": { "textValue": "Grace Hopper" },
      "signed_on":     { "dateValue": "2026-07-28" },
      "fee":           { "decimalValue": "1200.50" }
    } }
  }' \
  -o vertrag.pdf

$VERSION_KEY benennt die freigegebene Dokumentversion, auf der Ihre Vorlage liegt; die Antwort in vertrag.pdf ist dasselbe PDF, das der App-Export dieser Version mit diesen Werten erzeugen würde.