> ## Documentation Index
> Fetch the complete documentation index at: https://www.courier.com/docs/llms.txt
> Use this file to discover all available pages before exploring further.

> ## Agent Instructions
> Install the Courier skill before writing code: `npx skills add trycourier/courier-skills`. It carries the verified SDK shapes and the rules you cannot get wrong.
> Authenticate every request with `Authorization: Bearer <API_KEY>`. A workspace has several environments and each has its own keys, which are plain `pk_` strings with no environment prefix. Start with Test.
> Send with `client.send.message`, the default import of the v7 Node SDK. Reference a template by its `nt_` id or its alias.
> A send accepts a bare Elemental element list, but storing content on a template requires the top-level elements wrapped in a channel element.
> Templates and journeys can be built in the Courier app or created through the API. Either way they live in the workspace and are referenced by ID when you send.
> The hosted MCP server is https://mcp.courier.com. For a briefing on what Courier is and when to use it, read https://www.courier.com/llms.txt.
> Prefer the Guides tab for how-do-I questions and the Docs tab for how-does-it-behave questions. The API reference lives under /api-reference.

# Courier Create brand editor

> Let a tenant edit its logo, colors, and email footer with BrandEditor and BrandProvider.

export const AppLink = ({href, children, name, bare}) => {
  const label = children || name || "Open in Courier";
  if (bare) {
    return <a href={href} target="_blank" rel="noreferrer">{label}</a>;
  }
  return <a className="cx-endpoint" data-kind="app" href={href} target="_blank" rel="noreferrer">
      <span className="cx-endpoint-label">{label}</span>
      <span className="cx-endpoint-method" aria-hidden="true">↗</span>
    </a>;
};

export const Doc = ({href, children, name, bare}) => {
  const label = children || name || href;
  if (bare) {
    return <a href={href}>{label}</a>;
  }
  return <a className="cx-endpoint" data-kind="doc" href={href}>
      <span className="cx-endpoint-label">{label}</span>
      <span className="cx-endpoint-method">DOC</span>
    </a>;
};

`BrandEditor` edits one Tenant's logo, colors, and email footer. `BrandProvider` carries its auth.

```jsx theme={null}
import "@trycourier/react-designer/styles.css";
import { BrandProvider, BrandEditor } from "@trycourier/react-designer";

function Branding({ token }) {
  return (
    <BrandProvider tenantId="acme-corp" token={token}>
      <BrandEditor />
    </BrandProvider>
  );
}
```

`BrandProvider` takes no `templateId`. A Brand belongs to the Tenant rather than to one
Template, so every Template that Tenant sends picks the same Brand up.

## Scope the token for a Brand

The Brand scopes are separate from the Template scopes, and a token carrying only Template
scopes cannot write a Brand.

| Scope                           | Grants                     |
| ------------------------------- | -------------------------- |
| `tenant:<id>:brand:read`        | Read one Tenant's Brand    |
| `tenant:<id>:brand:write`       | Write it                   |
| `tenants:brand:read` / `:write` | Brands across every Tenant |

Include both `read` and `write` for an editor a customer can save from. See
<Doc href="/docs/design/embedded-designer/overview#scopes">Overview</Doc> for the full list and
the mint flow.

**A missing Brand scope fails at save, not at mount.** The editor renders, the customer
edits, and the write is rejected. Scope the token before you ship rather than after a
customer reports it.

## Run both editors together

Two arrangements do this, and they differ in how much control you get.

`brandEditor` on `TemplateEditor` shows the Brand editor beside the content editor, inside
the Template provider. `brandProps` passes options through to it.

```jsx theme={null}
<TemplateProvider templateId={templateId} tenantId="acme-corp" token={token}>
  <TemplateEditor brandEditor brandProps={{ hidePublish: true }} />
</TemplateProvider>
```

Mounting `BrandProvider` separately gives the Brand its own page or dialog. Each provider
creates its own store, so a Brand editor mounted this way holds state independent of the
Template editor.

Either way the token needs the Brand scopes. Adding `brandEditor` to a Template editor does
not widen the token.

## Publish from your own button

`hidePublish` removes the built-in control, and `useBrandActions` exposes the actions
behind it.

```jsx theme={null}
import { useBrandActions } from "@trycourier/react-designer";

function PublishBrandButton() {
  const { publishBrand } = useBrandActions();
  return <button onClick={() => publishBrand()}>Publish brand</button>;
}
```

