Zum Hauptinhalt springen

Rendering

POST /v1/renders ist der Endpunkt, den Sie am häufigsten aufrufen. Er nimmt eine Quelle, Daten und Optionen entgegen und liefert ein Rendering mit Download-Link. Er braucht den Scope render:create. Die API-Referenz listet jedes Feld; diese Seite erklärt, wie sie zusammenspielen.

Die Felder der Anfrage im Überblick​

FeldTypStandardBedeutung
templateString–Eine gespeicherte Vorlage: ihr Slug, ihre tpl_…-ID oder UUID. Senden Sie genau eines von template, html und url (Quellen).
htmlString–Ad-hoc-HTML, höchstens 5 MB.
urlString–Eine öffentliche https://-Seite, höchstens 2.048 Zeichen.
enginejinja2, liquid, handlebars oder nullnullDie Vorlagensprache von html. null druckt das HTML unverändert.
versionStringpublishedWelche Version von template rendert: published, ein Channel-Name, latest (nur Test-Schlüssel) oder eine Versionsnummer (welche Version).
dataObjekt{}Die Daten, mit denen die Vorlage gefüllt wird.
outputpdf, png, jpg, webp, docx, pptxdas eigene Format der QuelleDas Dateiformat (Ausgabeformat).
settingsObjekt–Überschreibt die Einstellungen der Vorlage für diese Anfrage (Einstellungen überschreiben).
localeStringdie der VorlageKurzform für settings.locale, höchstens 20 Zeichen, etwa de-DE.
validate_dataBooleanfalsedata vor dem Rendern gegen das gespeicherte Schema der Vorlage prüfen (Daten prüfen).
filenameStringdocument.<format>Der Name der Ausgabedatei, höchstens 200 Zeichen (Dateiname).
accesssigned oder publicdie Einstellung Ausgabe-URLs des Workspace, signed, wenn niemand sie geändert hatArt des Download-Links (Download-Links).
expires_inGanzzahl (Sekunden)die AufbewahrungsfristDie Ausgabe früher löschen, als die Aufbewahrungsfrist vorsieht. Kann sie nur verkürzen.
modesync oder asyncsyncAuf das Dokument warten oder sofort 202 erhalten (synchron oder asynchron).
webhook_urlString–Eine öffentliche https://-Adresse, die die Ereignisse dieses Renderings zusätzlich erhält (Webhook-URLs pro Rendering).
webhook_secretString, 16–200 Zeichen–Signiert die Zustellungen an webhook_url; ohne ihn sind sie unsigniert. Braucht webhook_url und wird nie zurückgegeben.
metaObjekt{}Ihre eigenen Felder, zurückgegeben im Rendering und seinen Webhooks (Metadaten).
dedupeBooleantruefalse rendert auch dann, wenn ein identisches Rendering wiederverwendet werden könnte (Deduplizierung).
canary_keyString, 1–200 Zeichen–Entscheidet, auf welcher Seite des Canary eines Channels ein Rendering landet (wer den Canary bekommt).
postObjekt–Schritte für Zusammenführen, Wasserzeichen, Passwort und E-Rechnung am PDF (Post-Processing).
storagefalse oder { "key": "…" }–Die Kopie im eigenen Bucket des Workspace.
regioneu, us oder apacdie Region des WorkspaceLassen Sie es weg. Heute rendert nur die EU-Region (Datenresidenz).

Quelle wählen​

Jede Anfrage enthält genau eines der Felder template, html oder url; keines oder zwei ergeben 400 invalid_request.

Der Normalfall: die veröffentlichte Version der Vorlage plus Ihre Daten.

{
"template": "invoice",
"data": { "invoice": { "number": "2026-0042", "lines": [ … ] } }
}

template ist der Slug, die tpl_…-ID oder die UUID. Die API rendert die veröffentlichte Version, sofern version keine andere wählt; eine Vorlage ohne veröffentlichte Version antwortet mit 404 template_not_found. Die Vorlage sieht das Brand Kit der Organisation als brand und kann geteilte Partials einbinden.

Ausgabeformat​

output ist pdf, png, jpg, webp, docx oder pptx. Welche Werte ein Rendering anfordern darf, hängt von seiner Quelle ab:

