Design
A design is the white-label theme applied to your team's public passports — the pages a QR code resolves to. Colours, corner rounding, shadow depth, logo, fonts, the page's structural style, how each kind of value is drawn and how each part of the page is styled (the top bar, the masthead, the chapters, the cards, the tables, the figures, the charts, the spacing): everything a visitor sees that is yours rather than Synex's.
There is exactly one design per team, and it is addressed without an id. A key is pinned to one team and a team has one theme, so there is nothing for an id to disambiguate — /v1/design is the whole address. A composition can override any of it, but the logo, for the passports of one ontology.
The abilities are the whole gate
Neither design:read nor design:write is in a key's default ability set — grant them explicitly when you mint the key.
They are non-default for a reason worth stating plainly: a key minted to write passport values has no business restyling every public passport the team has ever released. One careless grant turns a data integration into something that can repaint the customer-facing surface of the whole product line.
There is deliberately no gate beyond the ability. In the Synex application a member edits branding if their role grants design:write — the default Editor role does — and a key is capped by what its owner may currently do, so asking more of the key here would make the API stricter than the surface it mirrors. A rule that only exists on one of two equivalent paths is not a security boundary, just a difference to work around.
The design model
Twenty-six settings beside the object discriminator: the five original ones (palette, rounding, shadow, logo, font), thirteen that shape the page's structure, colours, type and components (see Presets, colour modes and components), and the eight part-style blocks. Null means "not set", and a setting that is not set inherits (see Resolution order).
- Name
object- Type
- string
- Description
Always
design.
- Name
palette- Type
- array
- Description
An ordered colour scale of
{ lightness, hex }entries, step 50 first.[]when unset. At most 32 entries; the renderer reads the first 11.
- Name
rounding- Type
- integer
- Description
Corner-rounding level, 1–6. Nullable.
- Name
shadow- Type
- integer
- Description
Shadow-depth level, 1–6. Nullable.
- Name
logo- Type
- string
- Description
A
file_id from your team's Drive. Nullable.
- Name
font- Type
- string
- Description
The body font, a family from the font catalogue. Nullable.
- Name
preset- Type
- string
- Description
The passport's structural style:
standard,editorial,technicalorsoft. The default for every setting left null. Nullable; null reads asstandard.
- Name
mode- Type
- string
- Description
simpleoradvanced: whether the advanced settings apply. Nullable.
- Name
color_mode- Type
- string
- Description
simplegenerates every colour frompalette;advancedusescolors. Nullable.
- Name
appearance- Type
- string
- Description
A
lightor adarkpage. Nullable; null follows the preset.
- Name
colors- Type
- object
- Description
The advanced-mode colours:
background,text,muted,rule,surfaceandaccent, each#rrggbb(read back in lower case) or null.nullwhen no role is set.
- Name
font_heading- Type
- string
- Description
The face for names, chapter titles and card titles, from the catalogue. Nullable; null follows the preset, then
font.
- Name
font_figures- Type
- string
- Description
The face for numbers, measurements and identifiers, from the catalogue. Nullable; null follows the preset, then
font.
- Name
labels- Type
- string
- Description
How field labels and small headings are set:
muted,capsorstrong. Nullable.
- Name
navigation- Type
- string
- Description
One long page of numbered
chapters, or a strip oftabs. Nullable.
- Name
hero- Type
- string
- Description
How the product image and the name open the page:
split,banner,compactorcentered. Nullable.
- Name
frames- Type
- string
- Description
Cards on hairline rules (
open) or inframedsurfaces. Nullable.
- Name
components- Type
- object
- Description
The default display style of each of the seven kinds of value.
nullwhen no kind is set.
- Name
component_options- Type
- object
- Description
Design-wide display defaults per kind; only
imagehas options.nullwhen no option is set.
- Name
top_bar- Type
- object
- Description
The top bar: layout, logo size and which parts show.
- Name
masthead- Type
- object
- Description
The masthead: title size, key figures, the product image and which parts show.
- Name
chapters- Type
- object
- Description
The chapter headers, the chapter index and the tabs.
- Name
sections- Type
- object
- Description
The card headers and the gallery's columns.
- Name
fields- Type
- object
- Description
The rows of field lists and children tables.
- Name
figures- Type
- object
- Description
The stat grids and key figures.
- Name
charts- Type
- object
- Description
The charts' colours, grid, legend and bars.
- Name
rhythm- Type
- object
- Description
The spacing between chapters and cards, and the content's width.
The eight part-style blocks are objects even when nothing is set: every leaf is there, null until set.
palette is an ordered scale, not a bag of settings
The array's position carries meaning: entry 0 is step 50, entry 5 is step 500 and entry 10 is step 950. The renderer reads entry 5 as the brand colour. It builds the page background from step 50 blended toward white, or, on a dark page, from step 950 blended toward black; the other steps give the text, the shades between and the chart colours. Reordering the entries restyles the passport even though every colour in it is unchanged, so treat the palette as a scale you author top to bottom rather than as a set of named options you can shuffle.
Each entry is {lightness, hex} — lightness on the familiar 50/100/200/…/900/950 scale, and hex as #RRGGBB. The Synex application writes 11 entries; at most 32 are accepted, and the renderer reads the first 11.
Send all 11 steps. A palette with fewer usable entries (a 50…900 scale, a single colour, an entry whose hex is null) is completed rather than ignored: each usable entry keeps its position, and the others are filled from the brand scale of the usable entry nearest step 500 (entry 5 itself when it is usable; between two equally near, the darker). Position decides the step, with one exception: a palette of a single entry that carries a lightness is the step it names, so {"lightness": 500, …} is the brand colour and the scale is built around it. A single entry without one is step 50, so the brand scale is built from that colour and the page takes a tint of it. A palette with no usable entry draws in a neutral slate.
rounding and shadow are levels, not measurements
Both are integers from 1 to 6, and both index the renderer's own class maps. rounding: 3 is not "3px" and shadow: 2 is not a blur radius — they select the third and second steps of scales the renderer defines. Move them a step at a time and look at the result; there is no unit to compute with.
The font catalogue
font is the body font; font_heading and font_figures take the same values. A font is one of 24 families, and the viewer loads them from Synex's own servers, never from a third party.
| Category | Families |
|---|---|
| Sans | Inter, Roboto, Open Sans, Nunito, Poppins, Work Sans, DM Sans, IBM Plex Sans |
| Serif | Merriweather, Playfair Display, Lora, Crimson Pro, Cormorant Garamond, Source Serif 4 |
| Mono | Fira Code, JetBrains Mono, Source Code Pro, DM Mono, IBM Plex Mono |
| Display | Archivo, Montserrat, Bebas Neue, Oswald, Raleway |
A PATCH matches a family without case and without regard to hyphens, underscores or spaces between words, and stores the catalogue's own spelling: montserrat and bebas-neue are stored as Montserrat and Bebas Neue. A family outside the list is 400 parameter_invalid ("Pick a font from the list.").
Older teams stored fonts free-form, and the Synex Design page used to store lower-case slugs, so a stored value is read the same way: one that matches the catalogue comes back in its canonical spelling, and one that does not comes back as null. A PATCH of any setting writes the design back in that normalised form, so it also clears a stored font that is outside the catalogue.
An unset design reads as null, not as a default
A team that has never set a theme gets palette: [], null for every other setting and the eight blocks as objects of nulls — the Unset response under Retrieve the design. Those nulls mean "not set", not "set to the default". The renderer's own fallbacks decide what an unthemed passport looks like, and reporting those fallbacks here would claim the team had picked something it never picked — which is exactly the claim that breaks the moment the renderer's defaults change.
If you are mirroring a design into your own system, carry the nulls through rather than substituting values for them.
logo is an id, not a link
GET /v1/design returns the logo's file id and stops there. To get the bytes, turn it into a link yourself with GET /v1/files/{file}/download.
A presigned URL is a bearer credential with a clock on it. Minting one on every read of the theme would hand you a signature you did not ask for, on every poll, that you could not cache for longer than its own short lifetime — and it would put a signing round-trip inside a call that is otherwise a single cheap read.
Presets, colour modes and components
The thirteen settings below describe a passport's structure, colours, type and components. Every one of them is nullable, and null means "inherit" (see Resolution order). The eight part-style blocks have a section of their own, Part styles.
| Setting | Values | What it decides |
|---|---|---|
preset | standard, editorial, technical, soft | The passport's structural style, and the default for every setting left null. Null reads as standard. |
mode | simple, advanced | Whether the advanced settings apply at all (see The mode). |
color_mode | simple, advanced | simple generates every colour from palette, for the appearance. advanced uses colors. |
appearance | light, dark | A light or a dark page. Null reads as the preset's. |
colors | an object of six roles | The advanced-mode colours: background, text, muted, rule, surface, accent. |
font_heading | a catalogue family | The face for names, chapter titles and card titles. Null follows the preset, then the body font. |
font_figures | a catalogue family | The face for numbers, measurements and identifiers. Null follows the preset, then the body font. |
labels | muted, caps, strong | Field labels and small headings in the muted colour, in capitals, or in sentence case at weight 600 in the text colour. Null follows the preset. |
navigation | chapters, tabs | One long page of numbered chapters, or a tab strip. |
hero | split, banner, compact, centered | How the product image and the name open the page. centered is one centred column with the key figures in a row and the image below. |
frames | open, framed | Cards on hairline rules, or in framed surfaces. rounding and shadow apply to framed surfaces. |
components | an object of seven kinds | The default display style of each kind of value. |
component_options | an object of options per kind | The design-wide display defaults of a kind of value. Only image has options. |
Presets
A preset is a structural style: heading weight, corner shape and spacing live in the viewer's stylesheet and follow the preset. It also supplies a default for each setting you leave null; for labels that is caps on editorial and technical, and muted on standard and soft.
| Preset | Character |
|---|---|
standard | The team's font for every face, a split hero, open frames, a light page and the team's rounding. |
editorial | Cormorant Garamond headings over DM Sans, square corners, plain images. |
technical | A dark page with DM Sans and JetBrains Mono for figures and identifiers, square corners, framed images. |
soft | DM Sans throughout, a banner hero, round corners and pill-shaped chips, card files and badge yes/no values. |
A preset sets no part-style leaf: every block leaf defaults to the value in the part-style tables, whichever preset is chosen. The exceptions are the masthead's image_side, image_corners and image_fit, whose defaults follow the hero layout (a null hero takes the preset's) and, for image_fit, the preset itself.
Presets never set colours; colours come from palette or colors. Choosing a preset in the Synex application also writes the preset's fonts, rounding and appearance into the design, so you can see and change each, and leaves the hero, the component styles and every part style alone. The API does not write any of them: PATCH {"preset": "technical"} changes preset and nothing else, and the settings you left null follow the preset when a passport is drawn.
Colour modes
In simple mode the viewer generates the six colour roles from palette, for the appearance, and holds them to its contrast rules: text on the background at 12:1 or better, muted text at 4.5:1 or better on the background, on surfaces and on framed cards. colors is ignored.
In advanced mode colors is used as given. A role left null is derived: the background and text from the palette, the muted, rule and surface colours from the text and background, and the accent from the palette's brand colour. So you can set only background and accent.
The roles you set are used verbatim and are not checked for contrast:
text the same colour as the background renders as text the same colour as the
background. The colour of links and focus rings is still adjusted until it
reads at 4.5:1 on the background, on surfaces and on framed cards, and a
muted you leave null is derived to read at 4.5:1 on all three.
Components
components sets how each kind of value is drawn by default. A kind is a group of property types, and a style is one way of drawing it:
| Kind | Property types | Styles |
|---|---|---|
image | image | plain, captioned, framed, file |
file | file | row, card, button, link |
figure | number, measurement | figure, inline, meter, ring |
boolean | boolean | text, badge |
tags | tags | chips, outline, list |
long_text | long text | paragraph, lead, collapsed |
range | range | track, text |
Short text and date values have one look and no kind. A figure left null is drawn as a large figure in a stat grid and as an inline value elsewhere. An image or file value with no link is not drawn as a broken image or a dead download.
A single placement can override its kind's default with its own display: see Compositions.
Component options
component_options sets how every value of a kind is displayed, the way a placement's display_options set it for one value: in the passport's sections and among the masthead's key figures. Only image has options. The masthead's product image is styled by the masthead block's image_ leaves instead.
| Option | Values | Default | What it does |
|---|---|---|---|
aspect | original, 1:1, 4:3, 3:2, 16:9 | original | The shape of every image a placement does not shape itself; original keeps the image's own. |
fit | cover, contain | cover | Fill the shape and crop, or show the whole image. Applies only to an image with a shape (an aspect other than original). |
corners | square, rounded, extra | by style | None; the design's rounding (its inner radius on every picture, its full radius on the framed card); or twice the design's rounding and at least 1.5rem. Null keeps each style's own: plain square, the others rounded. |
backdrop | surface, none, tint, white | none | What shows around a contained image or behind a transparent one: the surface colour, nothing, the accent washed out, or white in both appearances. |
aspect, fit and backdrop apply to the plain, captioned and framed styles; the file style ignores all three, and only corners reaches its thumbnail. A placement's display_options win, key by key: a placement that sets its own aspect keeps it and still takes the design's fit when it sets none. corners and backdrop come from the design alone, and width stays a placement option.
component_options reads back as null until an option is set, and then with every option, null for those not set:
{ "component_options": { "image": { "aspect": "4:3", "corners": "rounded" } } }
Resolution order
Each setting is resolved separately, for every passport, from the first of these that is not null:
- the composition's
designblock, - the team's design, this resource,
- the preset's default, where the preset is itself resolved composition first, then team, then
standard, - the viewer's default (for
font_headingandfont_figures, the resolved body font).
components resolves per kind, component_options per option (with no preset step) and each part-style block per leaf (and per show part) in the same order, and a placement's own display and display_options sit above all of them. In simple mode the advanced settings skip steps 1 and 2 and resolve from the preset alone (see The mode).
Part styles
Eight objects restyle one part of the passport each: top_bar, masthead, chapters, sections, fields, figures, charts and rhythm. Every leaf is nullable and null inherits, leaf by leaf, in the resolution order. The Default column is what an unset leaf draws, which is the passport as it has always looked: nothing changes until you set a leaf. A show object holds one true or false per part and null to inherit; every part shows by default.
The mode
mode decides whether the advanced settings apply. In simple the passport resolves color_mode, colors, font_figures, labels, hero, frames, shadow, components, component_options and every part-style leaf from the preset alone and ignores what is stored; in advanced every layer applies. Switching never deletes a value, so going back to advanced brings the stored ones back.
Each design, the team's and each composition's, has its own mode: the mode it stores, or, when that is null, advanced if it stores an advanced setting. An advanced setting is any of those listed above but shadow and font_figures (every older save stores a shadow, and picking a preset writes the figures face). A composition's mode wins over the team's, and only a winning simple gates anything:
- A composition that sets
frames,hero,components, a part style or another advanced setting isadvancedunless it saysmode: "simple", even over a team insimple, and its settings apply. Like a composition set toadvanced, it also brings back the advanced values the team has stored. - A composition with no advanced setting and no
modefollows the team's mode. - A design with no
modeand no advanced setting gates nothing.
A PATCH that sets an advanced setting and no mode (left out or null) stores mode: "advanced", so what you set is what a reader sees. To store an advanced value while the team stays in simple, send mode: "simple" in the same body. The Synex Design page writes the mode it shows on every save: the one the person chose, else the one this section infers.
top_bar
| Leaf | Values | Default | What it does |
|---|---|---|---|
layout | overlay, solid, floating | overlay | Transparent over the masthead and a translucent surface once scrolled; the surface and a rule from the start; an inset bar with the design's rounding and a shadow. |
logo_size | s, m, l | m | The height of the logo. |
show | logo, qr, language, audience, identity | all shown | identity is the name, reference and status that appear once the page scrolls. Hiding audience leaves the ?audience= link working. |
masthead
| Leaf | Values | Default | What it does |
|---|---|---|---|
title_size | s, m, l, xl | l | The size of the product name; l is the size each hero layout starts from. |
figures | ruled, plain, tiles | ruled | Key figures with a rule above each, bare, or each on a surface of its own. |
image_side | start, end | end for centered, else start | The side the product image sits on: left or right for split and compact, above or below the text for banner and centered. On a phone split and compact keep the image first whatever the side. |
image_size | s, m, l | m | The size of the product image; m is the size each layout has always drawn. |
image_frame | bleed, inset | inset for compact and centered, else bleed | A split or banner image to the page edge, or inset from it, where it takes image_corners. compact and centered always inset the image. |
image_corners | square, rounded, extra | rounded for centered and an inset split or banner, else square | The corners of an inset image: none, the design's rounding, or twice it and at least 1.5rem. A bleed image has none. |
image_fit | cover, contain | contain for compact, centered and the technical and soft presets, else cover | Fill the frame and crop, or show the whole product. |
image_focus | center, top, bottom, left, right | center | Where a filled image is anchored when it is cropped. Ignored with contain. |
image_backdrop | surface, none, tint, white | surface | Behind a contained or transparent product: the surface colour, nothing, the accent washed out, or white in both appearances. |
show | logo, ids, status, figures, image, explore | all shown | ids is the reference and passport ID line; status the status, date and tier badges; explore the "Explore the passport" link. Hiding image makes the layout fall back to text only, and none of the image_ leaves draws. |
chapters
| Leaf | Values | Default | What it does |
|---|---|---|---|
header | numbered, numeral, centered, minimal | numbered | A "01 / 04" line with the icon and title and a rule under; the number as a large figure beside the title; number line and title centred over a short accent rule; the title only. |
size | s, m, l | m | The size of the chapter title. |
index | sidebar, strip, hidden | sidebar | A sticky sidebar on large screens and a sticky strip below them; the strip at every width; or no index. A hidden index leaves every chapter its heading and anchor. |
tabs | underline, pills | underline | With navigation: tabs, the active tab underlined or drawn as a pill on the accent colour. |
show | number, icon, divider | all shown | Parts of a chapter header, in any header style. |
sections
| Leaf | Values | Default | What it does |
|---|---|---|---|
header | ruled, plain, bar, kicker | ruled | A rule above an open card; no rule; a short accent bar above the title; the title as a small label above the content. The title stays a heading. |
size | s, m | m | The size of the card title. |
gallery_columns | "2", "3" | "3" | The columns of a media section's gallery from the large breakpoint; below it the gallery keeps two, and one on a phone. A full-width image spans the columns in effect. A string: the number 2 is refused. |
show | description | shown | The card's description. |
fields
Field lists and the table of a children card. A Field List set to the Timeline style ignores rows and dividers.
| Leaf | Values | Default | What it does |
|---|---|---|---|
rows | split, stacked, grid | split | Label and value in two columns; the label above the value; stacked pairs in two columns from the small breakpoint and three from the large. |
dividers | lines, zebra, none | lines | A rule between rows; every other row on the surface colour; nothing. |
density | compact, comfortable, roomy | comfortable | The vertical padding of a row. |
figures
Stat grids and key figures.
| Leaf | Values | Default | What it does |
|---|---|---|---|
look | grid, tiles, ruled, plain | tiles when frames is framed, else grid | Ruled cells; each figure in a box; a rule above each figure and no box; the figures alone. |
size | s, m, l | m | The size of a figure. |
unit | after, below | after | The unit beside the number, or on its own line under it. |
corners | square, rounded | rounded when the look is tiles, else square | With the grid look rounded rounds the outer frame to the design's rounding and keeps a rule between cells; with tiles it rounds each box. ruled and plain have no box, so they ignore it, and the masthead's key figures keep their own look. |
charts
| Leaf | Values | Default | What it does |
|---|---|---|---|
colors | accent, multi, mono | accent | The brand accent; seven fixed hues kept legible against the page background; the accent followed by shades of the text colour. |
grid | lines, none | lines | Grid lines behind a chart. |
legend | side, below, hidden | side | Beside a pie from the small breakpoint; always below; visually hidden but still read by screen readers. |
bars | rounded, square | rounded | The corners of a bar. |
rhythm
| Leaf | Values | Default | What it does |
|---|---|---|---|
spacing | compact, comfortable, spacious | comfortable | The gap between chapters and between cards: three quarters of the default, the default, one and a half times it. |
width | narrow, standard, wide | standard | The maximum width of the content: 64rem, 80rem or 90rem. The masthead keeps its own frame. |
A block replaces wholesale
A block in a PATCH replaces the stored block: a leaf you leave out is unset afterwards. To change one leaf, send the leaves you want to keep with it. A block sent as null or {} clears it, and a block you leave out of the body is untouched.
curl -X PATCH https://api.synexcloud.com/v1/design \
-H "Authorization: Bearer $SYNEX_API_KEY" \
-H "Content-Type: application/json" \
-d '{"chapters": {"header": "minimal"}}'
curl -X PATCH https://api.synexcloud.com/v1/design \
-H "Authorization: Bearer $SYNEX_API_KEY" \
-H "Content-Type: application/json" \
-d '{"chapters": {"size": "l"}}'
After the second call chapters.size is l and chapters.header is null: the second body replaced the first. A show object that names one part reads back with the other parts null, and those parts show.
Retrieve the design
Reads the team's theme. Requires design:read.
The singleton always exists — a team that has never touched its branding still has a design, one whose every field is null — so this never 404s.
Every setting is present on every read. The eight part-style blocks always come back as objects with every leaf, null for those not set; colors, components and component_options come back as null until something in them is set, and then with every role, kind or option.
Request
curl https://api.synexcloud.com/v1/design \
-H "Authorization: Bearer $SYNEX_API_KEY"
Response
{
"object": "design",
"palette": [
{ "lightness": 50, "hex": "#F5F8FC" },
{ "lightness": 100, "hex": "#E7EEF8" },
{ "lightness": 200, "hex": "#C8D9F1" },
{ "lightness": 300, "hex": "#9CBCE6" },
{ "lightness": 400, "hex": "#6698DA" },
{ "lightness": 500, "hex": "#2F6FED" },
{ "lightness": 600, "hex": "#2459C4" },
{ "lightness": 700, "hex": "#1C459B" },
{ "lightness": 800, "hex": "#153472" },
{ "lightness": 900, "hex": "#0E2450" },
{ "lightness": 950, "hex": "#0A1836" }
],
"rounding": 3,
"shadow": 2,
"logo": "file_00a1b2c3d4e5f607",
"font": "Inter",
"preset": "technical",
"color_mode": "simple",
"appearance": "dark",
"colors": null,
"font_heading": "DM Sans",
"font_figures": "JetBrains Mono",
"navigation": "chapters",
"hero": "split",
"frames": "framed",
"components": {
"image": "framed", "file": "row", "figure": null, "boolean": null,
"tags": null, "long_text": null, "range": null
},
"mode": "advanced",
"labels": "caps",
"component_options": {
"image": { "aspect": "4:3", "fit": null, "corners": "rounded", "backdrop": null }
},
"top_bar": {
"layout": "solid", "logo_size": null,
"show": { "logo": null, "qr": false, "language": null, "audience": null, "identity": null }
},
"masthead": {
"title_size": null, "figures": "tiles",
"image_side": null, "image_size": null, "image_frame": "inset", "image_corners": null,
"image_fit": null, "image_focus": null, "image_backdrop": null,
"show": { "logo": null, "ids": null, "status": null, "figures": null, "image": null, "explore": null }
},
"chapters": {
"header": "numeral", "size": null, "index": null, "tabs": null,
"show": { "number": null, "icon": false, "divider": null }
},
"sections": { "header": null, "size": null, "gallery_columns": "2", "show": { "description": null } },
"fields": { "rows": null, "dividers": null, "density": null },
"figures": { "look": null, "size": null, "unit": null, "corners": null },
"charts": { "colors": null, "grid": null, "legend": null, "bars": null },
"rhythm": { "spacing": null, "width": null }
}
Unset
{
"object": "design",
"palette": [],
"rounding": null,
"shadow": null,
"logo": null,
"font": null,
"preset": null,
"color_mode": null,
"appearance": null,
"colors": null,
"font_heading": null,
"font_figures": null,
"navigation": null,
"hero": null,
"frames": null,
"components": null,
"mode": null,
"labels": null,
"component_options": null,
"top_bar": {
"layout": null, "logo_size": null,
"show": { "logo": null, "qr": null, "language": null, "audience": null, "identity": null }
},
"masthead": {
"title_size": null, "figures": null,
"image_side": null, "image_size": null, "image_frame": null, "image_corners": null,
"image_fit": null, "image_focus": null, "image_backdrop": null,
"show": { "logo": null, "ids": null, "status": null, "figures": null, "image": null, "explore": null }
},
"chapters": {
"header": null, "size": null, "index": null, "tabs": null,
"show": { "number": null, "icon": null, "divider": null }
},
"sections": { "header": null, "size": null, "gallery_columns": null, "show": { "description": null } },
"fields": { "rows": null, "dividers": null, "density": null },
"figures": { "look": null, "size": null, "unit": null, "corners": null },
"charts": { "colors": null, "grid": null, "legend": null, "bars": null },
"rhythm": { "spacing": null, "width": null }
}
Update the design
Moves only the fields present in the body. Requires design:write. Omit a field and it keeps its stored value; there is no need to read the theme, edit it, and send it back whole. The one field that moves without being sent is mode: an advanced setting sent without one stores mode: "advanced" (see The mode).
palette replaces wholesale. Merging a caller's three entries into a stored eleven would splice two scales together and produce something nobody designed — a brand colour from one palette sitting on a background from another. Send the entire scale, or leave the key out of the body entirely.
colors, components, component_options and the eight part-style blocks replace wholesale for the same reason. Each is a whole object, so a body that names two colour roles leaves the other four unset instead of keeping the stored ones. To change one role, kind, option or leaf, send all the ones you want to keep. One sent as null and one left out read back the same.
Clearing a field
Every setting but palette, rounding and shadow accepts an explicit null to unset it: logo, font, the thirteen newer settings and each part-style block.
One quirk, documented because it will otherwise cost you an afternoon:
font: "" also clears it. The API converts empty strings in request
bodies to null globally, so an empty string does not round-trip — you
send "", and the next read returns null. Send null when you mean
"unset".
The logo must exist
A logo that does not name a file in this team's Drive is rejected with 400 resource_missing and param: "logo". Nothing is written.
It is checked because the failure it prevents is silent and public. A logo pointing at nothing renders as a broken image on every public passport the team has released, and the write that caused it would have returned a 200 that looked exactly like success.
It is a 400 rather than a 404 because of what the request addressed: the /v1/design resource exists and was found; it is the logo parameter that names a missing object.
Values outside the contract
A setting outside its list is 400 parameter_invalid, and nothing is written. param names the setting, or the slot inside an object, and the message lists what is accepted. That covers a preset, mode, color_mode, appearance, labels, navigation, hero or frames that is not in its list, a font outside the catalogue, a colors role that is not one of the six or not #rrggbb (colors.text), a components kind that does not exist (components.video) or a style that is not its kind's (components.image), a component_options kind other than image (component_options.file) or an option or value outside its list (component_options.image.aspect), and a part-style leaf or show part that does not exist or holds the wrong kind of value (chapters.header, masthead.show.ids). Every enum leaf is a string: sections.gallery_columns takes "2", and the number 2 is refused.
An empty body is an error
PATCH /v1/design with {} is 400 parameter_invalid ("Send at least one design setting."), not a no-op success. A body naming no field is a caller mistake — a serializer that dropped its fields, a config object that came back empty — and a 200 would hide it behind a response that looks exactly like the change landing.
It also keeps the change feed honest: no design.updated is emitted for a request that changed nothing.
Optional attributes
- Name
palette- Type
- array
- Description
The whole colour scale. Replaces, never merges.
- Name
rounding- Type
- integer
- Description
Corner-rounding level, 1–6.
- Name
shadow- Type
- integer
- Description
Shadow-depth level, 1–6.
- Name
logo- Type
- string
- Description
A
file_id from your team's Drive, ornullto clear.
- Name
font- Type
- string
- Description
A catalogue family, matched without case, or
nullto clear.
- Name
preset- Type
- string
- Description
standard,editorial,technicalorsoft. Writes the preset alone.
- Name
mode- Type
- string
- Description
simpleoradvanced.
- Name
color_mode- Type
- string
- Description
simpleoradvanced.
- Name
appearance- Type
- string
- Description
lightordark.
- Name
colors- Type
- object
- Description
The six colour roles, each
#rrggbbor null. Replaces.
- Name
font_heading- Type
- string
- Description
A catalogue family.
- Name
font_figures- Type
- string
- Description
A catalogue family.
- Name
labels- Type
- string
- Description
muted,capsorstrong.
- Name
navigation- Type
- string
- Description
chaptersortabs.
- Name
hero- Type
- string
- Description
split,banner,compactorcentered.
- Name
frames- Type
- string
- Description
openorframed.
- Name
components- Type
- object
- Description
A style per kind. Replaces.
- Name
component_options- Type
- object
- Description
{ "image": { aspect, fit, corners, backdrop } }. Replaces.
- Name
top_bar- Type
- object
- Description
A part-style block, as are
masthead,chapters,sections,fields,figures,chartsandrhythm. Each replaces.
Request
curl -X PATCH https://api.synexcloud.com/v1/design \
-H "Authorization: Bearer $SYNEX_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"color_mode": "advanced",
"colors": {"background": "#0b1220", "text": "#e6edf7", "accent": "#4f8cff"},
"components": {"figure": "meter"},
"charts": {"bars": "square"}
}'
Response
{
"object": "design",
"palette": [
{ "lightness": 50, "hex": "#F5F8FC" },
{ "lightness": 100, "hex": "#E7EEF8" },
{ "lightness": 200, "hex": "#C8D9F1" },
{ "lightness": 300, "hex": "#9CBCE6" },
{ "lightness": 400, "hex": "#6698DA" },
{ "lightness": 500, "hex": "#2F6FED" },
{ "lightness": 600, "hex": "#2459C4" },
{ "lightness": 700, "hex": "#1C459B" },
{ "lightness": 800, "hex": "#153472" },
{ "lightness": 900, "hex": "#0E2450" },
{ "lightness": 950, "hex": "#0A1836" }
],
"rounding": 3,
"shadow": 2,
"logo": "file_00a1b2c3d4e5f607",
"font": "Inter",
"preset": "technical",
"color_mode": "advanced",
"appearance": "dark",
"colors": {
"background": "#0b1220", "text": "#e6edf7", "muted": null,
"rule": null, "surface": null, "accent": "#4f8cff"
},
"font_heading": "DM Sans",
"font_figures": "JetBrains Mono",
"navigation": "chapters",
"hero": "split",
"frames": "framed",
"components": {
"image": null, "file": null, "figure": "meter", "boolean": null,
"tags": null, "long_text": null, "range": null
},
"mode": "advanced",
"labels": "caps",
"component_options": {
"image": { "aspect": "4:3", "fit": null, "corners": "rounded", "backdrop": null }
},
"top_bar": {
"layout": "solid", "logo_size": null,
"show": { "logo": null, "qr": false, "language": null, "audience": null, "identity": null }
},
"masthead": {
"title_size": null, "figures": "tiles",
"image_side": null, "image_size": null, "image_frame": "inset", "image_corners": null,
"image_fit": null, "image_focus": null, "image_backdrop": null,
"show": { "logo": null, "ids": null, "status": null, "figures": null, "image": null, "explore": null }
},
"chapters": {
"header": "numeral", "size": null, "index": null, "tabs": null,
"show": { "number": null, "icon": false, "divider": null }
},
"sections": { "header": null, "size": null, "gallery_columns": "2", "show": { "description": null } },
"fields": { "rows": null, "dividers": null, "density": null },
"figures": { "look": null, "size": null, "unit": null, "corners": null },
"charts": { "colors": null, "grid": null, "legend": null, "bars": "square" },
"rhythm": { "spacing": null, "width": null }
}
Error
{
"error": {
"type": "invalid_request_error",
"code": "parameter_invalid",
"message": "Pick one of: plain, captioned, framed, file.",
"param": "components.image",
"errors": {
"components.image": ["Pick one of: plain, captioned, framed, file."]
}
}
}
That body named four settings, applied to the design from Retrieve the design. The other twenty-two kept whatever they held. colors lists all six roles, with muted, rule and surface null (derived when the passport is drawn). components and charts were replaced: image and file were set before and are null now, and bars is the only chart leaf left.
Events
A successful PATCH emits design.updated. Its payload is byte-identical to the response you just received, so a consumer on the change feed sees the same theme the caller does, with no follow-up read.
There is no design.created. The singleton always exists, so there is no moment of creation to report.