/api/v1/brands/{slug}, with
Authorization: Bearer ab_..., and answer { "data": ... }.
Writing takes
write on the workspace. An import that publishes also takes
sharing, which a grant can switch off. The full schemas are in the
API reference.
The model
For each brand kept in a repository, Artbucket stores its source: the repository, branch and folder, the commit both sides last agreed on, and the brand as of that commit (the base). Every import merges three states: the base, the brand in the app now, and the files.- Import at a commit (the repository changed): the base becomes the files at that commit.
- Import without a commit (files from anywhere else, like the CLI): the base stays, so the next import still merges from what the repository last said.
PUT .../sourcewithsynced(the integration just committed what Artbucket exported): the base becomes those files, so nothing is merged or exported again.- Moving the source to another repository, branch or folder forgets the base: the next import takes the files whole.
The source
GET /api/v1/brands/acme/source
source is null for a brand that lives in Artbucket alone.
Connect a brand
A repository that already holds the brand
POST /api/v1/brands with name and slug) when Artbucket doesn’t
have it: the import names it from brand.yaml.
An empty repository, from a brand Artbucket has
- Export with every asset given a path:
POST .../files/exportwith{ "assets": "files" }. - Commit
files, and each asset inassets, fetched from itsurlwith the key, at its path. - Record it:
PUT .../sourcewith the repository andsynced, the commit and what you committed.
PUT /api/v1/brands/acme/source
synced is checked like an import: files that don’t check are refused with
their problems.
The repository changed
On a push to the source’s branch that touches its folder, send the folder at that commit:POST /api/v1/brands/acme/files/import
200
color.primary was changed on both sides since the base: the files’
value won, and the app’s is in the version before.
A conflict’s
what names its piece: name, theme.radius,
rule color.primary, rule color.primary (dark-background), page logo.
When two sound sides make an unsound whole (a page in the app binding a rule
the files removed), the import takes the files whole and says so as a
conflict on brand.
The brand changed in the app
Whensource.pending is true, export, giving the repository’s files at its
head:
POST /api/v1/brands/acme/files/export
200
files is the whole brand. A file that says the same as its previous comes
back exactly as it was, comments and all, so:
- to write: each file whose text differs from
previous, or thatpreviouslacks; - to delete: each file of
previousunderrules/orpages/thatfilesno longer has; - assets: each path in
assetswhosesha256the repository’s file doesn’t match, fetched fromurlwith the key.assets: "files"in the request gives every asset the brand points at a path, including ones the repository never held.
PUT .../source with synced: { commit, files, assets }, the folder as
committed. Until then the source stays pending.
If the commit goes through a pull request that people change before merging,
skip synced: the merge comes back as an import, and the base moves then.
Pull request previews
POST /api/v1/brands/acme/previews
200
- The files are checked as an import is, and refused the same way.
- One preview per
ref: saving it again (a new push to the pull request) keeps its link and replaces what it shows. diffis what merging would change of the brand as it stands, for the pull request’s comment.- The link needs no account, like a share link, and shows every page that isn’t hidden, members’ pages too, with files signed for whoever opens it. Treat it as you would the pull request.
- It closes 30 days after its last update. Close it sooner when the pull
request is merged or closed:
DELETE .../previews?ref=pull/12.
GET /api/v1/previews/{token}?page=color answers a page of the preview as
data, for an integration that draws its own.
Assets
A path underassets/ is matched to the library by the SHA-256 of its bytes
(or an asset id given in its place). When a file the folder points at isn’t
in the library, the request is refused with missing:
422
assets/ no file points at can be sent or left
out.
Problems
Files that don’t check are refused with422, every problem at its file and
line, and the files to upload:
422
Connecting from the app
SetGIT_CONNECT_URL on the server to where your integration starts, with
{brand} for the brand’s slug: https://git.example.com/connect?brand={brand}.
New brand, a new brand’s setup and the builder link there for workspace
admins, with {brand} empty when the brand is to come from a repository.
When it is done, send the person back to the brand’s Overview,
{APP_URL}/brands/{slug}?git=connected, which says when the first sync has
landed.
A minimal integration
Import on every push to the main branch, from a CI job or a webhook handler. It assumes the brand’s source is set (PUT .../source, once).
sync.mjs
pending, commit, PUT .../source with synced) and previews on
pull requests, each one call as above.