QuelleoutputOhne output
Vorlage der Art pdfpdf, png, jpg, webppdf
Vorlage der Art imagepdf, png, jpg, webpihr settings.image.format, PNG, wenn keines gesetzt ist
Word-Vorlage (Art docx)docx, pdfihr settings.office.output, sonst docx
PowerPoint-Vorlage (Art pptx)pptx, pdfihr settings.office.output, sonst pptx
Ad-hoc-html, urlpdf, png, jpg, webppdf

Jede andere Kombination beantwortet die API mit 400 invalid_request und den erlaubten Werten in allowed. Bei Bildern kommt der Viewport aus settings.image (Referenz der Einstellungen); eine Vorlage der Art image bringt ihn bereits mit. Ohne Einstellungen ist ein Bild der Viewport von 1200 × 630 px mit dem Skalierungsfaktor 2, die Datei misst also 2400 × 1260 Pixel.

{
"template": "social-card",
"data": { "title": "Release 1.4" },
"output": "png"
}

Word- und PowerPoint-Vorlagen rendern mit einem Live-Schlüssel ab dem Starter-Plan; im Free-Plan beantwortet die API ein Live-Rendering mit 403 forbidden und feature: "office". Im Free-Plan erzeugen ihre Test-Renderings nur das PDF mit der Testmarkierung: Ein Test-Rendering, das docx oder pptx anfordert oder output weglässt, sodass das eigene Format der Vorlage gilt, beantwortet die API mit 403 test_output_unavailable. Senden Sie dort mit einem Test-Schlüssel "output": "pdf".

Ein PDF mit mehr Seiten als das Seitenlimit des Plans schlägt mit 422 page_limit_exceeded fehl, eine Datei über dem Größenlimit des Plans mit 422 output_too_large; siehe Plan-Limits. Beides kostet nichts.

Die Datei statt des JSON​

POST /v1/renders?output=binary antwortet mit dem Dokument selbst: den Bytes, Content-Type und Content-Disposition: attachment mit dem Dateinamen, dazu ID, Seitenzahl und Ablauf des Renderings in X-Render-Id, X-Render-Pages und X-Render-Expires-At sowie den üblichen Nutzungs-Headern. Praktisch für ein Shell-Skript oder einen Proxy, der die Datei direkt weiterreicht:

curl -X POST "https://api-eu.formfeed.dev/v1/renders?output=binary" \
-H "Authorization: Bearer $FORMFEED_API_KEY" -H "Content-Type: application/json" \
-d '{ "template": "invoice-de", "data": { "invoice": { "number": "2026-0042" } } }' \
-o rechnung.pdf

Ein Rendering, das in den Hintergrund wandert, antwortet unabhängig vom Query-Parameter mit 202 und dem JSON-Rendering, weil es noch keine Datei gibt. Sein Location fragt erneut nach der Datei: GET /renders/{id}/result?output=binary antwortet mit 202, bis das Rendering fertig ist, und dann mit der Datei. Eine Antwort mit der Datei wird für idempotente Wiederholungen nicht gespeichert.

Welche Version rendert​

Standardmäßig rendert die veröffentlichte Version. version nimmt auch eine Versionsnummer, und latest rendert die neueste Version, ob veröffentlicht oder nicht (meist den Entwurf, an dem der Editor arbeitet) – das darf nur ein Test-Schlüssel (ein Live-Schlüssel erhält 400 invalid_request). So probieren Sie eine Änderung vor der Veröffentlichung aus:

{
"template": "invoice-de",
"version": "latest",
"data": { "invoice": { "number": "2026-0042" } }
}

version nimmt auch den Namen eines Release Channels wie staging, den Live- und Test-Schlüssel rendern dürfen. Ein Channel ist ein Zeiger, den jemand bewusst auf eine Version verschiebt; eine Staging-Umgebung rendert so, was für sie freigegeben wurde, statt jedem Speichern im Editor zu folgen. Release Channels gibt es ab dem Starter-Plan; im Free-Plan ergibt ein Channel-Name 403 forbidden mit feature: "channels".

{
"template": "invoice-de",
"version": "staging",
"data": { "invoice": { "number": "2026-0042" } }
}

