Zum Hauptinhalt springen

Streams und Feeds

Ein Stream ist eine Adresse, an die Ihre eigenen Systeme Daten einspielen. Er trägt eine Vorlage und, wo Ihr Dokument nicht schon zu dieser Vorlage passt, eine Feldzuordnung, die sagt, wo in Ihrem Dokument jede Eingabe der Vorlage liegt. Kanäle abonnieren den Stream und erben beides, sodass die Zuordnung an genau einer Stelle geschrieben und an genau einer Stelle geprüft wird.

Feeds sind die andere Hälfte: die Daten, die Reelwire mitbringt, die die Kanäle eines Workspaces empfangen, statt dass der Workspace sie erzeugt. Diese Routen konfigurieren beides. An einen Stream zu senden ist Ereignisse senden. Im Dashboard sind Streams unter Eigene Feeds und Feeds unter Fertige Feeds.

MethodePfadBerechtigung
GET/v1/streamsstreams:read
POST/v1/streamsstreams:write
POST/v1/streams/{id}/updatestreams:write
POST/v1/streams/{id}/deletestreams:write
POST/v1/streams/{id}/suggest-mapstreams:write
POST/v1/streams/{id}/testangemeldete Person
GET/v1/feedsfeeds:read
PUT/v1/feedsfeeds:write
GET/v1/feeds/{feedId}/referencefeeds:read
GET/v1/feeds/{feedId}/reference/{ratio}feeds:read

Streams und Feeds sind zwei Berechtigungsbereiche, Eigene Feeds (streams) und Fertige Feeds (feeds), weil sie zwei verschiedene Dinge sind: Ein Stream ist Ihre eigene Leitung, ein Feed ist etwas, das Reelwire mitbringt. /v1/streams und /v1/stream sind auch verschiedene Routen in verschiedenen Bereichen. Die im Plural konfiguriert Streams und braucht streams; die im Singular nimmt Ereignisse entgegen und braucht ingest:write.

Einen Stream erstellen​

curl http://localhost:4000/v1/streams \
-H "Authorization: Bearer rw_your_key_here" \
-H "Content-Type: application/json" \
-d '{
"name": "Docs examples stream",
"description": "Created while writing the reference",
"templateId": "tpl_dd7e6b264fb8",
"fieldMap": {
"/clipData/label": { "from": "/article/title", "as": "string", "maxLength": 70 },
"/clipData/value": { "from": "/article/figure", "as": "number" },
"/clipData/source": { "const": "Orchid Invest" }
}
}'
{
"stream": {
"id": "st_d84d89e39bfc",
"name": "Docs examples stream",
"description": "Created while writing the reference",
"enabled": true,
"templateId": "tpl_dd7e6b264fb8",
"fieldMap": {
"/clipData/label": { "as": "string", "from": "/article/title", "maxLength": 70 },
"/clipData/value": { "as": "number", "from": "/article/figure" },
"/clipData/source": { "const": "Orchid Invest" }
},
"subscribers": 0,
"listeners": []
}
}

201. Der Text:

FeldErforderlichWas es ist
idneinst_ und zwölf Hexadezimalzeichen, wo Sie ihn wählen wollen. Lassen Sie ihn weg, und Reelwire zieht einen. Eine ID, die in irgendeinem Workspace schon vergeben ist, ist ein 409.
nameja1 bis 120 Zeichen.
descriptionneinBis zu 500 Zeichen.
enabledneinStandard ist true.
templateIdjaDie Vorlage, mit der jedes Ereignis dieses Streams gerendert wird, eine, die diesem Workspace angeboten wird. Jede andere ist ein 404.
fieldMapneinStandard ist {}, was Durchreichen bedeutet.

Ein hier gesendetes templateVariant oder templateVersion wird verworfen: Welcher Zuschnitt und welcher Build einen Clip rendert, ist die Wahl jedes Kanals, der abonniert.

