---
title: "Change the saved brand with ops applied in order, or import it from a public website or a Figma file with url. Every op is validated before anything is written: an invalid op returns status invalid with the issues and the op's expected shape, and nothing changes. An import returns at once with status running and an importId; GET /v1/brand reports it, under extraction for a website or figmaDesigns for a Figma file, until it is completed or failed. A Figma file needs Figma connected in Replo: without it the call returns action_required with a connectUrl for the user to connect it. Calls with url have a daily cap per project, whether or not they start an import; past it the call returns 429 until UTC midnight, and ops still work."
method: PATCH
path: "/brand"
tags: ["Brand"]
---

# Change the saved brand with ops applied in order, or import it from a public website or a Figma file with url. Every op is validated before anything is written: an invalid op returns status invalid with the issues and the op's expected shape, and nothing changes. An import returns at once with status running and an importId; GET /v1/brand reports it, under extraction for a website or figmaDesigns for a Figma file, until it is completed or failed. A Figma file needs Figma connected in Replo: without it the call returns action_required with a connectUrl for the user to connect it. Calls with url have a daily cap per project, whether or not they start an import; past it the call returns 429 until UTC midnight, and ops still work.

`PATCH /brand`

## Request

- Base URL: `https://api.replo.app/v1`
- URL: `https://api.replo.app/v1/brand`
- Auth: HTTP bearer

**Beta** — this endpoint may change before general availability.

## Headers

- `Replo-Api-Version` '2026-10-29', required

## Request body

- object
  - `ops` object[]: Ops in order; fill only the fields your op uses. setColor value: { key, name, hex: "#rrggbb", category: "brand" | "neutral", roles: string[], usage: string | null, position: integer }. setFont value: { key, family, roles: string[], delivery: "google-fonts" | "cdn" | "uploaded" | "system" | "unknown", license: string | null, usage: string | null, position: integer, faces: [{ weight: 1-1000, style: "normal" | "italic", assetId: uuid | null, sourceUrl: string | null }] }. setBusiness patch: any of name, about, audience, pricePoint, businessModel, foundedYear, location, voice, sourceUrl. setOverview: kind "business" | "design" and markdown. setMarks: logoAssetId, faviconAssetId or logomarkAssetId; upload the image with upload_asset first and pass the asset id it returns. setPersona value: copy a persona from get_branding and change what you need; a persona nickname is a segment label, never a person's name. A refused op comes back with `expected`, the exact shape to resend. Pass ops or url, not both.
    - `op` 'setBusiness' | 'setOverview' | 'setColor' | 'setFont' | 'setPersona' | 'setMarks' | 'tagFile' | 'untagFile', required: BrandingOpName
    - `patch` object: setBusiness: the business fields to change.
    - `kind` 'business' | 'design': setOverview: which overview.
    - `markdown` string, nullable: setOverview: the whole overview as markdown; null clears it.
    - `key` string: setColor, setFont, setPersona: the item's key.
    - `value` object, nullable: setColor, setFont, setPersona: the whole item; null deletes it.
    - `logoAssetId` string: setMarks: asset id to use.
    - `faviconAssetId` string: setMarks: asset id to use.
    - `logomarkAssetId` string: setMarks: asset id to use.
    - `clearMarks` string[]: setMarks: marks to remove.
    - `path` string: tagFile, untagFile: the file's path.
    - `tag` 'business' | 'design' | 'context': tagFile, untagFile: context (the business and design overviews are setOverview, not files).
    - `fileId` string, uuid: tagFile, untagFile: the file's id, from the brand's contextFiles or the file tools; or pass path as get_branding reports it. Tagging a file in someone's private home moves it to the team folder.
  - `url` string, uri: Import the brand from this public site instead of ops (add https:// if missing). Replo reads it on its servers into facts, palette, fonts, marks, brief, design notes and screenshots, and writes them by `mode`. Never restyles a site. A figma.com file link instead adds that file to the brand's designs (figmaDesigns: its frames, styles and photos); it takes no mode or parts, and needs Figma connected in Replo.
  - `mode` 'replace' | 'swap' | 'fill': With url: how the site lands on the brand. replace (default): the whole brand becomes the site's ("make my brand from this site"). swap: each part in `parts` becomes the site's in full, nothing else changes, the brand keeps its name ("use their fonts", "take their logo and colors"). fill: everything already set stays and the site fills only the gaps, blending field by field ("fill in the rest from this site"). Pick swap for another company's site, so its story never mixes into this brand.
  - `parts` string[]: With url: the parts swap and fill take from the site, each written as the ops of its kind: facts → setBusiness and setOverview, colors → setColor, fonts → setFont, logo → setMarks; images adds the site's photos to brand imagery. Required for swap; on fill, omitted means all of them; refused with replace.
  - `projectId` string, uuid, required: The project whose saved brand to change.

## Response `200`

Change the saved brand with ops applied in order, or import it from a public website or a Figma file with url. Every op is validated before anything is written: an invalid op returns status invalid with the issues and the op's expected shape, and nothing changes. An import returns at once with status running and an importId; GET /v1/brand reports it, under extraction for a website or figmaDesigns for a Figma file, until it is completed or failed. A Figma file needs Figma connected in Replo: without it the call returns action_required with a connectUrl for the user to connect it. Calls with url have a daily cap per project, whether or not they start an import; past it the call returns 429 until UTC midnight, and ops still work.

- object
  - `status` 'applied' | 'partial' | 'invalid' | 'running' | 'failed' | 'action_required', required: applied: every op was written. partial: the brand ops are live but some file ops were refused (see `message` and `files`). invalid: nothing was written (see `issues`). The rest answer a url import, which returns at once. running: the import started, one was already running, or a Figma file hasn't changed (`message` says there's nothing to wait for), and keeps going on Replo's servers; follow it with get_branding until `importId` is completed or failed (a website in extraction, a Figma file in figmaDesigns), instead of calling again. failed: the import could not start; `message` says why. action_required: the person has to connect Figma outside the chat first; `connectUrl` and `message` say what, and nothing started.
  - `version` integer, nullable, required: The brand's version after the write; it is live. Null when nothing on the brand was written: only file ops, or an import that did not finish.
  - `files` union[], required: One result per tagFile/untagFile op, in order.
    - union: BrandingFileResult
      - object: BrandingFileMarked
        - `status` 'marked', required
        - `file` object, required: The entry as get_branding now lists it under contextFiles.
          - `fileId` string, uuid, required
          - `path` string, nullable, required: Absolute sandbox path, e.g. /workspace/project/BUSINESS.md; null when missing or outside the caller's mounts
          - `kind` 'file' | 'folder' | 'null', nullable, required
          - `tags` string[], required: Brand tags the file carries, in vocabulary order
          - `status` 'ok' | 'missing' | 'no-access', required: ProjectContextFileStatus
        - `message` string, required: One line for the user.
      - object: BrandingFileUnmarked
        - `status` 'unmarked', required
        - `fileId` string, uuid, required
        - `tag` 'business' | 'design' | 'context', required: ProjectContextTag
        - `removed` boolean, required: False when the file did not carry that tag; nothing changed.
        - `message` string, required: One line for the user.
      - object: BrandingFileNotFound
        - `status` 'not-found', required
        - `message` string, required: What to do next.
      - object: BrandingFileInvalid
        - `status` 'invalid', required
        - `issues` object[], required: What the server refused; nothing was written.
          - `path` string, required: Which value, e.g. colors.action.hex
          - `message` string, required
        - `message` string, required: What to do next.
      - object: BrandingFileNoContext
        - `status` 'no-context', required
        - `message` string, required: The project has no context rows yet; how to create them.
  - `message` string, nullable, required: On partial: which file ops were refused and what to do next. On an import: the one line for the user. On a write while a website import runs: that a replace will overwrite it. Null otherwise.
  - `issues` object[], required: On invalid: the values that were refused. Empty otherwise.
    - `path` string, required: Which value, e.g. colors.action.hex
    - `message` string, required
  - `expected` string, nullable, required: On invalid: the expected shape of the first refused op.
  - `importId` string, nullable, required: On a running import: its id. A website's is the one get_branding's extraction reports; a Figma file's is its file key, the importId of its figmaDesigns entry. Null otherwise.
  - `dashboardUrl` string, nullable, required: On a running import: Branding, where the person can watch it. Null otherwise.
  - `connectUrl` string, nullable, required: On action_required: the link where the person connects Figma. Show it to them, and call update_brand again once they say they finished. Null otherwise.
  - `object` 'brand_update', required

