Zum Hauptinhalt springen

Release Channels

Ein Release Channel ist ein benannter Zeiger von einer Vorlage auf eine ihrer Versionen. Ihr Code rendert den Channel, und wohin der Zeiger zeigt, entscheidet, welche Version er bekommt, ohne Deployment auf Ihrer Seite.

Jede Vorlage hat den Channel published: Das Veröffentlichen einer Version verschiebt ihn, und ihn verwendet ein Rendering, das keine Version nennt. Benannte Channels wie staging oder beta stehen daneben. Ein typischer Aufbau:

ChannelZeigt aufGerendert von
publishedVersion 7Produktion (version weggelassen)
stagingVersion 8Ihrer Staging-Umgebung ("version": "staging")

Sieht Version 8 auf Staging richtig aus, veröffentlichen Sie sie (oder verschieben published dorthin), und die Produktion rendert sie innerhalb einer Minute. Stellt sie sich als falsch heraus, rollen Sie zurück, und Version 7 ist wieder live.

Einen Channel rendern​

Übergeben Sie den Namen des Channels als version:

{
"template": "invoice",
"version": "staging",
"data": { "number": "RE-2026-001" }
}
versionRendertSchlüsselPläne
weggelassen oder publishedDie veröffentlichte Version (und ihren Canary, falls es einen gibt)Live und Testalle
ein Channel-NameDie Version, auf die der Channel zeigt (und seinen Canary, falls es einen gibt)Live und Testab Starter
eine NummerGenau diese VersionLive und Testalle
latestDie neueste gespeicherte Version, veröffentlicht oder nichtnur Testalle

latest ist die neueste Version, die jemand gespeichert hat, im Editor oder mit formfeed templates push; nicht gespeicherte Änderungen in einem offenen Editor stehen in keiner Version. Ein Live-Schlüssel, der latest sendet, bekommt 400 invalid_request. Anders als latest ist ein Channel eine bewusste Entscheidung, die jemand getroffen hat; deshalb dürfen auch Live-Schlüssel ihn rendern. Nehmen Sie für eine Staging-Umgebung lieber einen Channel als latest: Er ändert sich nur, wenn ihn jemand verschiebt, nicht bei jedem Speichern.

Das Rendering hält fest, woher die Version kam:

"template": { "id": "tpl_03b04e8c168dfc7aa5fdcdcf3f54489c", "slug": "invoice", "version": 8, "channel": "staging", "canary": false }

channel ist null, wenn die Anfrage eine Nummer oder latest nannte. Dasselbe template-Objekt steht in GET /renders/{id} und in den Webhook-Ereignissen.

Ein Channel, den die Vorlage nicht hat, antwortet mit 404 channel_not_found. Eine Vorlage ohne veröffentlichte Version antwortet für published weiterhin mit 404 template_not_found. Im Free-Plan antwortet jeder Channel außer published mit 403 forbidden (siehe Pläne).

Verschiebungen brauchen bis zu einer Minute

Die API speichert 60 Sekunden lang zwischen, worauf ein Channel zeigt, genau wie beim Veröffentlichen. Nach einer Verschiebung verwenden Renderings die neue Version innerhalb einer Minute; bis dahin können einige noch die vorherige verwenden.

GET /templates/{id}/versions/staging liest die Version des Channels mit allen Dateien, genauso wie published oder eine Nummer. Derselbe Name funktioniert überall, wo eine Version gelesen oder geprüft wird: GET /templates/{id}/schema?version=staging liefert das Datenschema dieser Version, und POST /templates/{id}/validate mit "version": "staging" prüft Daten dagegen.

In Zapier, Make und n8n​

  • Zapier: Die Aktion Create Document zeigt ein Feld Version, sobald die Vorlage einen Channel neben published hat, und listet jeden Channel mit der Version, die er rendert; eine Vorlage ohne benannte Channels zeigt gar kein Versionsfeld.
  • Make: Das Modul Create a document verhält sich genauso, und in das Select lässt sich auch eine Versionsnummer mappen.
  • n8n: Die Option Version Name or ID des Nodes (unter den Optionen) listet immer Published und jeden benannten Channel mit seiner Version und nimmt auch eine Versionsnummer als Expression.

Channel-Namen​

1 bis 32 Kleinbuchstaben, Ziffern und Bindestriche, beginnend mit einem Buchstaben: staging, beta, customer-a. latest und draft sind reserviert, und published gibt es immer; er lässt sich nicht löschen (DELETE …/channels/published antwortet mit 400 invalid_request). Jeder andere Name, etwa Staging oder 2026-q4, antwortet mit 422 channel_name_invalid.