Die Vorlage muss einen Build zum Rendern haben, sonst wird der Stream mit einem 404 abgelehnt, der das sagt. Ein Workspace hält bis zu 50 Streams, sofern Reelwire es nicht anders gesetzt hat; darüber nennt ein 409 die Zahl. Ein Workspace, dessen Abonnement gekündigt wurde, kann keinen neuen Stream erstellen (ein 409), bis er wieder abonniert, in jedem Tarif, auch in Free.

Die Feldzuordnung​

Eine Zuordnung ist flach: Jeder Schlüssel ist ein JSON Pointer in das Eingabedokument der Vorlage, und jeder Wert sagt, woher diese Eingabe in Ihrem Dokument kommt.

Titel und Zusammenfassung eines RSS-Elements, an einen Stream gesendet, dessen Feldzuordnung sie in die Headline und die Subline der Vorlage setzt, mit einer Konstante für die Quellenzeile

SchlüsselWas es bewirkt
fromEin JSON Pointer in die gesendeten data.
constEin fester Wert, für eine Eingabe, für die Ihre Daten keine Entsprechung haben.
fallbackWird verwendet, wenn from zu nichts aufgelöst wird. Fehlend ist nicht dasselbe wie leer.
asUmwandlung vor der Validierung: string, number, integer, boolean oder iso-date.
maxLengthZu langen Text auf das Limit der Vorlage kürzen, statt die Validierung fehlschlagen zu lassen.

Geben Sie jedem Eintrag ein from oder ein const. Wo from nichts findet, wird fallback verwendet, dann const, wenn der Eintrag beides hat; mit weder fallback noch const ist das Ereignis unmapped, und seine mappingErrors nennen das Feld (siehe die Status). Schlüssel müssen mit / beginnen, und ihr erster Schritt muss eine der eigenen Eingaben der Vorlage sein (/clipData/... oder /metaData/... bei jeder Vorlage im Katalog): Eine Zuordnung, die woandershin zielt, ist ein 400, der auflistet, was die Vorlage annimmt, wenn der Stream erstellt oder geändert wird.

Eine leere Zuordnung bedeutet Durchreichen, nicht „alles verwerfen“, und ist jetzt der gewöhnliche Fall: Eine Vorlage veröffentlicht die Form, die sie annimmt, unter /v1/formats/template/{templateId}, sodass ein dagegen geschriebener Produzent schon das richtige Dokument ausgibt und nichts zuzuordnen hat.

Sie können eine Zuordnung laufen sehen, ohne etwas zu senden, bei /v1/stream/validate. Das rohe Dokument geht hinein, und document kommt zurück, wie die Vorlage es sehen wird:

curl http://localhost:4000/v1/stream/validate \
-H "Authorization: Bearer rw_your_key_here" \
-H "Content-Type: application/json" \
-d '{
"streamId": "st_d84d89e39bfc",
"eventId": "01M2SQMKK3G1NVFXV31DCBVB95",
"occurredAt": "2026-09-18T08:00:00Z",
"data": {
"article": { "title": "Assets under management at Orchid Invest", "figure": "12.4", "body": "..." },
"publishedBy": "newsroom"
}
}'
{
"requestId": "01M2SQYACSHKW9FHFD8RYAK1WT",
"streamId": "st_d84d89e39bfc",
"templateId": "tpl_dd7e6b264fb8",
"templateVersion": "1.0.0",
"valid": true,
"document": {
"clipData": {
"label": "Assets under management at Orchid Invest",
"value": 12.4,
"source": "Orchid Invest"
}
},
"warnings": [{ "path": "/clipData/value", "message": "converted to number" }],
"errors": [],
"wouldMatch": []
}

wouldMatch ist leer, weil noch kein Kanal diesen Stream abonniert. Das Dokument ist trotzdem gültig. Jede Umwandlung, die einen Wert geändert hat, jeder verwendete Fallback und jeder auf maxLength gekürzte Text ist eine Warnung; ein Pointer, der nichts findet, ohne fallback oder const, oder ein Wert, der sich nicht umwandeln lässt, ist ein Fehler.