Die veröffentlichte Version und Channels werden bis zu einer Minute zwischengespeichert: Eine neu veröffentlichte Version oder ein verschobener Channel rendert innerhalb dieser Minute. Eine Versionsnummer oder latest wird nie zwischengespeichert. Ein Channel mit Canary schickt seinen Anteil der Renderings an die Canary-Version, gewählt anhand von canary_key, wenn die Anfrage einen sendet, sonst anhand des Idempotency-Key, sonst zufällig (wer den Canary bekommt). Ein Channel, den die Vorlage nicht hat, antwortet mit 404 channel_not_found, eine Versionsnummer, die sie nicht hat, mit 404 template_not_found.

Einstellungen pro Anfrage überschreiben​

settings wird per Deep Merge über die Einstellungen der Vorlage gelegt, Sie senden also nur, was abweicht. null entfernt einen Abschnitt:

{ "template": "invoice", "data": { … }, "settings": { "paper": { "format": "Letter" }, "footer": null } }

locale ist eine Kurzform für settings.locale: Es stellt die Formatierung von Datum, Zahlen und Währungen sowie das Übersetzungswörterbuch für dieses eine Dokument um.

{ "template": "invoice", "data": { … }, "locale": "fr-FR" }

settings.pdf.ua macht die Datei zu einem PDF/UA-1-Dokument: einem getaggten PDF, das veraPDF vor der Auslieferung gegen die maschinell prüfbaren Regeln von PDF/UA-1 prüft. Das Rendering trägt dann das Urteil des Validators als accessibility.

{ "template": "notice", "data": { … }, "settings": { "pdf": { "ua": true } } }

Die Daten zuerst prüfen​

Formfeed prüft Ihre Daten nur, wenn Sie darum bitten. Mit "validate_data": true wird data gegen das gespeicherte Datenschema der Vorlage geprüft, bevor etwas gerendert oder gezählt wird:

{
"template": "invoice",
"data": { "invoice": { "number": 42 } },
"validate_data": true
}
  • Passen die Daten nicht, antwortet die API mit 422 data_validation_error; errors listet jede Abweichung mit ihrem path und ihrer message. Es wird nichts gerendert und nichts berechnet.
  • Eine Vorlage ohne gespeichertes Schema und eine html- oder url-Quelle ergeben 400 invalid_request: Es gibt nichts, wogegen geprüft werden könnte.
  • Ohne validate_data rendert ein fehlendes Feld einfach leer. Um eine Vorlage mit Daten zu prüfen, ohne zu rendern, nutzen Sie POST /templates/{id}/validate.

Dateiname​

filename benennt die Ausgabedatei. Der Name steht am Ende der Download-URL und im Header Content-Disposition, Browser speichern die Datei also unter diesem Namen. Ein PDF oder eine Office-Datei wird heruntergeladen, statt im Tab geöffnet zu werden; ein Bild (PNG, JPG, WebP) öffnet sich im Tab, wo ein og:image oder ein angeklickter Link gezeigt wird, und wird unter seinem Namen gespeichert. Mit dem Abfrageparameter download=1 am Link eines Bildes wird es stattdessen heruntergeladen.

{
"template": "invoice",
"data": { "invoice": { "number": "2026-0042" } },
"filename": "rechnung-2026-0042.pdf"
}

Der Name wird verwendet, wie Sie ihn senden, nicht als Vorlage: Bauen Sie ihn in Ihrem Code zusammen. Zeichen außer den Buchstaben A–Z und a–z, Ziffern, _ . - ( ) und Leerzeichen werden zu _ (aus Rechnung März.pdf wird Rechnung M_rz.pdf, aus rechnung-{{ invoice.number }}.pdf wird rechnung-_ invoice.number _.pdf), der Name wird auf 150 Zeichen gekürzt, und die Endung der Ausgabe wird angehängt, wenn sie fehlt. Ohne filename heißt die Datei document.pdf, document.png und so weiter.