Einen Channel verschieben​

Das Verschieben eines Channels, den es noch nicht gibt, legt ihn an. published zu verschieben ist dasselbe, wie diese Version zu veröffentlichen: Die bisher veröffentlichte Version wird archiviert. published lässt sich in jedem Plan verschieben; jeder andere Channel braucht Starter oder höher. Jede Verschiebung merkt sich als previous_version, worauf der Channel vorher zeigte; dorthin kehrt das Zurückrollen zurück.

Die Vorlagenliste zeigt die benannten Channels jeder Vorlage in der Spalte Kanäle. Um sie zu ändern, öffnen Sie die Vorlage im Editor und dort das Panel Versionen:

  • Der Abschnitt Kanäle oben listet jeden Channel mit der Version, auf die er zeigt, seinem Canary (Canary v8 · 10 %) und pro Version den Renderings und fehlgeschlagenen Renderings der letzten 24 Stunden.
  • Jede Version hat Kanal hierher verschieben: Wählen Sie einen Channel, um ihn auf diese Version zeigen zu lassen, oder Neuer Kanal …, um dort einen anzulegen. Jede Version zeigt außerdem die benannten Channels, die auf sie zeigen.
  • Das Menü ⋮ neben einem Channel (Kanal-Aktionen) bietet Canary starten … (oder Canary ändern …), Canary v… übernehmen, Canary beenden, Zurück auf v… und bei benannten Channels Kanal löschen.

Inhaber, Admins und Editoren verschieben Channels; im Free-Plan weist das Panel darauf hin, dass Channels ab Starter verfügbar sind.

Channels zu schreiben braucht den Scope template:write, sie aufzulisten template:read.

Canary-Releases​

Ein Channel kann einen Anteil seiner Renderings an eine zweite Version schicken, den Canary, während der Rest weiter die Hauptversion verwendet. So probieren Sie eine neue Version mit echtem Traffic aus, bevor alle sie bekommen.

npx formfeed channels set invoice published 7 --canary 8 --percent 10
PUT /v1/templates/invoice/channels/published

{ "version": 7, "canary": { "version": 8, "percent": 10 } }
  • Der Anteil ist eine ganze Zahl von 1 bis 50 Prozent, und der Canary muss eine andere Version als die Hauptversion sein; sonst antwortet die API mit 400 invalid_request.
  • Canary-Releases brauchen Pro oder höher (Pläne); auch published kann einen Canary haben.
  • template.canary ist true bei Renderings, die der Canary-Anteil gewählt hat.
  • Einen Channel ohne canary zu setzen, entfernt seinen Canary. Um einen Canary zu beenden, setzen Sie den Channel erneut auf seine Hauptversion (Canary beenden in der App).
  • Vergleichen Sie die Fehlschläge des Canary mit denen der Hauptversion in formfeed channels list, im Panel Versionen oder in usage_24h, und promoten oder entfernen Sie ihn dann. Automatisch wird nichts promotet oder zurückgerollt.

Wer den Canary bekommt​

Jedes Rendering wird per Hash der Hauptversion oder dem Canary zugeordnet, sodass der Anteil im Mittel stimmt und dieselbe Eingabe immer auf derselben Seite landet. Was in den Hash eingeht:

Die Anfrage sendetAuf derselben Version landen
canary_keyAlle Renderings mit diesem Wert, z. B. alle Dokumente eines Kunden
keinen canary_keyNur Wiederholungen derselben Anfrage (ihr Idempotency-Key); getrennte Renderings werden unabhängig verteilt, ohne Schlüssel zufällig
einen Batch ohne canary_keyAlle Einträge dieses Batches; setzen Sie canary_key am Batch oder an einzelnen Einträgen, um stattdessen nach Kunde zu verteilen

Senden Sie canary_key, wenn zusammengehörige Dokumente gleich aussehen sollen, etwa Auftragsbestätigung und Rechnung derselben Bestellung:

{ "template": "invoice", "data": { "…": "…" }, "canary_key": "customer-4711" }

canary_key ist ein String mit 1 bis 200 Zeichen. In einem Batch gilt ein canary_key auf oberster Ebene für jeden Eintrag, der keinen eigenen hat. Verwenden Sie eine neutrale ID statt einer E-Mail-Adresse; der Wert wird nur gehasht und nie gespeichert. Verwenden Sie dafür keinen wiederholten Idempotency-Key: Auf einen wiederholten Schlüssel antwortet die API 24 Stunden lang mit der gespeicherten Antwort des ersten Renderings, jedes spätere Dokument wäre also wieder das erste.

