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

# Agency integration

> Operate a fleet of subaccount shops with one agency Channel API credential — one secret that authenticates three different ways

An **agency integration** lets one operator run merch for many creators from a
single console. One confidential `channel.*` credential operates a whole fleet
of subaccount shops: you onboard creators, browse and publish to each shop, and
surface live storefronts — all from one secret.

The worked example for this guide is
[**Greenroom**](https://github.com/FourthwallHQ/fourthwall-examples/tree/main/examples/agency),
an agency console you can read end to end.

<Note>
  The Channel API requires **special access** — it is **invite only** and
  currently in **beta**. Your channel must be granted access by Fourthwall, which
  provisions a dedicated agency-bound `channel.*` client for it;
  [reach out](mailto:support@fourthwall.com) to request access. See
  [Authentication](/channel-api/authentication).
</Note>

## One secret, three faces

The single agency secret authenticates **three different ways** depending on the
call. The secret **never reaches the browser** — every `channel-api` and
`open-api` call goes through a server route that mints the token and attaches it.

| Face            | Authenticates with                   | Operates on                         |
| --------------- | ------------------------------------ | ----------------------------------- |
| **channel-api** | bearer token alone                   | the agency channel + its bound shop |
| **open-api**    | bearer token **+ `X-ShopId`** header | one ownership-checked subaccount    |
| **publicToken** | a browser-safe storefront token      | the live shop, read-only            |

`X-ShopId` is ownership-checked upstream: a missing or unowned shop id is
rejected, so you can only ever act on a shop your agency manages.

## The token mint — HTTP Basic, not body credentials

The agency uses the **client credentials** grant, sending the id/secret as
`client_secret_basic` (HTTP Basic). Posting the secret in the request body
returns `401 invalid_client` — the single most common first mistake:

```ts theme={null}
const basic = Buffer.from(`${CLIENT_ID}:${CLIENT_SECRET}`).toString("base64");
await fetch(TOKEN_URL, {
  method: "POST",
  headers: {
    Authorization: `Basic ${basic}`,
    "Content-Type": "application/x-www-form-urlencoded",
  },
  body: new URLSearchParams({ grant_type: "client_credentials" }),
});
```

Cache the token until shortly before it expires, then mint a fresh one.

## Two clients over one token

* **`channelApi`** — attaches `Authorization: Bearer <token>` only.
* **`openApi`** — attaches the bearer **and** the `X-ShopId` header for the
  target subaccount.

## Five features, mapped to the three faces

<Steps>
  <Step title="F1 — Fleet home (channel-api)">
    Identify the connected agency and list its fleet of shops.

    `GET /channel-api/v1.0/channel/current` + `GET /channel-api/v1.0/shops`
  </Step>

  <Step title="F2 — Onboard a creator (channel-api)">
    Create a subaccount shop, invite the owner, and optionally start payout
    onboarding. The creation response carries the new shop's `publicToken`.

    `POST /channel-api/v1.0/shops`
  </Step>

  <Step title="F3 — Subaccount catalog (open-api)">
    Browse one shop's products, scoped by `X-ShopId`.

    `GET /open-api/v1.0/products` with `X-ShopId: <subaccount>`
  </Step>

  <Step title="F4 — Design & publish (two faces)">
    The load-bearing flow — it touches **both** faces. See the next section.
  </Step>

  <Step title="F5 — Creator storefront (publicToken)">
    Resolve the shop's browser-safe `publicToken` server-side, then read the
    live shop directly from the browser against the Storefront API — no agency
    secret in play.

    `PUT /open-api/v1.0/public-token` (server) → Storefront API (browser)
  </Step>
</Steps>

## F4 — why artwork is registered twice

F4 renders an instant preview on the channel's bound shop, then publishes on the
subaccount. The artwork is registered **twice, across two faces**:

```mermaid theme={null}
flowchart TD
    subgraph Preview [Preview — channel-api, bound shop]
      A[upload-url] --> B[PUT bytes] --> C[media/images] --> D[previews]
    end
    subgraph Publish [Publish — open-api + X-ShopId, subaccount]
      E[media/upload-url] --> F[PUT bytes] --> G[media/images again] --> H[products type=design] --> I[products/id/state PUBLIC]
    end
    D --> E
```

The bound-shop `imageId` from the preview is **not** valid for the design-create
on the subaccount — that resolves the `imageId` against the `X-ShopId` shop, so
the publish path must register the media again on the subaccount.

<Warning>
  Gotchas the example pins down:

  * **The `region` is template-specific.** The renderer rejects an invalid region
    and returns the valid ones — `front` for DTG, `front_dtf` for DTFX, etc.
    Derive the region from the template's `productionMethod`.
  * **`colors` / `sizes` are case-sensitive** against the product's variant
    labels, or the render comes back empty. Leave them empty to use all available.
  * **`publishOnCreate`** controls whether the product is created `PUBLIC` or
    `HIDDEN`. When off, transition it to `PUBLIC` via `PUT /products/{id}/state`
    after creation.
  * **The presigned upload `PUT` must echo two signed headers** — `Content-Type`
    and `x-goog-content-length-range: 0,<size>` (the byte length declared in the
    `upload-url` request) — or GCS returns `403 SignatureDoesNotMatch`.
</Warning>

## F5 — the publicToken face

The `publicToken` is **browser-safe**. Resolve it server-side
(`GET /api/shops/{shopId}/public-token` → `PUT /open-api/v1.0/public-token`) and
then read the live shop directly from the browser against the
[Storefront API](/storefront/overview) — no agency secret in play. The token
comes from F2's creation response (for shops created in-session) or the
public-token endpoint (for any managed shop).

<Note>
  Onboarding creates **real shops and real payouts**. Target **staging** by
  default while building, and treat a payout account as *pending* after the
  handoff — returning from the `returnUrl` means the creator left the hosted flow,
  not that payouts are verified.
</Note>

## Full example

Read [**Greenroom**](https://github.com/FourthwallHQ/fourthwall-examples/tree/main/examples/agency)
end to end — a Next.js console where `lib/fourthwall.ts` holds the credential
core (token mint + the two clients) and every Fourthwall call runs through a
server route.