The hook also returns `saveBrand` for an explicit save, and `getTemplate`. Call it inside
`BrandProvider`, or inside the `TemplateProvider` when the Brand editor is mounted through
`brandEditor`.

Publishing a Brand is its own call. Publishing a Template does not publish the Brand beside
it, so a customer who changes both has two things to publish.

## Theme the editor

`theme` takes a theme object or a class name, and `colorScheme` selects `light` or `dark`.

```jsx theme={null}
<BrandEditor theme={{ background: "#ffffff", primary: "#0f62fe" }} colorScheme="light" />
```

Import `@trycourier/react-designer/styles.css` once in your app. Without it the editor
renders unstyled.

## Provider props

`BrandProvider` takes auth and identity only.

| Property   | Type   | Required | Description                                          |
| ---------- | ------ | -------- | ---------------------------------------------------- |
| `tenantId` | string | Yes      | The Tenant whose Brand is edited.                    |
| `token`    | string | Yes      | JWT carrying the Brand scopes. Issue it server-side. |
| `apiUrl`   | string | No       | Override the API base URL.                           |

## Editor props

| Property                       | Type                       | Default | Description                                                                    |
| ------------------------------ | -------------------------- | ------- | ------------------------------------------------------------------------------ |
| `autoSave`                     | boolean                    | `true`  | Save Brand changes automatically.                                              |
| `autoSaveDebounce`             | number                     | `500`   | Milliseconds to wait before an auto-save.                                      |
| `colorScheme`                  | `"light" \| "dark"`        | None    | Force a color scheme.                                                          |
| `disableVariablesAutocomplete` | boolean                    | `false` | Let customers type any variable name.                                          |
| `hidePublish`                  | boolean                    | `false` | Hide the built-in Publish button.                                              |
| `templateEditor`               | boolean                    | None    | Show the template editor beside the Brand editor. The mirror of `brandEditor`. |
| `theme`                        | `Theme` or class name      | None    | Style the editor.                                                              |
| `value`                        | `BrandEditorFormValues`    | None    | Initial Brand values.                                                          |
| `variables`                    | `Record<string, unknown>`  | None    | Variables offered to autocomplete in footer content.                           |
| `variableValidation`           | `VariableValidationConfig` | None    | Restrict allowed variable names.                                               |

## Verify

Render the page, change the logo or a color, and wait for the save message. Publish, then
open <AppLink href="https://app.courier.com/designer/brands">Brands</AppLink> and confirm
the Tenant's Brand carries the change. Send that Tenant a Template and confirm the email
picks the Brand up.

## Troubleshooting

| Symptom                                        | Cause                                                                            |
| ---------------------------------------------- | -------------------------------------------------------------------------------- |
| The editor renders, saves are rejected         | The token has Template scopes but no `brand` scopes.                             |
| The editor renders unstyled                    | `@trycourier/react-designer/styles.css` was never imported.                      |
| A published Template still shows the old Brand | The Brand was never published. Publishing a Template does not publish the Brand. |
| `useBrandActions` does nothing                 | The component calling it sits outside both providers.                            |

## Limits & behavior

* **Brand scopes are separate from Template scopes.** One token needs both to edit both.
* **A Brand is Tenant-wide.** Every Template that Tenant sends resolves the same Brand.
* **Publishing is per resource.** A Template publish leaves an edited Brand unpublished.
* **Each provider owns its store.** A separately mounted Brand editor shares no state with the Template editor.

## FAQ

<AccordionGroup>
  <Accordion title="Can a customer edit only their Brand?">
    Mount `BrandProvider` with `BrandEditor` and issue a token carrying just the Brand
    scopes. No Template scopes are needed.
  </Accordion>

  <Accordion title="Why is the Brand editor rejecting saves?">
    The token is missing `tenant:<id>:brand:write`. A missing Brand scope fails at save
    rather than at mount, so the editor renders before anything reports it.
  </Accordion>

  <Accordion title="Does publishing a Template publish the Brand too?">
    Publishing is per resource. A customer who edits both has two publishes to make, and
    `publishBrand` from `useBrandActions` is the Brand one.
  </Accordion>

  <Accordion title="Does BrandProvider need a templateId?">
    A Brand belongs to the Tenant rather than to a Template, so `BrandProvider` takes
    `tenantId` and `token` only.
  </Accordion>
</AccordionGroup>
