Zum Hauptinhalt springen

API-Überblick

Die Formfeed-API ist eine REST-API mit JSON über HTTPS. Diese Seite beschreibt die Konventionen, die für jeden Endpunkt gelten; die API-Referenz enthält jedes Feld.

Basis-URL​

https://api-eu.formfeed.dev/v1

Nutzen Sie diesen Host. Formfeed rendert in Frankfurt und speichert Dateien in der EU, und die SDKs verwenden diesen Host standardmäßig. https://api.formfeed.dev/v1 und https://api-us.formfeed.dev/v1 erreichen heute dasselbe Gateway: Nicht der Host entscheidet, wo ein Dokument gerendert wird, sondern der Workspace Ihres Schlüssels, und die EU ist die einzige Region, die rendert. Siehe Datenresidenz.

Authentifizierung​

Senden Sie einen API-Schlüssel als Bearer-Token:

Authorization: Bearer ff_live_3f9c...

X-API-Key: ff_live_… funktioniert ebenfalls, für Clients, die Authorization nicht setzen können. Schlüssel gehören zu einem Workspace und tragen Scopes; ff_test_-Schlüssel rendern kostenlos. Siehe Authentifizierung.

Anfragen und Antworten​

  • Anfrage-Bodys sind JSON mit Content-Type: application/json. Feldnamen sind snake_case. Uploads (Dateien der Bibliothek, Word- und PowerPoint-Vorlagen, umzuwandelnde Dokumente) sind multipart/form-data. Ein Body, der kein gültiges JSON ist, dem ein Pflichtfeld fehlt oder der einen Wert vom falschen Typ enthält, ergibt 400 invalid_request, meist mit einer Liste issues, die zu jedem Problem den path und eine message nennt.
  • Antworten sind JSON. Fehler sind application/problem+json (RFC 9457) mit einem stabilen code; siehe Fehler.
  • Renderings, Batch-Jobs, Vorlagen und Dateien der Bibliothek haben IDs mit einem Typpräfix und 32 Hexadezimalzeichen: rnd_… (Rendering), job_… (Batch-Job), tpl_… (Vorlage), fil_… (Datei der Bibliothek). Webhook-Ereignisse haben IDs evt_…. Webhook-Endpunkte, Workspaces und Listen-Sitzungen haben einfache UUIDs. Überall, wo eine Rendering-, Job-, Vorlagen- oder Datei-ID erwartet wird, können Sie auch die einfache UUID senden, und eine Vorlage auch über ihren Slug (invoice-de).
  • Zeitstempel sind ISO 8601 in UTC. Je nach Feld stehen sie mit Z oder +00:00 und mit oder ohne Sekundenbruchteile (2026-09-11T10:00:00Z, 2026-09-11T10:00:00.4821+00:00). Lesen Sie sie deshalb mit einer Datumsbibliothek, statt Zeichenketten zu vergleichen.
  • Jede Antwort trägt eine X-Request-Id, jedes Problem denselben Wert als request_id. Geben Sie sie an, wenn Sie den Support kontaktieren.

Header​

Anfrage-Header, die die API liest:

HeaderWann
Authorization: Bearer <key>Jede Anfrage außer GET /v1/openapi.json und der Geräteanmeldung. Siehe Authentifizierung.
X-API-Key: <key>Statt Authorization. Werden beide gesendet, zählt Authorization.
Content-Typeapplication/json, oder multipart/form-data für Uploads.
Idempotency-KeyOptional bei jedem POST; siehe Idempotenz.
X-Formfeed-WorkspaceNur mit einem Sitzungstoken von Formfeed (die App oder ein über den MCP-Server verbundener KI-Agent): die ID des Workspace, für den der Aufruf gilt. Ein API-Schlüssel ignoriert ihn, weil er bereits zu einem Workspace gehört.
X-Formfeed-Environment: testNur die eigene Testanfrage der App (POST /renders mit der Sitzung eines Mitglieds). Ein API-Schlüssel ignoriert ihn: Seine Umgebung ist sein Präfix.

Antwort-Header, die die API schreibt:

HeaderBei
X-Request-IdJeder Antwort.
Idempotent-Replayed: trueEiner gespeicherten Antwort auf einen wiederholten Idempotency-Key.
RateLimit-Limit, RateLimit-Remaining, RateLimit-ResetAnfragen, die gegen das Rate-Limit zählen.
Retry-AfterSekunden, die Sie warten sollen: bei 429, bei 503 und bei jedem 202.
LocationJedem 202: das Ergebnis des Renderings bei einem Rendering, /v1/jobs/{id} bei einem Batch.
X-Units-Charged, X-Units-Remaining, X-Render-Region, X-Render-Engine, X-Render-Deduplicated, Server-TimingPOST /renders und den PDF-Tools; siehe das Rendering-Objekt.
X-Render-Async-Fallback: trueEinem synchronen Rendering, das in den Hintergrund gewandert ist (der automatische Fallback).
X-Render-Id, X-Render-Pages, X-Render-Expires-At, X-Formfeed-PDF-UAEiner Antwort, die die Datei selbst ist (?output=binary).
WWW-AuthenticateEinem 401, weil kein Schlüssel gesendet wurde.

Paginierung​

GET /renders, GET /templates und GET /files liefern eine Seite und einen Cursor:

{
"data": [ … ],
"next_cursor": "2026-09-11 10:00:00.4821+00|0f3c9a2e-7b1d-4e5f-8a6c-3b2d1e0f9a8b"
}

Übergeben Sie next_cursor als ?cursor=, um die nächste Seite zu erhalten; auf der letzten Seite ist er null. Behandeln Sie ihn als undurchsichtige Zeichenkette und kodieren Sie ihn für die URL, denn er enthält Leerzeichen und andere Zeichen, die eine URL nicht erlaubt. limit setzt die Seitengröße: 1–100 für Renderings und Dateien (ein größerer Wert ergibt 400 invalid_request), 1–200 für Vorlagen (ein größerer Wert wird auf 200 gekürzt), standardmäßig 50. Die all()-Helfer der SDKs (renders.all(), templates.all(), files.all()) folgen dem Cursor für Sie.

curl "https://api-eu.formfeed.dev/v1/renders?status=failed&limit=100" -H "Authorization: Bearer $FORMFEED_API_KEY"

GET /renders nimmt die Filter template (Slug, tpl_…-ID oder UUID), status (queued, rendering, succeeded, failed), environment (live, test), since und until (ISO-8601-Zeitpunkte) und liefert die neuesten zuerst. Webhook-Endpunkte, geteilte Partials, Versionen und Channels einer Vorlage kommen als eine Liste in data, ohne Cursor.

Idempotenz​

Netzwerke fallen aus. Senden Sie bei jedem POST einen Idempotency-Key-Header und wiederholen Sie die Anfrage mit demselben Schlüssel, wenn Sie nicht wissen, ob der erste Versuch angekommen ist:

curl https://api-eu.formfeed.dev/v1/renders \
-H "Authorization: Bearer $FORMFEED_API_KEY" \
-H "Idempotency-Key: order-8812-invoice" \
-H "Content-Type: application/json" \
-d '{ "template": "invoice", "data": { … } }'

Die erste JSON-Antwort zu einem Schlüssel wird pro Workspace 24 Stunden gespeichert. Eine Wiederholung mit demselben Schlüssel liefert diese Antwort erneut, mit ihrem Statuscode, ihrem Body und Idempotent-Replayed: true, ohne ein zweites Mal zu rendern oder abzurechnen. Idempotenz-Schlüssel sind bis zu 128 Zeichen lang. Verwenden Sie etwas, das an Ihren eigenen Datensatz gebunden ist (order-8812-invoice), oder eine UUID. Die SDKs senden bei jedem Aufruf, der ein Rendering oder einen Batch anlegt, einen neuen.