Promoten und zurückrollen​

AktionWas passiert
PromotenDer Canary wird die Hauptversion des Channels, und der Canary wird entfernt; die alte Hauptversion wird previous_version, ein Zurückrollen macht das Promoten also rückgängig. Antwortet mit 400 invalid_request, wenn der Channel keinen Canary hat. In der App: Canary v… übernehmen.
ZurückrollenDer Channel kehrt zu der Version zurück, auf die er vor seiner letzten Verschiebung zeigte (previous_version), und ein Canary wird entfernt. Ein zweites Zurückrollen kehrt dorthin zurück, wo das erste begann. Antwortet mit 400 invalid_request, wenn es keine frühere Version gibt. In der App: Zurück auf v….
LöschenEntfernt einen benannten Channel. Wird mit 409 channel_in_use abgelehnt, solange Renderings der letzten Stunde ihn verwendet haben, außer Sie erzwingen es. Renderings, die ihn danach noch nennen, schlagen mit 404 channel_not_found fehl. In der App: Kanal löschen, dann Trotzdem löschen, wenn er in Gebrauch ist.

Promoten und Zurückrollen nehmen wie eine Verschiebung allow_breaking im Body, weil die Schema-Prüfung auch für sie läuft.

npx formfeed channels promote invoice published
npx formfeed channels rollback invoice published
npx formfeed channels delete invoice beta --force
await client.templates.channels.promote('invoice', 'published');
await client.templates.channels.rollback('invoice', 'published');
await client.templates.channels.delete('invoice', 'beta', { force: true });
client.templates.channels.promote("invoice", "published")
client.templates.channels.rollback("invoice", "published")
client.templates.channels.delete("invoice", "beta", force=True)

Über HTTP sind das POST …/channels/{name}/promote, POST …/channels/{name}/rollback und DELETE …/channels/{name}?force=true.

Pläne​

FreeStarterProBusinessEnterprise
publishedjajajajaja
Benannte Channels pro Vorlagekeine21010unbegrenzt
Canary-Releasesneinneinjajaja
  • Im Free-Plan antwortet das Anlegen oder Rendern eines benannten Channels mit 403 forbidden und feature: "channels". Nach einem Downgrade auf Free antworten Renderings, die einen Channel nennen, ebenso; Renderings von published funktionieren weiter.
  • Einen Channel über die Anzahl des Plans hinaus anzulegen, antwortet mit 403, feature: "channels" und dem limit. Nach einem Downgrade von Pro auf Starter funktionieren alle bestehenden Channels weiter und lassen sich weiter verschieben; nur neue werden abgelehnt.
  • Einen Canary ohne Canary-Releases zu setzen, antwortet mit 403 und feature: "canary". Ein Channel, der bereits einen Canary hat, rendert in einem solchen Plan nur seine Hauptversion; ein Downgrade bricht also keine Renderings.

Die Schema-Prüfung​

Bevor eine Version live geht, vergleicht Formfeed ihr Datenschema mit dem Schema der Version, die Aufrufer gerade verwenden. Die Prüfung läuft immer dann, wenn ein Channel auf eine andere Version zeigen soll:

  • beim Veröffentlichen (POST /templates/{id}/versions/{number}/publish und POST /templates/{id}/versions mit "publish": true);
  • beim Verschieben eines Channels, auch published, für die Hauptversion und den Canary;
  • beim Promoten und Zurückrollen.

Die Antwort enthält das Ergebnis als schema_check:

"schema_check": {
"source": "stored",
"breaking": [
{ "path": "customer.email", "pointer": "/properties/customer/properties/email", "kind": "field-became-required", "breaking": true, "message": "\"customer.email\" is now required" }
],
"safe": [
{ "path": "notes", "pointer": "/properties/notes", "kind": "field-added-optional", "breaking": false, "message": "New optional field \"notes\"" }
]
}

Jede Änderung nennt das Feld als Punkt-Pfad path (items[] für die Einträge eines Arrays, leer für die ganzen Daten), einen JSON-pointer in das Schema, ob sie breaking ist, eine lesbare message (auf Englisch) und ihre Art kind: field-added-required, field-added-optional, field-removed, field-became-required, field-became-optional, type-narrowed, type-widened, enum-values-removed, enum-values-added, enum-added, enum-removed, const-changed, constraint-tightened, constraint-relaxed, additional-properties-closed, additional-properties-opened oder keyword-changed. Eine abgelehnte Anfrage trägt dieselben Listen breaking und safe in ihrem Problem.