Ein erfolgreiches Rendering hat eine download_url:

  • Signierte Links ("access": "signed") tragen ein Ablaufdatum und eine Signatur. Sie gelten höchstens sieben Tage und nie länger, als die Datei selbst aufbewahrt wird. GET /renders/{id} stellt jederzeit einen frischen Link aus. Speichern Sie also die ID des Renderings, nicht die URL.
  • Öffentliche Links ("access": "public") haben keine Signatur und funktionieren, bis die Datei gelöscht wird. Verwenden Sie sie nur für Dokumente, die nicht vertraulich sind.
  • Ohne access gilt die Einstellung des Workspace unter Einstellungen → Ausgabe-URLs, die Signiert ist, sofern kein Admin sie geändert hat.
  • Aufbewahrung: Der Plan legt fest, wie lange Ausgaben aufbewahrt werden: 1 Tag im Free-Plan, 30 Tage im Starter-Plan, 365 Tage im Pro-Plan und ohne Limit im Business-Plan. Ein Admin kann die Frist für einen Workspace unter Einstellungen → Aufbewahrung der Ausgaben (Tage) verkürzen, und expires_in (Sekunden) verkürzt sie für ein einzelnes Rendering. Verlängern lässt sie sich mit keinem von beiden: Eine längere Frist in den Einstellungen des Workspace gilt als die des Plans, und ein längeres expires_in wird auf die Aufbewahrungsfrist gekürzt. Ausgaben von Test-Renderings werden spätestens nach 24 Stunden gelöscht, gleich welcher Plan.
  • Vorzeitiges Löschen: DELETE /renders/{id}/outputs entfernt die Dateien sofort (Scope file:delete). Die Metadaten des Renderings bleiben im Protokoll, mit download_url: null.

Wenn Sie Dokumente selbst archivieren, bewahren Sie sie in Ihrem eigenen Speicher auf. Verlassen Sie sich nicht auf Formfeed als Archiv. Eigener Speicher (ab dem Pro-Plan) kopiert jedes Dokument beim Rendern in Ihren Bucket bei Amazon S3, Cloudflare R2 oder einem S3-kompatiblen Dienst; ohne ihn laden Sie jede Datei einmal herunter und legen sie selbst ab.

Synchron oder asynchron​

Standardmäßig wartet die Anfrage auf das Dokument (synchron) und antwortet mit 200 und dem fertigen Rendering.

Setzen Sie "mode": "async", um sofort 202 Accepted mit dem Rendering im Status queued zu erhalten. Das Ergebnis erfahren Sie dann über einen Webhook oder durch Abfragen von GET /renders/{id}. Verwenden Sie den asynchronen Modus für lange Dokumente, für alles, was eine Warteschlange speist, und immer dann, wenn der Aufrufer nicht warten soll.

Eine synchrone Anfrage wartet höchstens eine Minute. Zuerst braucht sie einen freien Render-Slot: Sind alle Slots belegt, wartet sie bis zu 30 Sekunden auf einen und geht dann in die Warteschlange. Eine synchrone Anfrage, die innerhalb von 30 Sekunden keinen Slot gefunden hat oder nach 60 Sekunden keine Antwort hat, wird nicht abgebrochen. Sie wird zu einem asynchronen Rendering: Die Antwort ist 202 mit X-Render-Async-Fallback: true, einem Location-Header und Retry-After: 5, und das Rendering wird im Hintergrund fertig. Ihr Code sollte daher auch dann mit einem 202 umgehen können, wenn er synchron angefragt hat: Folgen Sie Location, oder rufen Sie waitFor der SDKs (wait_for in Python) mit der ID des Renderings auf. Siehe der automatische Fallback.

Mehr dazu, einschließlich Batches mit bis zu 1.000 Dokumenten, unter Asynchron und Batch.

Wie lange ein Rendering dauern darf​

SchrittLimitWenn es überschritten wird
Die Vorlage mit den Daten füllen5 Sekunden, höchstens 20 MB HTML504 render_timeout, bei der Größe 422 output_too_large
Das Dokument in Chromium laden, mit allem, was es lädt, bis keine seiner Anfragen mehr läuft (wie, sagt waitFor.networkIdle)30 Sekunden504 render_timeout; bei einem url-Rendering 422 navigation_failed
Auf settings.waitFor.selector wartenwaitFor.timeoutMs, standardmäßig 5 Sekunden, höchstens 30504 render_timeout
settings.waitFor.delayMshöchstens 10 Sekundenein längerer Wert wird auf 10 Sekunden gekürzt
Das PDF drucken120 Sekunden504 render_timeout