Jeder Kanal wird gegen die Variante geprüft, die er rendert. Die Varianten einer Vorlage können verschiedenen Inhalt nehmen: In einem Package ist jede Variante ein anderer Beitrag mit eigenen Feldern, und ein Feld, das eine Variante verlangt, hat eine andere vielleicht nicht. Das Dokument wird also einmal für jeden Kanal geprüft, der den Stream empfängt, gegen die Variante und Version dieses Kanals, und wouldMatch listet nur die Kanäle auf, die einen Beitrag erzeugen würden. Ein Kanal, dessen Variante das Dokument ablehnt, bleibt weg und wird in warnings genannt („Not posted on ...“), während die anderen weiter veröffentlichen; nimmt keine Variante eines Kanals es, ist valid false, und errors sagen warum, mit den Kanälen und der Variante, wo sie sich unterscheiden. Empfängt kein Kanal, wird das Dokument gegen die erste Variante der Vorlage geprüft. Ein echtes Ereignis bei /v1/stream wird genauso beurteilt: angenommen für die Kanäle, zu denen es passt, mit den anderen unter warnings genannt, und unmapped, mit mappingErrors, nur wenn es zu keinem passt.

Reelwire eine Zuordnung vorschlagen lassen​

POST /v1/streams/{id}/suggest-map gleicht Ihre eigenen Feldnamen mit den Eingaben der Vorlage ab und schlägt eine Zuordnung vor. Es braucht eine echte Nutzlast, von der es ausgehen kann, und sagt klar, wenn es keine gibt, mit einem 409:

curl http://localhost:4000/v1/streams/st_d84d89e39bfc/suggest-map \
-H "Authorization: Bearer rw_your_key_here" \
-H "Content-Type: application/json" \
-d '{}'
{
"error": {
"type": "https://reelwire.io/errors/no_sample",
"code": "no_sample",
"message": "This stream has not received anything yet",
"requestId": "01M2SQYKXH7B2JW3PWQ24YHV3Q",
"details": [
{
"path": "",
"message": "Send one event from your script first. The suggestion is built from your own field names, so there is nothing to match against until then."
}
]
}
}

Senden Sie ein Ereignis, dann fragen Sie erneut. Ein Stream behält dafür die letzte Nutzlast, die er empfangen hat. Hat er eine, ist die Antwort suggestion (eine Zuordnung zur Durchsicht, nie von selbst angewendet), templateFields (jede Eingabe der Vorlage) und sample (die Nutzlast, gegen die abgeglichen wurde). Senden Sie templateId und, wenn Sie möchten, templateVersion, um eine Zuordnung für eine andere Vorlage zu sehen, bevor Sie wechseln.

Streams auflisten​

curl http://localhost:4000/v1/streams \
-H "Authorization: Bearer rw_your_key_here"
{
"route": "http://localhost:4000/v1/stream",
"streams": [
{
"id": "st_30472168d941",
"name": "Enterprise 2 Stream 1",
"enabled": true,
"templateId": "tpl_dd7e6b264fb8",
"fieldMap": {},
"lastReceivedAt": "2026-09-18T07:45:50.088Z",
"subscribers": 2,
"listeners": [
{ "channelId": "01M2985NX4V0WA1GV9MC63DEPJ", "channelName": "Channel 4 de" },
{ "channelId": "01M2985NXJRXVE5HKCCGKASP8P", "channelName": "Channel 10 en" }
]
}
]
}

subscribers ist, wie viele Kanäle den Stream empfangen. Null bedeutet, dass nichts gerendert wird, was auch immer Sie senden. Jeder Stream trägt außerdem lastSample, die letzte Nutzlast, die er empfangen hat, sobald er eine hat.

Einen Stream ändern oder entfernen​

POST /v1/streams/{id}/update nimmt dieselben Felder wie das Erstellen, alle optional, außer id: Die ID ist absichtlich nicht bearbeitbar, weil Ihr Skript sie in jeder Anfrage nennt und eine Änderung aus Ihrer Sicht so aussähe, als gäbe Reelwire ohne Grund 404 zurück. Eine Änderung von templateId oder fieldMap wird vor dem Speichern gegen den neuesten Build der Vorlage geprüft und, wo es sie gibt, gegen die letzte Nutzlast des Streams, sodass eine Zuordnung, die gespeichert wird, eine ist, die rendert. Es antwortet mit { "ok": true }. Eine neue Vorlage schickt jeden empfangenden Kanal zurück zur ersten Variante der Vorlage und ihrem neuesten Build, weil das, was sie gewählt hatten, zur alten Vorlage gehörte.