sourceWannWirkung
storedBeide Versionen speichern ein DatenschemaBrechende Änderungen lehnen die Anfrage mit 409 schema_breaking_change ab, außer sie sendet allow_breaking: true
inferredMindestens eine Version hat kein gespeichertes Schema; verglichen wurden aus den Beispieldaten abgeleitete SchemasNur eine Warnung, nie eine Ablehnung: Ein abgeleitetes Schema ist kein Versprechen
noneNichts zu vergleichen: ein neuer Channel, das erste Veröffentlichen, dieselbe Version oder gar keine DatenNichts

Was als brechend gilt​

Die Prüfung betrachtet das Schema aus Sicht des Aufrufers, der Daten sendet, die die Vorlage liest. Eine Änderung ist brechend, wenn Daten, die zum alten Schema passten, vom neuen abgelehnt werden können.

BrechendUnbedenklich
Ein neues PflichtfeldEin neues optionales Feld
Ein optionales Feld wird PflichtEin Pflichtfeld wird optional
Ein Typ wird enger: ["string", "null"] zu "string", number zu integer, {} zu einem TypEin Typ wird weiter
Ein Enum-Wert entfällt, oder ein Feld ohne Enum bekommt einesEin Enum-Wert kommt hinzu, oder das Enum entfällt
const kommt hinzu oder ändert sichconst entfällt
minimum, maximum, minLength, maxLength, minItems, maxItems und ähnliche werden strenger; ein pattern oder format kommt hinzu oder ändert sichDiese Einschränkungen werden gelockert oder entfallen
additionalProperties wird falseadditionalProperties wird wieder geöffnet
Ein Feld entfällt aus einem Objekt, das keine unbekannten Felder annimmtEin Feld entfällt aus einem offenen Objekt (Aufrufer dürfen es weiter senden)
Jedes andere Schlüsselwort, das sich unterscheidet, weil die Prüfung es nicht beurteilen kannBeschreibungen, Titel, Beispiele und Standardwerte

Trotzdem fortfahren​

Manchmal ist eine brechende Änderung gewollt, zum Beispiel weil jeder Aufrufer das neue Feld bereits sendet. Dann sagen Sie es:

  • App: Der Veröffentlichen-Dialog listet die brechenden Änderungen; wählen Sie Trotzdem veröffentlichen (Trotzdem verschieben, wenn Sie einen Channel verschieben).
  • CLI: Hängen Sie --allow-breaking an templates push --publish, templates push --channel, channels set, channels promote oder channels rollback. Ohne das Flag gibt die CLI die Änderungen aus und endet mit Exit-Code 1.
  • API: Senden Sie "allow_breaking": true im Body der Anfrage zum Veröffentlichen, zum Channel oder zur Version.
  • SDKs: versions.publish(id, 8, { allowBreaking: true }) und channels.set(id, name, move, { allowBreaking: true }) in TypeScript, publish(id, 8, allow_breaking=True) und channels.set(…, allow_breaking=True) in Python.

Erzeugen Sie danach Ihre Typen neu, damit auch die Compiler der Aufrufer die Änderung sehen.

Ein typischer Ablauf​

  1. Nach Staging pushen. npx formfeed templates push invoice --channel staging legt aus Ihren Dateien eine Version an und lässt staging darauf zeigen.
  2. Testen. Ihre Staging-Umgebung rendert mit "version": "staging" und einem Test-Schlüssel, das kostet also nichts.
  3. Live gehen. Veröffentlichen Sie die Version (formfeed channels set invoice published 8 oder Veröffentlichen im Editor). Die Schema-Prüfung läuft; passen Sie die Daten an oder erlauben Sie die Änderung.
  4. Oder schrittweise ausrollen, ab Pro: formfeed channels set invoice published 7 --canary 8 --percent 10, formfeed channels list invoice beobachten, dann formfeed channels promote invoice published.
  5. Rückgängig machen, falls nötig: formfeed channels rollback invoice published.

In der CI kann der Schritt, der auf dem Standard-Branch templates push --publish ausgeführt hat, stattdessen bei jedem Merge nach staging pushen, und das Veröffentlichen wird ein eigener, manueller Schritt.