Was Sie wissen sollten, bevor Sie sich darauf verlassen:

  • Der Schlüssel ist nicht an die Anfrage gebunden. Ein Schlüssel, der innerhalb von 24 Stunden erneut verwendet wird, liefert die erste Antwort, auch für einen anderen Body oder einen anderen Endpunkt. Verwenden Sie für jede neue Anfrage einen neuen Schlüssel.
  • Fehler unter 500 werden ebenfalls gespeichert. Eine Anfrage, die mit 402 quota_exceeded oder 422 beantwortet wurde, erhält mit demselben Schlüssel 24 Stunden lang dieselbe Antwort. Haben Sie die Ursache behoben, senden Sie sie mit einem neuen Schlüssel. Serverfehler (5xx) und 429 rate_limited werden nicht gespeichert, eine Wiederholung versucht es also wirklich erneut.
  • Die Antwort wird gespeichert, wenn die erste Anfrage beantwortet ist. Eine Wiederholung, die gesendet wird, während der erste Versuch noch läuft, wird nicht erkannt und rendert ein zweites Mal. Warten Sie auf eine Antwort oder auf das Timeout Ihres Clients, bevor Sie wiederholen; ein synchrones Rendering kann bis zu 60 Sekunden dauern, bevor es mit 202 antwortet.
  • Eine Wiederholung ist die Antwort, wie sie war. Ein gespeichertes 202 zeigt das Rendering als queued; den aktuellen Stand liefert GET /renders/{id}. Eine Wiederholung trägt auch die Header, die die erste Antwort beschreiben: Location und Retry-After eines 202, X-Units-* und X-Render-*. X-Units-Charged nennt, was die erste Antwort gekostet hat; die Wiederholung selbst kostet nichts.
  • Dateien werden nicht wiederholt. Eine Antwort, die die Datei selbst war (?output=binary), wird nicht gespeichert, eine Wiederholung rendert also erneut. Ein Live-Rendering mit demselben Inhalt kommt dann meist kostenlos aus dem Dedup-Cache.

Wiederholungen​

Wiederholen Sie 429 und 503 nach der Anzahl Sekunden in Retry-After und Netzwerkfehler mit exponentiellem Backoff, immer mit demselben Idempotenz-Schlüssel. Wiederholen Sie andere 4xx-Antworten nicht: Sie beschreiben ein Problem mit der Anfrage, das eine Wiederholung nicht behebt. Die SDKs setzen genau diese Regel um (standardmäßig bis zu 3 Wiederholungen, mit der Wartezeit aus Retry-After, sonst 0,5 s und dann jeweils doppelt so lange, höchstens 30 s).

Aus dem Browser​

API-Schlüssel gehören auf einen Server. Die API lehnt einen Schlüssel ab, der von einer Webseite kommt: Eine Anfrage mit einem Origin-Header ergibt 403 forbidden („This API key may not be used from a browser origin“). Rufen Sie Formfeed aus Ihrem Backend auf und geben Sie dem Browser das Ergebnis, zum Beispiel die download_url des Renderings. Die einzige Ausnahme sind die Maker-Portale der Power Platform von Microsoft (Power Automate, Power Apps, Copilot Studio, Logic Apps im Azure-Portal), deren Connector-Proxy den Origin des Portals weitergibt; siehe Power Automate.

Versionierung​

Die API-Version steht im Pfad (/v1). Innerhalb einer Version sind Änderungen additiv: neue Endpunkte, neue optionale Anfragefelder, neue Antwortfelder, neue Fehlercodes und neue Ereignistypen. Schreiben Sie Clients, die unbekannte Felder ignorieren und einen unbekannten code wie seinen HTTP-Status behandeln. Inkompatible Änderungen erscheinen nur in einer neuen Version. In /v1 ist nichts abgekündigt; eine Abkündigung würde im Änderungsprotokoll angekündigt, bevor sich etwas ändert.

Das OpenAPI-3-Dokument liegt unter https://api.formfeed.dev/v1/openapi.json (ohne Schlüssel abrufbar). Erzeugen Sie daraus einen Client, importieren Sie es in Postman oder Insomnia oder richten Sie Ihre API-Tools darauf aus.

Endpunkte​

Alle Pfade unten beginnen mit /v1. „Scope“ ist, was ein API-Schlüssel braucht (Scopes); ein Schlüssel ohne ihn erhält 403 forbidden.