POST /v1/streams/{id}/delete entfernt den Stream und antwortet mit { "ok": true, "stopped": 2 }: Von da an werden Ereignisse an ihn abgelehnt, und die Kanäle, die ihn empfingen, hören auf, was stopped zählt. Was er schon empfangen hatte, wird weiterhin gerendert und veröffentlicht. Die Historie des Streams bleibt, unter seinem Namen mit angehängtem „ (removed)“; prüfen Sie also listeners, bevor Sie es aufrufen.

Einen Stream über das Dashboard testen​

POST /v1/streams/{id}/test ist die Route hinter dem Testwerkzeug unter Eigene Feeds und gehört allein einer angemeldeten Person: Jeder API-Schlüssel wird dort abgelehnt, was er auch enthält, und die Person braucht Betrieb, wie beim Erstellen eines Streams. Es nimmt die Daten des Ereignisses und baut den Rest des Umschlags so, wie Ihr Produzent es täte: den Stream aus dem Pfad, eine frische eventId und jetzt als occurredAt.

FeldErforderlichWas es ist
datajaEin Objekt: was ein Produzent als data senden würde.
checkneintrue führt den Probelauf aus und speichert nichts. Weggelassen wird das Ereignis wirklich gesendet.

Mit check ist die Antwort { "check": ... } und enthält genau das, was POST /v1/stream/validate antwortet. Ohne es ist die Antwort ein 202 { "sent": ... } und enthält das, was POST /v1/stream antwortet, und jeder Kanal, der den Stream empfängt, rendert und übergibt einen Beitrag davon wie jeden anderen. Ein pausierter oder unbekannter Stream wird abgelehnt, wie er es dort wird.

Es gehört einer Person, weil es echte Ereignisse sendet: Ein Schlüssel tut das nur mit Custom feeds - Send raw data (ingest:write), bei POST /v1/stream, und eine Person, die einen Stream testet, sollte nicht erst einen Schlüssel erstellen müssen. Siehe Ereignisse senden.

Feeds​

Die fertigen Feeds tragen Live-Daten: Der eigene Feed-Server von Reelwire ruft sie aus öffentlichen Quellen ab, baut den Inhalt, den die Vorlage jedes Feeds nimmt, und sendet jedes Ereignis an jeden Workspace mit einem empfangenden Kanal. Nichts davon braucht einen Schlüssel oder einen Produzenten von Ihnen.

GET /v1/feeds listet jeden mitgelieferten Feed auf, der dem Konto angeboten wird, gruppiert nach Quelle, mit der Vorlage, durch die jeder gerendert wird, den Kanälen, die ihn empfangen (listeners, gezählt in subscribers), wann er voraussichtlich veröffentlicht, und languages, den Sprachen, in denen sein Inhalt kommt, wobei en immer dabei ist: Ein Kanal in einer anderen Sprache erhält seinen englischen Inhalt. Es ist das, was die Seite Fertige Feeds zeigt.

Was einem Workspace angeboten wird, folgt den Branchen, die sein Inhaber gewählt hat (siehe Ihre Branchen): ein Feed, der für eine davon als Kern oder möglich eingestuft ist. Nichts anderes schaltet einen frei. Das entscheidet, was ein Kanal abonnieren darf, und nichts danach: Es gibt keine Branchenprüfung bei den Ereignissen selbst, sodass ein Feed, der dem Konto nicht mehr angeboten wird, aufgelistet bleibt und weiter veröffentlicht, solange ein Kanal ihn empfängt.

