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

# Composer API: run a brand's plays from an agent

> The Percify Composer API deals a brand's daily rounds, writes takes, renders plays, drives the autopilot and takes posts back from your own publisher. Bearer pk_live key.

The Composer API is Composer for agents and scripts. It runs the same loop the app does: find the brand, see the plays and the words written for it, make a file, set the autopilot going, read what it has learned and how the posts did, and, when you publish yourself, take the ready plays and report back.

Base URL `https://api.percify.io/v3/composer/api`. Authenticate with `Authorization: Bearer pk_live_…`, the same key as the rest of the [Percify API](/api-reference/introduction), which means the same plan gate: **Scale and above**.

Every route runs through the owner-scoped methods the app calls, so **a key can never do what the app cannot**. Round actions are logged with `source: "api"`, so an agent's picks never read as a person's.

## Find the brand

```bash cURL theme={"system"}
curl -s https://api.percify.io/v3/composer/api/brands \
  -H "Authorization: Bearer $PERCIFY_API_TOKEN"
```

| Route                                | What it does                                                     |
| ------------------------------------ | ---------------------------------------------------------------- |
| `GET /brands`                        | The key owner's brands, and the ids every other route takes      |
| `GET /plays`                         | The play library and how each one has done                       |
| `GET /brands/{profileId}/takes?ids=` | This brand's words for the plays named in `ids`, comma separated |

## Today's rounds

The same day the app's Plays tab shows, and the same verbs. `at` and `slot` count from 0.

| Route                                           | Body                     | What it does                                                             |
| ----------------------------------------------- | ------------------------ | ------------------------------------------------------------------------ |
| `GET /brands/{profileId}/round/today`           | `?tz=`                   | Today's rounds. `tz` is used only while the brand has no time zone saved |
| `POST /brands/{profileId}/round/reveal`         | `{ at }`                 | Turn a round over                                                        |
| `POST /brands/{profileId}/round/pass`           | `{ at, slot, playId? }`  | Pass on a card; another takes its slot, returned as `incoming`           |
| `POST /brands/{profileId}/round/shuffle`        | `{ at, slots? }`         | Reroll the round's open cards. **Approved cards never move**             |
| `PATCH /brands/{profileId}/plays/{playId}/vote` | `{ vote: "up" \| null }` | Approve a play, or take the approval back                                |

A pass is the round's own route rather than a vote, because passing swaps in a replacement.

## Write and render

| Route                                                      | Body         | What it does                                                         |
| ---------------------------------------------------------- | ------------ | -------------------------------------------------------------------- |
| `POST /brands/{profileId}/plays/{playId}/takes`            | `{ angle? }` | A new take on one play. Omit `angle` for the brand's next unworn one |
| `POST /brands/{profileId}/plays/{playId}/export`           |              | Render this brand's version to a file. Answers at once               |
| `GET /brands/{profileId}/plays/{playId}/export/{exportId}` |              | Poll that render                                                     |

<Warning>
  A render is the heaviest call here and is limited **per hour**, not per minute like the rest of the key's quota. Over the limit answers `429` with the number in the message: "Export limit reached, N an hour."
</Warning>

## The Library shelf

| Route                                         | Body         | What it does                                                                                                |
| --------------------------------------------- | ------------ | ----------------------------------------------------------------------------------------------------------- |
| `GET /brands/{profileId}/library`             |              | Saved, approved and scheduled plays, newest first, with their words and any file already made               |
| `POST /brands/{profileId}/library/{playId}`   |              | Save a play to the shelf now                                                                                |
| `DELETE /brands/{profileId}/library/{playId}` |              | Take a play off the shelf, **and its approval with it**                                                     |
| `POST /brands/{profileId}/library/download`   | `{ limit? }` | A file for each play on the shelf, 1 to 25, 10 by default. Renders what has none, within the hour's renders |

## The autopilot

| Route                                    | Body                                                                                       | What it does                                                    |
| ---------------------------------------- | ------------------------------------------------------------------------------------------ | --------------------------------------------------------------- |
| `GET /brands/{profileId}/autopilot`      |                                                                                            | On or off, what is queued, what it has learned                  |
| `PATCH /brands/{profileId}/autopilot`    | `{ autopilot?, timezone?, postHour?, perDay?, weekdays?, platforms?, market?, delivery? }` | Change it. Answers the new status                               |
| `POST /brands/{profileId}/autopilot/run` |                                                                                            | Fill the empty slots now rather than at the next 15 minute pass |
| `GET /brands/{profileId}/performance`    |                                                                                            | What performs for this brand, from its own posts with numbers   |

## Publishing it yourself

Set the autopilot's `delivery` to `external` and Percify renders the plays but does not post them. They wait in a queue for your publisher, and you report back.

| Route                                           | Body                                                                                                                    | What it does                              |
| ----------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------- | ----------------------------------------- |
| `GET /brands/{profileId}/queue`                 |                                                                                                                         | Rendered plays waiting for your publisher |
| `POST /brands/{profileId}/queue/{jobId}/posted` | `{ provider?, postedAt?, posts: [{ platform, externalId, url? }] }`                                                     | This job went out                         |
| `POST /brands/{profileId}/queue/{jobId}/failed` | `{ reason? }`                                                                                                           | This job could not be posted              |
| `POST /brands/{profileId}/metrics`              | `{ items: [{ jobId, platform, views?, likes?, comments?, shares?, saves?, reach?, impressions?, url?, externalId? }] }` | The numbers those posts earned            |

Reporting metrics back is what lets the brand learn: `GET /performance` reads from posts that have numbers.

## FAQ

<AccordionGroup>
  <Accordion title="Which plan do I need?">
    The same as any Percify API key: **Scale** ($65 a month) or **Ultra** ($128). See [Percify pricing](/percify/payments).
  </Accordion>

  <Accordion title="Does writing words or rendering cost credits?">
    Composer's writing and downloads do not use credits, but they are capped per day and renders are capped per hour. See [Composer](/brand-os/composer) for the limits.
  </Accordion>

  <Accordion title="Can an agent do something I cannot do in the app?">
    No. Every route runs through the same owner-scoped methods the app calls.
  </Accordion>

  <Accordion title="Can I tell an agent's picks from my own?">
    Yes. Round actions made through the API are logged with `source: "api"`.
  </Accordion>
</AccordionGroup>

## Related

<CardGroup cols={2}>
  <Card title="Composer in the app" icon="palette" href="/brand-os/composer">
    The same rounds, with a UI.
  </Card>

  <Card title="The Percify MCP server" icon="plug" href="/mcp-server">
    The same loop as MCP tools, for Claude and other clients.
  </Card>

  <Card title="Authenticate API requests" icon="key" href="/percify/api-auth">
    Creating, capping and revoking pk\_live keys.
  </Card>

  <Card title="Scheduling and autopilot" icon="calendar" href="/brand-os/scheduling">
    Connected accounts, platforms and posting times.
  </Card>
</CardGroup>