EndpunktWas er tutScope
POST /rendersEine Vorlage, HTML oder eine URL rendernrender:create
GET /renders, GET /renders/{id}Renderings auflisten, eines abrufen (mit frischem Download-Link)render:read
GET /renders/{id}/resultDas Ergebnis, auf das ein 202 zeigt: 202, solange das Rendering läuft, dann das Rendering, seine Datei oder sein Problemrender:read
GET /renders/{id}/inputDie aufbewahrte Anfrage eines Renderings, um es nachzustellenrender:input
DELETE /renders/{id}/outputsDie gespeicherten Dateien eines Renderings löschenfile:delete
POST /renders/batch, GET /jobs/{id}Bis zu 1.000 Renderings als Batch senden, den Job verfolgenrender:create, render:read
POST /pdf/merge, /pdf/protect, /pdf/watermarkPDF-Renderings nachbearbeiten: zusammenführen, Passwort, Wasserzeichenrender:create
POST /pdf/convertEine Word-, Excel-, PowerPoint-, OpenDocument-, RTF- oder HTML-Datei oder die Ausgabe einer Office-Vorlage in ein PDF-Rendering umwandelnrender:create
POST /pdf/infoSeitenzahl, Seitengrößen und Metadaten eines PDF-Renderingsrender:read
GET /templates, GET /templates/{id}Vorlagen auflisten und lesentemplate:read
GET /templates/{id}/versions[/{which}], GET /templates/{id}/schemaVersionen mit ihren Dateien, das Datenschematemplate:read
GET /templates/{id}/versions/{which}/fileDas Dokument einer Version einer Word- oder PowerPoint-Vorlage herunterladentemplate:read
POST /templates/{id}/validateEine Version mit Daten prüfen, ohne zu rendern, kostenlostemplate:read
POST /templates, PUT/DELETE /templates/{id}Vorlagen anlegen, ändern und archivierentemplate:write
POST /templates/{id}/versions, POST …/versions/{n}/publishVersionen anlegen und veröffentlichentemplate:write
GET /templates/{id}/channelsDie Release Channels der Vorlagetemplate:read
PUT/DELETE /templates/{id}/channels/{name}, POST …/channels/{name}/promote, POST …/rollbackRelease Channels verschieben, löschen, befördern und zurücksetzentemplate:write
GET /files, GET /files/{id}Die Dateibibliothek des Workspace auflisten und lesenfile:read
POST /files, DELETE /files/{id}In die Dateibibliothek hochladen und daraus löschenfile:write, file:delete
GET /brand, GET /partials, GET /partials/{name}Brand Kit und geteilte Partials der Organisation lesentemplate:read
PUT/DELETE /partials/{name}Geteilte Partials anlegen, ändern und löschentemplate:write
GET/POST /webhooks, PUT/DELETE /webhooks/{id}, POST /webhooks/{id}/testWebhook-Endpunkte verwaltenwebhook:manage
POST /webhooks/listen, DELETE /webhooks/listen/{id}Eine Listen-Sitzung starten und beendenwebhook:listen
POST /webhooks/events/{id}/resendEin gespeichertes Ereignis erneut an einen Endpunkt oder eine Listen-Sitzung sendenwebhook:manage oder webhook:listen
GET /account, GET /usageWorkspace, Plan und Verbrauchaccount:read
DELETE /workspaces/{id}Einen Workspace der Organisation löschen; sein Inhalt wird nach 30 Tagen entferntworkspace:delete
GET/PUT/DELETE /workspaces/{id}/storage, POST …/storage/test, POST …/storage/redeliverDen eigenen Bucket des Workspace verwalten und fehlgeschlagene Kopien erneut sendenkeiner: die Sitzung eines Inhabers oder Admins in der App
POST /auth/device, POST /auth/device/tokenDie Geräteanmeldung von formfeed loginkeiner: noch kein Schlüssel
POST /compat/apitemplate/*Kompatibilität mit apitemplate.iorender:create

SDKs und Tools​

  • TypeScript-SDK: npm install @formfeed/sdk, für Node, Deno, Bun und Cloudflare Workers, auf der Serverseite (nicht aus dem Browser).
  • Python-SDK: pip install formfeed, synchron und mit asyncio.
  • CLI: npx formfeed, Vorlagen als Code und Renderings aus dem Terminal.
  • MCP-Server: npx @formfeed/mcp oder der gehostete Endpunkt https://mcp.formfeed.dev, für KI-Agenten.