Diese Limits gelten pro Rendering, nicht pro Anfrage: Eine synchrone Anfrage, die insgesamt länger als 60 Sekunden läuft, wandert in den Hintergrund, und das Rendering läuft weiter. Die waitFor-Einstellungen beschreibt Auf Inhalte warten.

Post-Processing​

post führt PDF-Schritte am fertigen Dokument aus, als Teil desselben Renderings: andere PDFs anhängen, ein Wasserzeichen aufbringen, ein Passwort setzen, eine E-Rechnung erzeugen.

{
"template": "contract",
"data": { … },
"post": {
"merge_after": ["rnd_0f3c9a2e7b1d4e5f8a6c3b2d1e0f9a8b", "terms.pdf"],
"watermark": { "text": "DRAFT", "opacity": 0.15 },
"password": { "user": "open-sesame", "owner": "only-for-us", "permissions": ["print"] }
}
}
SchrittOptionen
merge_after1 bis 49 PDFs, die in dieser Reihenfolge hinter dieses Dokument gehängt werden: IDs (rnd_…) erfolgreicher PDF-Renderings im selben Workspace, deren Datei noch existiert, oder PDFs aus der Dateibibliothek über ihren Namen (Quellen). Die angehängten Seiten werden nicht noch einmal berechnet, page_count zählt sie aber mit.
watermarktext (1–200 Zeichen) oder image (ein PNG oder JPEG aus der Dateibibliothek, über seinen Namen), opacity (0–1, Standard 0.2), rotation (Grad im Uhrzeigersinn, −360 bis 360; Standard -45 für Text, von unten links nach oben rechts, und 0 für ein Bild), color (Hex wie #cc0000, nur für Text). Text wird in Noto Sans Bold gesetzt, die in die Datei eingebettet wird und lateinische, griechische und kyrillische Buchstaben abdeckt; ein Text mit anderen Zeichen wird mit 400 invalid_request abgelehnt. Ein Bild wird so skaliert, dass es in 60 % der Seite passt.
passworduser (zum Öffnen nötig, 1–127 Zeichen), owner (zum Aufheben der Einschränkungen nötig, 1–127 Zeichen; fehlt es, wird ein zufälliges gesetzt, sodass niemand sie aufheben kann), permissions: was ein Leser ohne Owner-Passwort darf, aus print, copy, modify, annotate. Fehlt es, sind alle vier erlaubt; [] erlaubt nichts. Senden Sie mindestens eines der drei. AES-256.
einvoiceMacht aus dem PDF eine ZUGFeRD- oder Factur-X-E-Rechnung, gebaut aus dem _invoice-Block in data: profile, flavour, xml, display_check, alle optional, oder false, um abzuschalten, was die Vorlage festlegt.
  • Die Schritte laufen in der Reihenfolge Zusammenführen, Wasserzeichen, Passwort, egal in welcher Reihenfolge Sie sie schreiben.
  • Zusammenführen, Wasserzeichen und Passwort kosten mit Live-Schlüsseln je 0,5 Einheiten, mit Test-Schlüsseln nichts. Sie setzen den Starter-Plan oder höher voraus; im Free-Plan antwortet die API mit 403 forbidden und feature: "post_processing".
  • einvoice ist ein eigener Schritt: Er läuft zuletzt, kostet mit Live-Schlüsseln 1 Einheit und lässt sich nicht mit password kombinieren (PDF/A verbietet Verschlüsselung). Live-E-Rechnungen setzen den Starter-Plan voraus (im Free-Plan 403 mit feature: "einvoice"); Test-Renderings erzeugen in jedem Plan eine begrenzte Zahl pro Tag, immer mit der Testmarkierung (Pläne und Einheiten). Mit settings.pdf.ua ist dieselbe Datei zugleich ein PDF/UA-1-Dokument, und das Rendering trägt beide Berichte.
  • Post-Processing funktioniert nur bei PDF-Ausgabe; jedes andere output ergibt 400 invalid_request. Unbekannte Schlüssel in post werden mit 400 abgelehnt, damit ein Tippfehler nie stillschweigend ein ungeschütztes PDF erzeugt.
  • Nachbearbeitete Renderings werden nicht dedupliziert.

Um Dokumente zu bearbeiten, die Sie bereits gerendert haben, nutzen Sie die Endpunkte der PDF-Tools: POST /v1/pdf/merge, /pdf/protect und /pdf/watermark liefern ein neues Rendering, und POST /v1/pdf/info liefert kostenlos Seitenzahl, Seitengrößen und Metadaten eines PDF-Renderings. Siehe PDF-Tools.

Metadaten​

meta ist ein frei gestaltbares Objekt, das Formfeed mit dem Rendering speichert und in der Antwort, in GET /renders/{id} und in jedem Webhook zu diesem Rendering zurückgibt. Legen Sie dort Ihre eigenen Kennungen ab, damit ein Webhook-Handler weiß, zu welchem Datensatz das Dokument gehört:

{ "template": "invoice", "data": { … }, "mode": "async", "meta": { "order_id": "8812", "tenant": "fennlor" } }

Deduplizierung​

Ein Live-Rendering wird aus einem früheren beantwortet, statt erneut zu rendern, wenn derselbe Workspace in den letzten 24 Stunden dasselbe gerendert hat und diese Datei noch existiert. „Dasselbe“ heißt: dieselbe Vorlagenversion (oder dasselbe html), Engine, Daten, Ausgabe, Einstellungen, Locale, Region und derselbe Zugriff, mit denselben Versionen von Brand Kit und geteilten Partials. Eine solche Antwort kostet 0 Einheiten, kommt in Millisekunden an und ist mit "deduplicated": true und dem Header X-Render-Deduplicated: true markiert.

Die Antwort ist das frühere Rendering: seine id, seine Datei und damit sein Dateiname und sein expires_at, mit dem meta Ihrer Anfrage und template: null. Im Render-Protokoll erscheint kein neuer Eintrag. filename, expires_in, meta und mode werden nicht verglichen; eine Anfrage, die sich nur darin unterscheidet, erhält also die frühere Datei unter ihrem früheren Namen.

Das ist meist gewünscht: Wer zweimal auf „Rechnung herunterladen“ klickt, erhält kostenlos dieselbe Datei. Senden Sie "dedupe": false, wenn jede Anfrage eine neue Datei erzeugen muss. Nie dedupliziert werden: Test-Renderings, url-Quellen, Renderings mit post (E-Rechnungen eingeschlossen), Einträge eines Batches und ein Rendering, das sein eigenes Objekt im Bucket des Workspace nennt (storage.key, siehe Eigener Speicher).

Deduplizierung ist nicht dasselbe wie Idempotenz: Die Deduplizierung vergleicht den Inhalt zweier Anfragen, die Idempotenz den Schlüssel, den Sie senden. Verwenden Sie Idempotenz-Schlüssel in jedem Fall, damit Wiederholungen sicher sind.

Das Rendering-Objekt​

{
"id": "rnd_0f3c9a2e7b1d4e5f8a6c3b2d1e0f9a8b",
"status": "succeeded",
"output": "pdf",
"download_url": "https://cdn.formfeed.dev/o/eu/…/invoice-2026-0042.pdf?exp=…&sig=…",
"expires_at": "2026-09-18T10:00:01.000Z",
"bytes": 48213,
"page_count": 2,
"units": 1,
"region": "eu",
"environment": "live",
"template": {
"id": "tpl_7c1e4b9a2d5f4c8e9b3a6d0f2e8c4a1b",
"slug": "invoice",
"version": 7,
"channel": "published",
"canary": false
},
"engine_version": "chromium 140.0.7339.16; engine jinja2",
"template_checksum": "9f2c…",
"output_sha256": "b41e…",
"deduplicated": false,
"storage": null,
"einvoice": null,
"accessibility": null,
"timings": {
"queue_ms": 0,
"compile_ms": 9,
"render_ms": 734,
"post_ms": 12,
"upload_ms": 41,
"total_ms": 812
},
"error": null,
"meta": { "order_id": "8812" },
"created_at": "2026-09-11T10:00:00.000Z",
"completed_at": "2026-09-11T10:00:01.000Z"
}
FeldBedeutung
idDie ID des Renderings, rnd_ und 32 Hexadezimalzeichen
statusqueued, rendering, succeeded oder failed
outputDas Dateiformat
download_url, expires_atLink zur Datei und wann der Link abläuft; null, bis das Rendering gelingt, und nachdem die Datei gelöscht wurde
page_count, bytesSeiten (PDF) und Dateigröße
unitsBerechnete Einheiten, in Schritten von 0,5; 0 bei Test-Schlüsseln und deduplizierten Renderings
region, environmentWo gerendert wurde (eu) und ob ein live- oder test-Schlüssel angefragt hat
templatenull bei html- und url-Quellen. id (tpl_…), slug, version (die gerenderte Version), channel (der Release Channel, aus dem sie kam, null bei einer Versionsnummer oder latest) und canary (ob der Canary-Anteil des Channels sie gewählt hat)
engine_version, template_checksum, output_sha256Nachweis der Reproduzierbarkeit
storageDie Kopie im eigenen Bucket des Workspace, mit ihrem status, bucket und key; null, wenn es keine gibt
einvoiceDer Bericht einer E-Rechnung: was erzeugt wurde, was der Validator gefunden hat und ob das PDF die Werte des XML zeigt; null, wenn das Rendering keine erzeugt hat
accessibilityDas Urteil des Validators über die Datei als PDF/UA-1, mit den verletzten Regeln und ihren Elementen; null, wenn das Rendering settings.pdf.ua nicht gesetzt hat
timingsMillisekunden in der Warteschlange, beim Füllen der Vorlage (compile_ms), in Chromium (render_ms), beim Post-Processing (post_ms), beim Hochladen und insgesamt; null, bis es fertig ist
errorWenn status den Wert failed hat: { code, message, … }, Code und Meldung des Problems, nicht das ganze Problem
metaIhre Metadaten
created_at, completed_atWann das Rendering angelegt und wann es fertig wurde

Die Antwort-Header wiederholen das Wesentliche: X-Units-Charged, X-Units-Remaining (was der Organisation in der Periode bleibt, Prepaid-Einheiten eingeschlossen), X-Render-Region, X-Render-Engine (die Vorlagensprache, none bei einfachem HTML und URLs, dedup bei einer deduplizierten Antwort), X-Render-Deduplicated, Server-Timing mit den Zeiten und die RateLimit-*-Header, die unter Limits und Verbrauch beschrieben sind. Ist die Antwort die Datei eines Renderings, das settings.pdf.ua gesetzt hat, sagt X-Formfeed-PDF-UA conformant oder not-conformant.

Ein Rendering, das fehlschlägt, während die Anfrage wartet, antwortet mit seinem Problem und dem HTTP-Status seines Codes (etwa 422 template_runtime_error), mit der ID des fehlgeschlagenen Renderings in render_id. So finden Sie es im Render-Protokoll und unter GET /renders/{id}.

Reproduzierbarkeit​

Jedes Rendering zeichnet den Chromium-Build und die verwendete Vorlagensprache auf (engine_version, etwa chromium 140.0.7339.16; engine jinja2), eine Prüfsumme der Vorlagenversion (template_checksum) und einen SHA-256 der Ausgabedatei (output_sha256). Dieselben Eingaben, gerendert mit demselben Chromium-Build, ergeben dasselbe Dokument. Die Datei selbst trägt den Zeitpunkt ihrer Erzeugung, zwei Renderings derselben Anfrage haben also verschiedene Werte von output_sha256. Speichern Sie beide Prüfsummen neben einem Vertrag oder einer Rechnung: template_checksum belegt später, welche Vorlagenversion das Dokument erzeugt hat, output_sha256, dass diese Datei nicht verändert wurde.

Das Render-Protokoll hält zu jedem Rendering einen ausführlicheren Nachweis: Neben diesen drei Angaben enthält er einen Hash der Daten und der Einstellungen, die Schriften, die Version des Brand Kits und die Versionen der geteilten Partials, die die Vorlage gelesen hat, sowie die Region, die gerendert hat. Die App zeigt ihn in den Details des Renderings unter Reproduzierbarkeit; die API liefert nur die drei Felder oben.

Eine vollständige Anfrage​

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": { "invoice": { "number": "2026-0042", "lines": [{ "description": "Beratung", "qty": 8, "price": 120 }] } },
"output": "pdf",
"filename": "rechnung-2026-0042.pdf",
"locale": "de-DE",
"settings": { "margin": { "top": "30mm" } },
"access": "signed",
"meta": { "order_id": "8812" }
}'