curl http://localhost:4000/v1/feeds \
-H "Authorization: Bearer rw_your_key_here"
{
"sources": [
{
"slug": "crypto-spot-and-dominance",
"title": "Crypto spot and dominance",
"category": "Market and financial data",
"description": "Prices, market values and 24-hour moves of the largest coins, and Bitcoin's share of the whole crypto market.",
"cadence": "Daily, and price milestones as they happen",
"period": "Daily",
"implementation": "Feed server: crypto market data",
"milestone": 1,
"entitled": false,
"live": true,
"formats": [
{
"id": "fd_c16f5268064a",
"slug": "crypto-board",
"name": "Crypto board",
"content": null,
"title": "Crypto board",
"example": "The eight largest coins with their 24-hour moves",
"description": "The eight largest coins by market value, excluding stablecoins, with their price and 24-hour move. Every morning.",
"group": "Crypto spot and dominance",
"contentStyle": null,
"milestone": 1,
"templateId": "tpl_06dc2554e5ac",
"templateName": "Number grid",
"templateVariant": "Data terminal",
"maxAgeSeconds": null,
"live": true,
"entitled": false,
"offered": true,
"enabled": true,
"subscribers": 1,
"listeners": [{ "channelId": "01M1VFRWE7EWSY5D7QDQ0KNK1S", "channelName": "Channel 1 en" }],
"schedule": { "every": "daily", "time": "08:00" },
"scheduleLabel": "Daily 08:00",
"scheduleNote": null,
"sentLast30Days": 30,
"multilingual": false,
"languages": ["en"],
"type": "live",
"plans": ["FREE", "INDIVIDUAL", "SMALL_BUSINESS", "ENTERPRISE"],
"usable": true
}
]
}
]
}

id ist die Feed-ID, fd_ und zwölf Hexadezimalzeichen. Sprechen Sie einen Feed überall, wo einer verlangt wird, über seine ID an: Die Slugs sind Wörter, und Wörter werden bearbeitet, und ein an einen Titel gebundener Produzent bricht an dem Tag, an dem jemand dessen Groß- und Kleinschreibung korrigiert. Ein Feed, den Reelwire selbst hinzugefügt hat, steht unter dem Source-Slug custom, mit seiner eigenen ID als Format-Slug.

offered ist das Feld, das zu lesen ist. entitled ist ein Überbleibsel aus der Zeit, als Feeds je Tarif gewährt wurden, und entscheidet nichts mehr. enabled ist der eigene Schalter des Workspaces, unten. templateId, templateName und templateVariant sind die Vorlage und der Stil, mit denen der Feed gerendert wird, von denen ein Kanal, der ihn abonniert, ausgeht. schedule und scheduleLabel sagen, wann der Feed voraussichtlich veröffentlicht, wie es der Veröffentlichungskalender zeigt, und scheduleNote sagt es in Worten, wo es keine Regel gibt. maxAgeSeconds ist hier null; ein Ereignis, das älter als ein Tag ist, wird aufbewahrt, aber nie veröffentlicht (das maxAgeSeconds des Katalogs, siehe Vorlagen und Formate). multilingual und languages sagen, in welchen Sprachen der Feed kommt. true nimmt jede language, die das Abonnement eines Kanals wählt, und übersetzt den Feed per KI hinein, was den Wortlaut dessen verändern kann, was einen Regulator interessiert; languages ist dann ["en"], die Sprache, die sein eigener Code schreibt. false kommt in den Sprachen, die languages nennt, die Standardsprache zuerst, jede vom eigenen Code des Feeds geschrieben und nie von KI (siehe Abonnements). type sagt, wie sein Inhalt entsteht: evergreen (einmal mit KI geschrieben, von einer Person geprüft, nach Plan veröffentlicht, keine Nachrichten), live (bei jedem Lauf neue Daten aus öffentlichen Quellen, sein Wortlaut vielleicht von KI geglättet und übersetzt) oder official (direkt von der Behörde oder dem lizenzierten Anbieter, der die Daten veröffentlicht, oder vom Code berechnet; Wortlaut und Sprachen im Code festgelegt, in keinem Schritt KI). plans listet die Tarife auf, in denen der Feed verfügbar ist, und usable sagt, ob der Tarif dieses Workspaces einer davon ist: Ein Feed mit usable false wird aufgelistet, damit man ihn sehen kann, und einen Kanal darauf zu abonnieren wird abgelehnt.