## Other responses

- `400`: Invalid request.
- `401`: Missing or invalid Replo Public API token. Send an active key as `Authorization: Bearer replo_sk_...`; if it was revoked, create a new key in Replo Settings.
- `403`: You are not authorized to access this resource. Ask a workspace owner to perform this action or to grant the key's creator access.
- `404`: Not Found. The resource may not exist, this key may lack the required scope, or the key's creator may have lost access. Check the key's scopes in Replo Settings and the creator's project or workspace access; a workspace owner can grant access.
- `409`: The resource is in a conflicting state.
- `413`: File content exceeds the 10 MiB limit.
- `429`: Too many requests. Please retry after the time indicated by the Retry-After header.
- `500`: An internal error occurred.
- `503`: Service temporarily unavailable.

## Changes

- **2026-11-04** `53d194ba2067`: 9 info
  - added the optional property `error/issues` to the response with the `400` status
  - added the optional property `error/issues` to the response with the `401` status
  - added the optional property `error/issues` to the response with the `403` status
  - added the optional property `error/issues` to the response with the `404` status
  - …5 more
- **2026-10-31** `608c19382134`: 1 info
  - endpoint added

[Operation history](https://skmtc.dev/replo/apis/replo-public-api/changes/brand/patch.md)

---

[API](https://skmtc.dev/replo/apis/replo-public-api.md) - [All operations](https://skmtc.dev/replo/apis/replo-public-api/llms.txt) - [OpenAPI document](https://skmtc.dev/replo/apis/replo-public-api/revisions/53d194ba2067?raw)
