Skip to main content

Notes and Colors

Not everything on a Playbook board is a file. Two kinds of asset carry their content inline instead of pointing at one:

  • A note is a sticky-note asset holding prose — a brief, a caption, a to-do, context for the assets around it. It has a background colour and a card height.
  • A colour swatch holds a single colour. Two or more swatches grouped together become a palette.

Both are ordinary assets: they live on a board, take tags and statuses, appear in search, and are fetched, updated and deleted through the same endpoints as any other asset. What makes them different is that there are no bytes to fetch, so they are created synchronously — the response is the finished asset, with no is_skeleton polling step of the kind uploading requires.

Prerequisites

  1. Access token — an API token with the write scope, sent as a Bearer token.
  2. Organization slug (slug) — your workspace identifier, e.g. coolclient-ltd.
  3. Board token (collection_token) — optional. If omitted or unknown, the asset falls through to a default board rather than erroring, so validate it first if it matters. Fetch board tokens from the boards endpoint.

Create a note

Notes share the generic asset-create endpoint with URL ingest. Sending text instead of uri is what makes the new asset a note.

Endpoint

POST /v1/{slug}/assets
Authorization: Bearer YOUR_TOKEN
Content-Type: application/json

Request body

All fields live under an asset key.

FieldTypeRequiredDescription
textstringyesThe note's content. Sending this instead of uri is what creates a note.
titlestringnoTitle shown above the note.
descriptionstringnoLonger description.
colorstringnoBackground colour as a hex code — use a note palette colour, see below. Renders white when omitted.
heightintegernoCard height in pixels.
collection_tokenstringnoBoard to add the note to.
group_tokenstringnoAn existing group asset to place the note inside.
tagsarraynoManual tags applied on creation.
statusstringnoStatus label applied on creation.

color and height apply to notes only and are ignored when uri is present.

Sample request

curl -X POST https://api.playbook.com/v1/coolclient-ltd/assets \
-H "Authorization: Bearer ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"asset": {
"text": "Ship the palette by Friday",
"title": "Deadline",
"color": "#F4EFD9",
"height": 320,
"description": "from the kickoff call",
"collection_token": "boardToken123"
}
}'

Sample response

HTTP/1.1 200 OK
Content-Type: application/json

{
"data": {
"token": "assetToken123",
"title": "Deadline",
"text": "Ship the palette by Friday",
"text_type": "note",
"average_color": "#F4EFD9",
"primary_height": 320,
"description": "from the kickoff call",
"collection_token": "boardToken123",
"is_link": false,
"is_skeleton": false
}
}

The note's prose comes back in text, and text_type is note. The background colour is reported as average_color — the same field an image uses for its dominant colour.

Note background colours

color is stored verbatim and is not validated, but only the note palette has a dark-mode counterpart. Playbook matches the stored string exactly, including case, against:

#ffffff · #F1F3F7 · #ECEAF2 · #F4EBE0 · #F4EFD9 · #E6EEE7 · #F1E5E5 · #ECEAE6

An off-palette colour is hard to read in dark mode

A note always switches to light text in dark mode, but its background is only darkened when the value matches one of the colours above. Send a brand colour like #FFE8A3 — or a palette colour in the wrong case, like #f4efd9 — and the note keeps its light background under light text. Stick to the list, copied exactly, unless you only care about light mode.

To edit a note later, PATCH /v1/{slug}/assets/{token} accepts text alongside the usual title and description.


Create colour swatches

Endpoint

POST /v1/{slug}/assets/create_colors
Authorization: Bearer YOUR_TOKEN
Content-Type: application/json

Request body

All fields live under a colors key.

FieldTypeRequiredDescription
color_codesarrayyes1–100 colour values. Each is a hex code, CMYK, a two-stop gradient, or a Pantone name — see below.
titlesarraynoTitles positionally aligned with color_codes. A null or missing entry falls back to the code itself; an explicit "" is kept as an empty title.
collection_tokenstringnoBoard to add the swatches to. board_token is accepted as an alias.
parent_tokenstringnoMerge the new swatches into this existing asset or group instead of forming a palette of their own.

What counts as a colour value

Four forms are accepted — the same four the web app can render — and anything else is rejected with 406 before a single swatch is created:

  • A hex code#RRGGBB or the three-digit shorthand #RGB, any casing. #FF0000 and #f00 both work; FF0000 without the # does not.
  • CMYKCMYK(0%, 100%, 100%, 0%): four numbers, percent signs optional.
  • A two-stop gradient — two hex codes joined by the arrow (U+2192), e.g. #FF0000→#0000FF. The gradient angle is not part of the value; the web app keeps it in the asset's title as 45°.
  • A Pantone name — written without the PANTONE prefix and matched exactly, including case, against Playbook's Pantone list: 185 C, 185 CP, 20-0001 TPM.
The PANTONE prefix is the common mistake

"PANTONE 185 C" — the way the name is normally written — is not a valid value. The correct form is "185 C". This is enforced rather than stored, because a swatch whose value resolves to no colour is created successfully, counts toward the board's asset total, and then renders as an empty slot with nothing to show.

Values are stored verbatim — Playbook does not rewrite a hex code's casing or expand a Pantone name into its hex on write. text reads back exactly what you sent.

Sample request

