> ## 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.

# Platform integration

> Build a single-creator platform on the Channel API — provision a shop behind the scenes and create real products from your own UI

A **platform integration** embeds Fourthwall merch directly into a product a
creator already uses — a link-in-bio page, a creator dashboard, a community
tool. The creator never leaves your UI: they browse blank products, drop in
artwork, see a live preview, and publish, while your backend provisions a
Fourthwall shop and creates real products on their behalf.

The worked example for this guide is
[**Linkstand**](https://github.com/FourthwallHQ/fourthwall-examples/tree/main/examples/partner-platform),
a product-first "links admin" where every row on the page is a real Fourthwall
product created through one guided wizard.

<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 `channel.*` client for it;
  [reach out](mailto:support@fourthwall.com) to request access. See
  [Authentication](/channel-api/authentication).
</Note>

## The defining idea: the shop appears on publish

Browsing templates, uploading artwork, and rendering a preview are all
**shop-less** — they go straight through the Channel API on the credential
alone. Only on **publish** does a shop come into play: your backend provisions
one (once) and creates the live product against it.

> Preview is shop-less; create is shop-bound.

| Wizard step                   | What happens                                               | API call                                                                                                     | Shop?              |
| ----------------------------- | ---------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------ | ------------------ |
| 1 · Pick a product            | Browse blank product templates                             | `GET /open-api/v1.0/product-templates/page/{page}`                                                           | No                 |
| 2 · Add artwork               | Presigned upload, then register the image                  | `POST /channel-api/v1.0/upload-url` → `POST /channel-api/v1.0/media/images`                                  | No                 |
| 3 · Preview                   | Render preview images synchronously                        | `POST /channel-api/v1.0/previews`                                                                            | No                 |
| 4 · Details & price → Publish | Provision a shop on first publish, then create the product | `GET /channel-api/v1.0/shops` → (first time) `POST /channel-api/v1.0/shops` → `POST /open-api/v1.0/products` | Creates / needs it |

## Two credential faces, one channel

The same channel credential authenticates two API surfaces:

* **channel-api** (`/channel-api/v1.0/…`) — authorized by the channel bearer
  **alone**. Everything shop-less: template browsing, upload, preview, shop
  lookup, and shop provisioning.
* **open-api** (`/open-api/v1.0/…`) — the product-templates list is shop-less,
  but product **create / list / state / delete** are shop-bound: the same
  bearer plus an `X-ShopId` header selects the shop you provisioned.

## Architecture: a thin backend-for-frontend

Keep the credential on your server. Each `/api/*` route is a small proxy that
attaches the channel credential and forwards to one (or, for publish, a few)
Fourthwall endpoints. The browser only ever talks to your own routes.

```
Creator → your app (browser) → /api/* (server, holds credential) → Fourthwall
```

Linkstand maps its routes one-to-one onto Fourthwall:

| Your route                        | Forwards to                                                        | Shop?         |
| --------------------------------- | ------------------------------------------------------------------ | ------------- |
| `GET  /api/templates`             | `GET /open-api/v1.0/product-templates/page/1`                      | No            |
| `GET  /api/channel`               | `GET /channel-api/v1.0/channel/current`                            | No            |
| `POST /api/upload-url`            | `POST /channel-api/v1.0/upload-url`                                | No            |
| `POST /api/media`                 | `POST /channel-api/v1.0/media/images`                              | No            |
| `POST /api/previews`              | `POST /channel-api/v1.0/previews`                                  | No            |
| `POST /api/publish`               | shop lookup → create (first only) → `POST /open-api/v1.0/products` | **Needs one** |
| `GET  /api/links`                 | `GET /channel-api/v1.0/shops` → `GET /open-api/v1.0/products`      | Needs one     |
| `PUT  /api/links/{id}/visibility` | `PUT /open-api/v1.0/products/{id}/state`                           | Needs one     |
| `DELETE /api/links/{id}`          | `DELETE /open-api/v1.0/products/{id}`                              | Needs one     |

## The flow, step by step

<Steps>
  <Step title="Connect the channel">
    Install your channel app and exchange the OAuth code for the channel
    credential, holding it server-side. Confirm the connection by reading the
    current channel:

    ```bash cURL theme={null}
    curl "https://api.fourthwall.com/channel-api/v1.0/channel/current" \
      -H "Authorization: Bearer $ACCESS_TOKEN"
    ```
  </Step>

  <Step title="Pick a blank product">
    List blank product templates and let the creator choose one. This is
    shop-less:

    ```bash cURL theme={null}
    curl "https://api.fourthwall.com/open-api/v1.0/product-templates/page/1" \
      -H "Authorization: Bearer $ACCESS_TOKEN"
    ```
  </Step>

  <Step title="Upload and register the artwork">
    Request a presigned upload URL, `PUT` the bytes, then register the image to
    get an `imageId`. Both calls are on the channel face — no shop yet:

    ```bash cURL theme={null}
    # 1. presigned URL
    curl -X POST "https://api.fourthwall.com/channel-api/v1.0/upload-url" \
      -H "Authorization: Bearer $ACCESS_TOKEN" \
      -H "Content-Type: application/json" \
      -d '{ "fileName": "art.png", "contentType": "image/png" }'

    # 2. PUT the bytes to the returned uploadUrl (echo the signed headers)

    # 3. register → imageId
    curl -X POST "https://api.fourthwall.com/channel-api/v1.0/media/images" \
      -H "Authorization: Bearer $ACCESS_TOKEN" \
      -H "Content-Type: application/json" \
      -d '{ "fileUrl": "<fileUrl from step 1>" }'
    ```
  </Step>

  <Step title="Render a live preview">
    Render preview mockups synchronously from the chosen template + `imageId`.
    Still shop-less:

    ```bash cURL theme={null}
    curl -X POST "https://api.fourthwall.com/channel-api/v1.0/previews" \
      -H "Authorization: Bearer $ACCESS_TOKEN" \
      -H "Content-Type: application/json" \
      -d '{ "productId": "<templateId>", "imageId": "<imageId>" }'
    ```
  </Step>

  <Step title="Publish — the shop boundary">
    Publish is the only step that needs a shop, and it owns the whole shop
    lifecycle so your client makes a single call:

    1. `GET /channel-api/v1.0/shops` — does the channel have a shop yet?
    2. `POST /channel-api/v1.0/shops` — **only on first publish** (idempotent:
       a second publish reuses the same shop, never a duplicate).
    3. `POST /open-api/v1.0/products` with the `X-ShopId` header — create the
       live product from the **same** `productId` + `imageId` the preview was
       rendered from.

    ```bash cURL theme={null}
    curl -X POST "https://api.fourthwall.com/open-api/v1.0/products" \
      -H "Authorization: Bearer $ACCESS_TOKEN" \
      -H "X-ShopId: $SHOP_ID" \
      -H "Content-Type: application/json" \
      -d '{ "type": "design", "name": "My link", "...": "..." }'
    ```
  </Step>
</Steps>

<Tip>
  Thread the previewed `productId` + `imageId` through your wizard state and reuse
  them at publish — don't re-derive the design. The previewed design and the
  published product are then guaranteed to be the same.
</Tip>

## Managing published products

Once a shop exists, manage the catalog on the open-api face with `X-ShopId`:

* **Show / hide** → `PUT /open-api/v1.0/products/{id}/state` (`PUBLIC` / `HIDDEN`)
* **Archive** → `DELETE /open-api/v1.0/products/{id}`
* **List** → `GET /open-api/v1.0/products`

## Full example

Read [**Linkstand**](https://github.com/FourthwallHQ/fourthwall-examples/tree/main/examples/partner-platform)
end to end — a Next.js app-router project where the BFF in `lib/fourthwall.ts`
is the only module that holds the credential and talks to Fourthwall.
