Brands
A brand is the look a clip wears: the colours, the typefaces, the logo, the badge, the opening and closing clips, and the lines of text that go out beside a post. Every channel wears exactly one brand, and a brand can be worn by many channels. Changing a brand changes every clip drawn from now on, and touches no clip already rendered. In the dashboard this is the Brands screen.
| Method | Path | Permission |
|---|---|---|
GET | /v1/brands | brands:read |
GET | /v1/brands/{id} | brands:read |
GET | /v1/brands/draft | brands:read |
POST | /v1/brands | brands:write |
PATCH | /v1/brands/{id} | brands:write |
POST | /v1/brands/palette | brands:read |
POST | /v1/brands/{id}/assets/{slot} | brands:write |
POST | /v1/brands/{id}/assets/{slot}/link | brands:write |
POST | /v1/brands/{id}/assets/{slot}/delete | brands:write |
POST | /v1/brands/{id}/duplicate | brands:write |
POST | /v1/brands/{id}/delete | brands:write |
GET | /v1/sound-library | media:read |
The style document
GET /v1/brands/{id} returns the brand with its whole style document.

curl http://localhost:4000/v1/brands/01M1VFRWE7VHP3H54ESRJQ0SAJ \
-H "Authorization: Bearer rw_your_key_here"
{
"brand": {
"id": "01M1VFRWE7VHP3H54ESRJQ0SAJ",
"name": "Brand 1",
"enabled": true,
"style": {
"identity": {
"name": "Brand 1",
"titlePrimary": "Enterprise 2 Brand 1",
"titleSecondary": "orchid.example",
"footerPrimary": "Enterprise 2 Brand 1",
"footerSecondary": "Orchid Invest. Educational only. Not advice."
},
"design": { "first": "#120A16", "second": "#1E1226", "third": "#2e2732", "fourth": "#77EE58" },
"accent": { "positive": "#77EE58", "negative": "#E8527F" },
"text": {
"primary": { "font": "open-sans", "size": 1, "color": "#F6EEFA" },
"secondary": { "font": "open-sans", "size": 1, "color": "#A990B5" }
},
"logo": {
"assetId": "01M2S2CPNV2YKF8VRV9396BYRD",
"fileName": "logo.png",
"mimeType": "image/png",
"url": "http://localhost:4000/v1/assets/01M2S2CPNV2YKF8VRV9396BYRD/download?exp=1789722057&sig=2af0b0a171347dad96f961ed99f44862050dfa2c678f0566f12a7f56392319c5"
},
"badge": {
"kind": "image",
"enabled": true,
"primaryLine": "Alex Fischer",
"secondaryLine": "Head of markets",
"asset": { "assetId": "01M2S2CP6YVZQX9BPE68FXSP5Y", "fileName": "badge.png", "mimeType": "image/png", "url": "..." }
},
"background": { "kind": "color" },
"sound": {
"mode": "clip-outro",
"asset": { "assetId": "01M2S2CPB4XJ7K0N2QH3RZ5TWE", "fileName": "jingle.mp3", "mimeType": "audio/mpeg", "url": "..." }
},
"posting": {
"description": "Orchid Invest shares short market updates. Educational only, not advice.",
"hashtags": ["orchidinvest"],
"link": { "url": "https://orchid.example", "label": "Visit our website" }
},
"safeAreas": {
"16x9": { "top": 64, "right": 96, "bottom": 72, "left": 96 },
"9x16": { "top": 96, "right": 72, "bottom": 240, "left": 72 }
}
}
}
}
The parts of the document:
| Part | What it is |
|---|---|
identity | The brand's own name, which is never drawn, and the four lines a template draws: titlePrimary and titleSecondary across the top, footerPrimary and footerSecondary across the bottom (Texts on the clip in the brand's dialogue). The name and titlePrimary take up to 80 characters, titleSecondary 120, footerPrimary 160 and footerSecondary 400. slogan, up to 60, is drawn by an intro and an outro whose content sends no slogan of its own. |
palette, design, accent | The colours. Where palette is set (the same fields /v1/brands/palette takes), the four design colours, the two accent colours and the colours of the text roles are worked out from it on every save; the faces and sizes stay as set. |
text | primary (headlines) and secondary (body), each a font, a size from 0.5 to 3.5 and a color; tertiary, still accepted for older brands but drawn by nothing (the smallest lines use the secondary colour); and frame, the face and size of the top and bottom lines. A role can carry a font file of the brand's own, drawn in preference to its font. |
logo, badge, background, stinger, intro, outro | The files, filled through the slots below. The badge has an enabled switch (absent is on) and adds two lines of text, primaryLine (up to 48 characters) and secondaryLine (up to 64), drawn as a name card. A background is kind color (with a color), image or video, with an enabled switch too (absent is on; off draws the first design colour); without one the first design colour is the ground. stinger, intro and outro each have enabled, clips per ratio and a universal clip asset (see the slots below); a finished clip plays stinger, intro, the clip, outro. made, on these three and on a background, records how Create one with Reelwire in the dashboard made the film (its template, style and content), so that dialogue opens on them again; nothing draws it. |
logoOnDark, mark | Two optional logos besides logo, filled through the slots logo-on-dark and mark: the same logo made for dark backgrounds (light letters), and the symbol alone without the name. Every template draws whichever logo reads on what is behind it, with logo the one for light backgrounds and plates, and the mark where the full logo would be too small or the style's logo space is round. A language's own logo replaces the brand's logoOnDark too; its own mark replaces the brand's mark. |
theme | The Theme every clip of the brand wears where its own Look leaves it to the style: brand, light, dark or bold. Absent, each style chooses. A clip whose look.theme names one keeps it. |
sound | A soundtrack laid over the finished clip, with mode: none, clip, clip-stinger, clip-intro, clip-outro, clip-stinger-intro, clip-stinger-outro, clip-intro-outro or clip-stinger-intro-outro, naming the parts it plays over besides the clip. |
header | The top line of the format templates, { "enabled": false } to leave it out of every clip: the logo, the title line and the style's decoration around them, the content then starting at the top. Absent is on. A single clip leaves it out with hideHeader in its look; intros, outros, stingers and overlays always draw the logo. |
footer | The footer line of the format templates, { "enabled": false } to leave it out of every clip: the brand's footer line (identity.footerPrimary and footerSecondary, kept as they are) and whatever the style draws only to carry it. Absent is on. A single clip leaves it out with hideFooter in its look; the presenter and the source line stay. |
safeAreas | Per ratio, how far from each edge (top, right, bottom, left, in pixels) a template keeps its content, clear of what a platform draws over a video. |
posting | What goes out beside every post, never on the clip: a description (sent as the post's disclaimer), hashtags added to the post's own, and a link (Post text in the brand's dialogue). Together they may take at most 300 characters, so every post keeps room of its own. |
locales | The same brand in another language, by language code: only what that language says differently, such as its own lines, colours, posting text or files. Everything it leaves out is the default's. The brand's name and its soundtrack are the same in every language. |
font is one of the faces the renderer carries: open-sans (the default), bebas-neue, dm-sans,
inter, lato, lora, montserrat, nunito, oswald, playfair-display, poppins, pt-serif,
raleway, roboto, roboto-mono, roboto-slab, rubik, source-sans-3, work-sans,
liberation-sans, liberation-serif or liberation-mono. Any other face is a font file of the
brand's own, in a font slot below.
The 300 characters of posting are counted as a post carries them, the blank line before each part
included, and every language's own posting text is held to the same. A post then carries the longest
of its caption's lengths that still leaves these 300 characters free within its platform's limit. A
save over the limit is a 400 that says how many characters the text takes. A link label with no
address is dropped rather than refused, and an address has to be a full web address, such as
https://www.example.com.
GET /v1/brands returns every brand with a flattened summary rather than the whole document, plus
the channels wearing it. Read the list to find a brand, read one brand to edit it. File addresses
in a brand read with GET /v1/brands/{id} are signed previews, minted afresh on every read and good
for an hour; what the brand stores is the file's id.
See a new brand before you make one
GET /v1/brands/draft composes exactly what create would have composed and stores nothing. It is
how the Add new brand dialogue on Brands shows a real document while the workspace still has no
such brand.
curl http://localhost:4000/v1/brands/draft \
-H "Authorization: Bearer rw_your_key_here"
{
"name": "New brand",
"style": {
"identity": {},
"design": { "first": "#120A16", "second": "#1E1226", "third": "#2e2732", "fourth": "#E0A52E" },
"accent": { "positive": "#E0A52E", "negative": "#E8527F" },
"text": {
"primary": { "font": "open-sans", "size": 1, "color": "#F6EEFA" },
"secondary": { "font": "open-sans", "size": 1, "color": "#A990B5" }
},
"safeAreas": {
"16x9": { "top": 64, "right": 96, "bottom": 72, "left": 96 },
"9x16": { "top": 96, "right": 72, "bottom": 240, "left": 72 }
}
}
}
name is the first free name in the workspace. identity comes back empty on purpose: a form with
"New brand 6" already typed into it is a form whose lazy path publishes a machine's placeholder onto
a clip.
Create one
curl http://localhost:4000/v1/brands \
-H "Authorization: Bearer rw_your_key_here" \
-H "Content-Type: application/json" \
-d '{"name":"Orchid Invest DE"}'
201, with the brand as { "brand": { ... } }. name and style are both optional. Without a
name, Reelwire takes the first free one. Without a style, it composes the same starting document
/v1/brands/draft shows, with the identity filled in from the name rather than left empty, because a
headless create has nobody to type it. A new brand takes the colours and type of the newest brand,
never its files (font files and soundtrack included), its identity, its posting text or its
languages. A name already in use is a 409, and so is a new brand past the number a workspace may
hold (200, unless Reelwire set another limit for it).
The Default brand
Every new workspace starts with one brand, Default brand, worn by its Default channel. Its top
line and footer carry its own name, it has the default colours and type, a posting text that is
plainly an example to replace, and the starter files its
media library came with, linked into its slots: the logo, a badge with the
lines "John Doe" and "Presenter", one universal background picture (background.asset), a headline typeface (Bebas Neue) and a soundtrack over
the clip and whichever of the stinger, intro and outro are on. It also holds a stinger, an intro and an
outro, one universal clip each as asset and no clip for any shape, each with enabled false,
so switching one on is all a customer does. It is an ordinary brand: edit it, or make your own and move the
channel to it.
Change one
PATCH /v1/brands/{id} takes name, enabled and style. A style is checked whole against the
contract, and a refusal is a 400 with a pointer under /style for each problem. A name another
brand has is a 409.
The files in a style you send are mostly ignored. The logo, the badge picture, the background
files and the stinger, intro and outro clips are read from what is stored, not from your body. A file
is attached through a slot, so a document from a stale client cannot introduce a reference the store
does not hold, and a badge, stinger, intro or outro left out keeps what is stored. What your body does
control is everything around those files: the badge's two lines, its kind and its enabled switch,
the stinger, intro and outro enabled switches and their made, and the background. A background
of kind: "color" is taken as you send it; one of kind image or video keeps the stored files, each shape's own,
and takes its enabled and made from your body; and a style with
no background at all saves the brand without one.
Three parts are taken as you send them: the font files under text, the sound, and the
locales. Send them back as GET gave them to you, or the save removes them.
Whatever a document names, only files Reelwire holds are drawn: a file uploaded to the brand or one from the media library. A reference to anything else draws as nothing.
The brand's own name in the style and the name in the list follow each other: setting
style.identity.name renames the record too, unless another brand already has that name, in which
case the record keeps its old one and the save still succeeds. It is the name the brand is known by
in the dashboard and in GET /v1/brands; nothing draws it on a clip, where the four lines are what a
viewer reads.
Frequency watches
A frequency watch says how many posts a week some brands should go out with together, across every channel wearing them, and Reelwire warns before the posts it has planned for them fall short. Several brands, because one brand often has sub-brands of its own (a news brand with its Breaking, Hot and Just In) that all count towards one cadence. Watches are the workspace's, under Brands: read and write.
curl -X POST http://localhost:4000/v1/brands/watches \
-H "Authorization: Bearer $REELWIRE_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "name": "Northwind Markets", "brandIds": ["01M1VFRWE7VHP3H54ESRJQ0SAJ", "01M1VG2Q4B7TS0D8YFAH6KXN3C"], "week1": 3, "week2": 1, "week2Per": "day", "week3": 2, "week3Per": "day" }'
week1 is the first week of the brands' life, counted from the first post of any of them, week2 the second, and
week3 the third and every week after, so new accounts can start with fewer posts. Each is a whole number from 0 to
50, 0 leaving that week unchecked, and at least one must be above 0 (a 400 otherwise). week1Per, week2Per and
week3Per say whether the number beside them is posts a "week" (left out) or posts a "day"; a day's number
counts over the whole week, seven times it, and weekly in the answer is what each week is judged by. brandIds lists 1 to 50 of
the workspace's brands; one that does not exist or was removed is a 404. A brand can be in more than one watch. A
workspace keeps at most 100 watches.
| Route | What it does |
|---|---|
GET /v1/brands/watches | Every watch, each with its next eight weeks |
POST /v1/brands/watches | A new watch: name, brandIds, week1, week2, week3, all required. 201 |
PATCH /v1/brands/watches/{watchId} | Change one: only what you send. brandIds is replaced whole |
POST /v1/brands/watches/{watchId}/delete | Remove one. The brands and their posts stay |
GET /v1/brands/watches/outlook?brandIds=a,b | The next eight weeks of any brands counted together, with no targets: what a watch over them would see |
A watch is answered with what it is and how its next eight weeks look:
{
"id": "01M6A2...",
"name": "Northwind Markets",
"brandIds": ["01M1VFRWE7VHP3H54ESRJQ0SAJ", "01M1VG2Q4B7TS0D8YFAH6KXN3C"],
"week1": 3, "week2": 1, "week3": 2,
"week1Per": "week", "week2Per": "day", "week3Per": "day",
"weekly": { "week1": 3, "week2": 7, "week3": 14 },
"setAt": "2026-10-31T09:12:00.000Z",
"brands": [{ "id": "01M1VFRWE7VHP3H54ESRJQ0SAJ", "name": "Northwind Markets", "enabled": true }],
"state": "soon",
"shortFrom": "2026-11-05T00:00:00.000Z",
"firstPostAt": "2026-08-06T07:00:00.000Z",
"countedFrom": "2026-08-06T00:00:00.000Z",
"weeks": [
{ "start": "2026-10-29T00:00:00.000Z", "end": "2026-11-05T00:00:00.000Z", "week": 13, "target": 14, "made": 9, "planned": 5, "short": false },
{ "start": "2026-11-05T00:00:00.000Z", "end": "2026-11-12T00:00:00.000Z", "week": 14, "target": 14, "made": 0, "planned": 6, "short": true }
],
"channels": [{ "id": "01M1W0A2...", "name": "Instagram", "enabled": true, "brandId": "01M1VFRWE7VHP3H54ESRJQ0SAJ" }],
"uncounted": [{ "channelId": "01M1W0A2...", "channel": "Instagram", "source": "Orders", "said": "Whenever your system posts to it" }]
}
(one brand and two of the eight weeks shown). brands are the brands as they are now: a brand removed since is left
out. made is the posts their channels made in the week before now, planned what the
posting calendar places for them from now to the week's end: the posts of
their post lists and the ready-made feeds that post at set times. A post counts once however many channels or brands it
goes out on. Only posts Reelwire makes are counted: posts somebody puts up by hand are invisible to it. A source that
posts at no set time (a custom feed, a feed that posts when its data changes) cannot be counted ahead and is listed in
uncounted; what it made counts once it is made. Paused channels plan nothing. state is ok while every week
holds its target, soon when a later week falls short (from shortFrom) and short when the week running does.
Changing the numbers moves setAt to that moment.
Whoever publishes for the workspace is emailed two weeks before a short week, one week before it, and once when it begins, and then not again while the watch stays short; nothing is sent for a watch whose brands are all paused or whose channels are all paused, while the workspace is stopped, or about the week in which its numbers were last changed.
Derive a palette
POST /v1/brands/palette works out a whole palette from one brand colour, and checks its contrast.
It stores nothing, so you can call it while somebody drags a colour picker, and a key that may only
read brands may call it: it is a POST because the question is a document, not because it changes
anything.
curl http://localhost:4000/v1/brands/palette \
-H "Authorization: Bearer rw_your_key_here" \
-H "Content-Type: application/json" \
-d '{"stage":"dark","brand":"#77EE58","direction":"green-up"}'
{
"palette": {
"stage": "#0E100D",
"surface": "#1C1F1B",
"surfaceRaised": "#2A2C28",
"line": "#383C36",
"lineStrong": "#525750",
"textStrong": "#F7F9F6",
"textBody": "#E3E5E2",
"textMuted": "#9D9F9C",
"onBrand": "#0B0B0C",
"onPositive": "#0B0B0C",
"onNegative": "#0B0B0C",
"brand": "#77EE58",
"brandMuted": "#21301D",
"brandSecond": "#B2CFFE",
"positive": "#3FB950",
"negative": "#E5534B",
"neutral": "#848882",
"series": ["#77EE58", "#B2CFFE", "#29B2EE", "#787AD8", "#D383D1", "#CC5E6B"]
},
"checks": [
{ "label": "Headline on the stage", "ratio": 18.1, "target": 7, "ok": true },
{ "label": "Headline on a card", "ratio": 15.7, "target": 7, "ok": true },
{ "label": "Body text on a card", "ratio": 13.1, "target": 7, "ok": true },
{ "label": "Labels on a card", "ratio": 6.2, "target": 4.5, "ok": true },
{ "label": "Brand colour on the stage", "ratio": 12.9, "target": 3, "ok": true },
{ "label": "Rise on the stage", "ratio": 7.5, "target": 3, "ok": true },
{ "label": "Fall on the stage", "ratio": 5.2, "target": 3, "ok": true },
{ "label": "Text on a brand fill", "ratio": 13.3, "target": 7, "ok": true }
]
}
The body:
| Field | Required | What it is |
|---|---|---|
brand | yes | The one colour that makes a clip theirs, as a hex string. |
stage | no | dark or light. Defaults to dark. |
background | no | The ground. Absent is a near-black or near-white carrying a trace of the brand's hue. |
direction | no | Which colours mean up and down: green-up, red-up, blue-up or custom. Defaults to green-up. |
positive, negative | no | Only with direction: "custom". |
second | no | The second colour, where two things are compared. |
overrides | no | Individual colours pinned by hand. |
brand is required, and a body without it is a 400 naming /palette/brand.
Brand files
A brand's files go in named slots, one file per slot, uploaded as multipart/form-data exactly as
the media library takes one. A slot holds one thing, so the file uploaded to
it before is deleted, unless another slot of the brand still wears it; a media library file that was
linked there stays in the library. A brand's files count against the media share of the
workspace's storage, together with the media library: a file that does not fit is a 429 that says
how much room is left. A brand holds at most 100 uploaded files (unless Reelwire set another limit), and past that an upload is a
409.
| Slot | Takes | Limit |
|---|---|---|
logo, logo-on-dark, mark | PNG, JPEG, WebP, SVG | 5 MB |
background, background-{ratio} | PNG, JPEG, WebP, SVG, MP4, WebM | 50 MB |
badge | PNG, JPEG, WebP, SVG, MP4, WebM | 20 MB |
sound | MP3, WAV | 20 MB |
stinger, intro, outro, stinger-{ratio}, intro-{ratio}, outro-{ratio} | MP4, WebM | 50 MB |
font-primary, font-secondary, font-tertiary, font-frame | WOFF2, WOFF, TTF, OTF | 10 MB |
curl http://localhost:4000/v1/brands/01M1VFRWE7VHP3H54ESRJQ0SAJ/assets/logo \
-H "Authorization: Bearer rw_your_key_here" \
-F "file=@logo.png;type=image/png"
{
"slot": "logo",
"asset": {
"assetId": "01M2SRB6Q0Y4ZT5N8X2KDM7HCE",
"url": "http://localhost:4000/v1/assets/01M2SRB6Q0Y4ZT5N8X2KDM7HCE/download?exp=...&sig=...",
"mimeType": "image/png",
"fileName": "logo.png"
}
}
201. A clip put in a stinger, intro or outro slot switches that side on, and a sound file put in
while the soundtrack is off switches it on over the clip. A slot name Reelwire does not know is a
404; a file of a type the slot does not take, or a locale that is not a supported language code,
is a 400.
The stinger, the intro, the outro and the background each have one slot per ratio:
stinger-1x1, intro-9x16, outro-16x9, background-16x9, and one universal slot,
stinger-universal, intro-universal, outro-universal and background-universal. A render takes its
own shape's file; for a shape without one, the universal file; and never another shape's, so with neither a
shape draws no clip and a background is drawn on the first design colour. A universal file is scaled to fill
the frame and cropped from the centre, so make it with the main action in the middle and the edges to
spare. The per-shape files are each set's clips (background.assets), keyed by ratio, and the universal
file is its asset (background.asset).
The slots with no suffix, stinger, intro, outro and background, are refused with a 400 that
names the slots to send instead (from 2026-10-10, when the old default was withdrawn; the universal slot is
what it became).
Fonts and sound are not per language: a brand is set in its typeface and sounds the same whichever
language it speaks. The logo, the background, the badge and the clips can be, with ?locale=de on
the upload, the link or the delete, and the file then lands in that language's overlay rather than in
the brand's default.
font-tertiary is still taken, for brands that were given one, but nothing is drawn with it: the
smallest lines are set in the body face, font-secondary.
The reference is written into the stored document at upload time rather than left for a later save, so there is never a file the store holds and nothing points at.
POST /v1/brands/{id}/assets/{slot}/link points a slot at a file already in the media library,
with { "url": "media://{id}" } or { "url": "media://{name}" }, instead of uploading a second copy.
Only a library file of this workspace, named by its id or by its name in the library and of a type the
slot takes, is accepted: a brand never points at a file on somebody else's server. The starter files
every workspace is given are library files like any other, so media://example-sound.mp3 sets the
example soundtrack. Anything but media:// and an id or a name, a web address included, is a 400, a
file the library does not hold is a 404, and a file of a type the slot does not take is a 400. It answers
with the slot and the reference, as an upload does, and a clip or sound file switches its side on
here too. The sound slot also takes a track of Reelwire's sound database by its
ref, media://{id}.
POST /v1/brands/{id}/assets/{slot}/delete empties the slot and answers
{ "slot": "...", "asset": null }, also when the slot was empty already. A file uploaded to the slot
is deleted with it, unless another slot of the brand still wears it; a linked library file stays in
the library. Emptying sound switches the soundtrack off.
The sound database
GET /v1/sound-library lists Reelwire's ready-made soundtracks, which any brand may wear: tracks recorded for
Reelwire, royalty free for clips made with Reelwire, each about a minute long and made to loop seamlessly. A
track stays in the database, so giving it to a brand counts against none of the workspace's storage and adds
nothing to its media library. Reading it takes media:read.
curl http://localhost:4000/v1/sound-library -H "Authorization: Bearer rw_your_key_here"
{
"genres": ["ambient", "acoustic", "corporate", "electronic", "jazz", "rock", "pop", "hip-hop", "lo-fi", "cinematic", "funk", "classical", "latin", "world"],
"tracks": [
{
"id": "01M1VG3T8Q2C7K9D4E5F6G7H8J",
"title": "Morning Ledger",
"genre": "corporate",
"energy": 3,
"bpm": 112,
"description": "Bright piano and plucked synths over a steady clap beat. Confident and friendly.",
"durationMs": 60000,
"mimeType": "audio/mpeg",
"byteSize": 961536,
"fileName": "Morning Ledger.mp3",
"ref": "media://01M1VG3T8Q2C7K9D4E5F6G7H8J",
"url": "https://app.reelwire.io/v1/assets/01M1VG3T8Q2C7K9D4E5F6G7H8J/download?exp=...&sig=..."
}
]
}
energy runs from 1, calm, to 5, punchy. url plays the track for an hour and is no address to keep; ref is
what the sound slot's link takes:
curl -X POST http://localhost:4000/v1/brands/01M1VFRWE7VHP3H54ESRJQ0SAJ/assets/sound/link -H "Authorization: Bearer rw_your_key_here" -H "Content-Type: application/json" -d '{ "url": "media://01M1VG3T8Q2C7K9D4E5F6G7H8J" }'
The description comes in the language of whoever asks, as the catalogue's words do. Reelwire keeps the database: a track taken out of it stays with the brands already wearing it, and can no longer be given to another.
Read a website for its brand
POST /v1/brands/read-website with { "url": "beckers-garage.example" } reads one public page, a business's
own website, and answers website: what a brand is made of, for an assistant to propose a brand from.
| Field | What it is |
|---|---|
logos | Logo candidates as addresses, the business's structured data first, then images the page calls a logo. Bring one in with POST /v1/media/import. |
inlineSvgLogo | The page draws its logo inline as SVG, which has no address: ask for the file instead. |
colours, neutrals, themeColour | The colours its styles use most, weighted towards the ones it names as its brand's, greys, white and black apart. |
fonts | The typefaces its font links and styles name. |
socials | Its social profiles, listed and not read: those answer a server with a sign-in page. |
pictures | The pictures on the page as addresses, with alt where it gives one: what it offers for sharing first, then what stands on it, logos and small images left out. Bring one in with POST /v1/media/import. |
pages | The site's own pages it links to, each its url and text, the product, service, news and about pages first, for reading on with another call. |
business | Its own details where it gives them: name, type, telephone, email, address, openingHours. |
title, description, menu, headings, paragraphs | Its own words. |
Nothing is stored. The page is fetched as a media import is fetched: HTTPS only (an address typed without one is asked over HTTPS), never an internal address, redirects checked, at most 3 MB, with up to three of its own stylesheets. Everything in the answer is the website's text, and the MCP server hands it to an assistant marked as outside text. It needs Brands: read, and a workspace may read thirty websites an hour.
The brand kit
One brand as a design system to share outside Reelwire, as the dashboard's Brand kit makes it: a PDF or one HTML page in the language of whoever asks, with the logos, every colour a clip draws and its contrast, the type set in its own fonts, the lines a clip draws, the background and presenter, three frames of every stinger, intro and outro file, the transition and sound, a frame of a few clips drawn in the brand, the post text, the languages and the channels wearing it. See Brand kit for what it holds.
GET /v1/brands/{id}/kit answers its facts at once, as kit, the document an assistant presents:
| Field | What it is |
|---|---|
identity | The title line, footer line and slogan, each half as written. |
lines | header and footer: whether the format templates draw the logo and title line and the footer line. |
logos | logo, logoOnDark and mark: the file each holds, or null. |
colours | given, the palette the brand was given (null for a brand on its four design colours); roles, every colour a clip draws by its role and hex; series, the six chart colours; checks, each pairing's contrast against its target. |
type | The headline, body and frame fonts and the small text: font, size, colour and the brand's own font file. |
background, badge | Whether each is on, and its files per shape. |
films | stinger, intro and outro: on or off, the file per shape and the universal one, and how Reelwire made it. |
transition, sound | The transition between the parts, where the soundtrack plays and its file. |
posting, languages, channels | The post text, what each language changes, and the channels wearing the brand. |
The file is made as a job, since its pictures are drawn one at a time, a minute or two:
curl -X POST http://localhost:4000/v1/brands/01M1VFRWE7VHP3H54ESRJQ0SAJ/kit/jobs \
-H "Authorization: Bearer rw_your_key_here" \
-H "Content-Type: application/json" \
-d '{"format":"pdf"}'
Sample posts about the business, for an assistant: clips, up to six, drawn first in place of the templates'
own examples. Each holds templateId, variant (its style, the first where left out), version (the newest where
left out), ratio and locale (the brand's first channel's where left out, else 9x16 and English), title
(printed under its picture after Sample post) and data, the content in the template's shape, as
Create clip takes it. Every clip is checked against its template before the job starts, and
a mistake is a 400 naming its field as /clips/{n}/..., with nothing started. With clips given, the kit draws them,
then the posts of the brand's post lists and its ready-made feeds, and no template example and no custom feed, whose
picture is its template's example too. They are taken from an API key, which is what an assistant's connection is, and
refused from a signed-in session: the dashboard's Brand kit card draws what the brand's channels post.
curl -X POST http://localhost:4000/v1/brands/01M1VFRWE7VHP3H54ESRJQ0SAJ/kit/jobs \
-H "Authorization: Bearer rw_your_key_here" \
-H "Content-Type: application/json" \
-d '{"format":"pdf","clips":[{"templateId":"tpl_dd7e6b264fb8","variant":"Corporate glass","ratio":"9x16","locale":"en","title":"Ten years in Leeds","data":{"clipData":{"label":"Years serving Leeds","value":10,"unit":"years"}}}]}'
It answers 202 with a jobId. GET /v1/brands/{id}/kit/jobs/{jobId} says how far it is (state queued,
working, done or failed, and done of total pictures), and once made the pictures it hands over one by one at
GET /v1/brands/{id}/kit/jobs/{jobId}/pictures/{index} (the logo, the outro's end card, the intro, the stinger and
the clips, up to eight); GET /v1/brands/{id}/kit/jobs/{jobId}/file downloads it. GET /v1/brands/{id}/kit/jobs
lists the kits of the brand this credential asked for, GET /v1/brands/kits those of every brand, each naming its
brandName, and POST /v1/brands/{id}/kit/jobs/{jobId}/cancel stops one
or takes a made one off the list. GET /v1/brands/{id}/kit/link?jobId= answers a link to the file that needs no
credential, for two hours, which an assistant hands to the person. A workspace's kits are made one after the
other, at most five waiting; a made kit is kept an hour, or as long as a link to it works, and an update of Reelwire
forgets it. Every one of these
needs only Brands: read: a kit changes nothing in the brand and counts no video.
Duplicating a brand
POST /v1/brands/{id}/duplicate with { "name": "Northwind DE" } makes a second brand like this one
and answers 201 with it, as create does. Everything is copied: the whole style document, every
language in it, and enabled. The name given is the copy's own, as name and as identity.name, so
the list and the copy's settings agree. It is required, and a name another brand has is a 409.
Files uploaded to the brand are copied, so the copy's slots hold files of their own with new ids, and
the bytes count against the storage allowance a second time (a 429 when there is no room, and nothing
is made). Files from the media library (media:// references) are not copied: both brands point at
the same library file, as two brands choosing it would. Needs Brands: write.
Deleting a brand
Every channel wears a brand, so POST /v1/brands/{id}/delete on a brand in use is a 409 whose
message names the count: "Brand 1 is used by 5 channels. Choose the brand they use instead."
Send { "moveTo": "{another brand id}" } and the channels are moved to that brand in the same step;
a moveTo naming no other brand of the workspace is a 404. The channels array on GET /v1/brands
tells you in advance how many there are. The answer is
{ "deleted": "...", "moved": 5 }. A removed brand leaves the list but not the history: clips it
already drew keep its look, and its name gets " (removed)" on the end, so a new brand can take it.
Mistakes people make
Sending file references in a style PATCH. The logo, badge, background and clips are ignored,
and always were. Upload to the slot, or link a library file to it.
Leaving the font files under text, sound or locales out of a style PATCH. Those are
taken as sent, so a document missing them saves a brand without its font files, its soundtrack or its
languages.
Leaving background out of a style PATCH. The brand is saved without a background, picture or
video included. Send it back as GET gave it to you.
Uploading one intro clip and expecting it to fit everywhere. It is used everywhere, fitted onto
black in the other shapes: a 9x16 channel with only intro or intro-16x9 uploaded opens with a
letterboxed clip. Upload intro-9x16 for a clip that fills the frame.
Editing a brand to change one channel. Every channel wearing it changes. Make a second brand.
Expecting /v1/brands/palette to change a brand. It only answers. A brand takes the colours when
the same fields are saved as its style.palette with a PATCH, which works out its design,
accent and text colours from them.
Deleting a brand without moveTo. A 409 that names the number of channels, every time.