curl -X POST https://api.playbook.com/v1/coolclient-ltd/assets/create_colors \
-H "Authorization: Bearer ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"colors": {
"collection_token": "boardToken123",
"color_codes": ["#FF0000", "#00FF00", "#0000FF"],
"titles": ["Red", "Green", null]
}
}'

Sample response

HTTP/1.1 200 OK
Content-Type: application/json

{
"data": {
"assets": [
{ "token": "aA1", "title": "Red", "text": "#FF0000", "text_type": "color", "is_group": false },
{ "token": "bB2", "title": "Green", "text": "#00FF00", "text_type": "color", "is_group": false },
{ "token": "cC3", "title": "#0000FF", "text": "#0000FF", "text_type": "color", "is_group": false }
],
"group": {
"token": "dD4",
"is_group": true,
"text": "#FF0000|#00FF00|#0000FF",
"text_type": "palette"
}
}
}

assets comes back in the order you sent the codes. The third swatch was given a null title, so it fell back to its own code.

Palettes

Sending two or more codes merges them into a palette: a group asset whose text_type is palette and whose text is the member codes joined by |. You do not ask for a palette — grouping colour swatches produces one.

  • One code, no parent_token → a single loose swatch, and group comes back null.
  • Two or more codes → a palette, returned as group.
  • Any number of codes with parent_token → the swatches are merged into that asset's group. If the group ends up containing anything that is not a colour, it is a plain group rather than a palette, and its text_type is null. Note that grouping moves the swatches onto the parent's board, whatever collection_token said.

To rearrange a palette after the fact — add more swatches to it, or break it apart — use the grouping endpoints described in Advanced Asset Management: POST /v1/{slug}/assets/group_assets and POST /v1/{slug}/assets/ungroup_assets, exposed over MCP as group_assets and ungroup_assets.

One thing to know before ungrouping: a group left with fewer than two assets is dissolved. Its last asset is freed as well and the group asset itself is deleted, so ungrouping one swatch from a two-swatch palette leaves you with two loose swatches and no palette. The response lists only the assets you named, not every asset that changed — re-read the group if you need its state.

Errors

Errors use the standard envelope, { "errors": [{ "message": "...", "extensions": { "code": "..." } }] }.

StatusMessageCause
406color_codes cannot be emptycolor_codes was missing or an empty array.
406color_codes must be non-blank stringsAn entry was empty, whitespace, or not a string (a JSON number or boolean counts).
422color_codes limit is 100More than 100 codes in one call.
403Not authorizedThe token lacks the write scope, or you cannot write to the board.
404Not foundparent_token does not name a live asset in this workspace — a trashed asset counts as absent.

The whole call runs in one transaction, so a rejected batch leaves no partial palette behind.

Swatches do not count against your workspace's asset ceiling: that limit counts stored files, and a swatch has no file. A workspace at its file limit can still create colours.


Reading notes and swatches back

Every asset endpoint reports text assets through the same three fields:

FieldMeaning
text_typenote, color, palette, or null for an ordinary file or link.
textThe note's prose, the swatch's colour value, or a palette's member codes joined by |.
average_colorA note's background colour. (For an image this is its dominant colour instead.)

Text assets have no bytes, so display_url, thumbnails, size and media_type are empty for them. Render a note or a swatch from text and average_color.


An image's own colours

Everything above is about colours somebody authored. An image also has colours of its own, and those are a separate field.

When Playbook renders an asset's preview it extracts the image's primary colours and stores them. Every asset endpoint returns them as colors: an array of #RRGGBB strings, most dominant first, up to six. These are the small swatches the web app draws underneath an asset.

{
"token": "3EiEw6UGeLArdBVVxo4XDqQr",
"media_type": "image/jpeg",
"average_color": "#888579",
"colors": ["#0B0B0B", "#2A3A2E", "#5A5648", "#A9A396", "#D8D5C6", "#8E8B7C"]
}

Three things worth knowing before you build on it:

  • colors is always an array, and it is empty for anything without a rendered preview — notes, swatches, links, and file types that never produced one. An empty array means the image was never analysed, not that it has no colours. It never comes back null.
  • colors and average_color are not the same thing. average_color is a single blended value, and on a note it carries the note's background colour instead. colors is the palette.
  • You cannot set colors. It is derived at preview time and read-only; to put a colour on a board, create a swatch with create_colors as described above.

The two halves of this page compose: read colors from the images on a board, then post the codes you like back to that same board with create_colors to save the palette as real swatches. No image download and no colour analysis of your own is involved.


Over MCP

Both operations are available to AI assistants through the hosted MCP server, so you can create them in natural language instead of writing calls:

ToolWhat it does
create_noteAdd a note to a board, with optional background colour and card height. Needs write.
create_colorsAdd 1–100 swatches; two or more come back merged into a palette. Needs write.

create_colors returns assets, group (null when no palette was formed) and count. get_asset carries text, text_type and average_color, so an assistant can read back what it created. The list and search tools return a leaner projection that keeps text_type — enough to tell a note from a swatch from a file — but not the text itself; fetch that with get_asset.

get_asset also carries the colors array described above, which is what lets an assistant build a palette from a board: list the board, read each image's colors, then hand the codes it picks to create_colors. Those colours are on the detail response only — list and search rows omit them, so ask for an asset by token.