GET /v1/feeds/{feedId}/reference listet die Beispielclips auf, die ein Feed hat, nach Variante und Version, mit den Bildformaten, in denen jeder kommt. .../reference/{ratio} liefert einen davon, eine MP4 statt JSON; schränken Sie mit ?variant= und ?version= ein (ohne sie die erste Variante und die neueste Version, und latest ist ebenfalls die neueste), und die gelieferte Version steht in x-preview-version. Eine Variante, Version oder ein Bildformat ohne Clip ist ein 404, und das etag funktioniert mit If-None-Match wie beim Referenzclip einer Vorlage. Die Clips eines Feeds zeigen den Stil, an den er gebunden ist; die anderen Stile der Vorlage stehen unter GET /v1/templates/{templateId}/reference.

PUT /v1/feeds schaltet einen Feed für den ganzen Workspace ein oder aus. Es nimmt sourceSlug, formatSlug und enabled, und enabled muss ein Boolean sein und kein String. Jeder Feed beginnt eingeschaltet, und das allein sendet nichts: Ein Kanal muss ihn noch abonnieren. Ein Feed, den es nicht gibt, ist ein 404; einer, der dem Workspace nicht angeboten wird und den kein Kanal empfängt, ist ein 403.

curl -X PUT http://localhost:4000/v1/feeds \
-H "Authorization: Bearer rw_your_key_here" \
-H "Content-Type: application/json" \
-d '{"sourceSlug":"crypto-spot-and-dominance","formatSlug":"crypto-board","enabled":true}'
{ "sourceSlug": "crypto-spot-and-dominance", "formatSlug": "crypto-board", "enabled": true }

Einen Feed hier auszuschalten stoppt ihn für alle Kanäle auf einmal. Das Abonnement eines einzelnen Kanals zu entfernen ist eine Kanaloperation; siehe Kanäle.

Einen Stream oder Feed veröffentlichen lassen​

Keiner von beiden veröffentlicht etwas, bis ein Kanal ihn abonniert: ein Stream mit kind: "STREAM" und seiner streamId, ein Feed mit kind: "BROADCAST" und seinem sourceSlug und formatSlug, beide bei POST /v1/channels/{id}/subscriptions; siehe Kanäle. Die Vorlage kommt vom Stream oder Feed, und der Kanal wählt den Zuschnitt und den Build. Ein Feed, der dem Workspace nicht angeboten wird, ist dort ein 403.

Häufige Fehler​

/v1/stream mit /v1/streams verwechseln. Die eine nimmt Ereignisse entgegen, die andere konfiguriert sie, und sie brauchen verschiedene Berechtigungen. Der Präfixabgleich ist exakt, sodass ein Schlüssel mit ingest:write allein bei der Route im Plural einen 403 erhält und ein Schlüssel mit streams:write allein nicht senden kann.

Eine Feldzuordnung schreiben, wenn der Produzent einfach die richtige Form ausgeben könnte. Fragen Sie /v1/formats/template/{templateId}, was die Vorlage annimmt. Eine Zuordnung gibt es für Daten, die Sie nicht kontrollieren, nicht als normalen Weg hinein.

Die Zuordnung verschachteln, um das Ziel nachzubilden. Sie ist flach. "/instrument/symbol" ist ein Schlüssel, keine zwei Objektebenen.

Eine Vorlagenversion am Stream festlegen. Das geht nicht: Der Stream trägt die Vorlage, und welcher Zuschnitt und welcher Build, ist die Wahl des abonnierenden Kanals. Siehe Kanäle.

Einen Stream löschen, um ihn zurückzusetzen. Seine Abonnements enden mit ihm, jeder Kanal, der ihn empfing, empfängt nichts mehr, und die ID kann nicht erneut verwendet werden. Pausieren Sie ihn stattdessen mit enabled: false, was Ereignisse mit einem 409 stream_disabled beantwortet, bis Sie ihn wieder einschalten.