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

# Publishing

> Turn finished media into a post and send it to your creator's connected accounts.

Publishing is two calls: build the post, then send it. They are separate on purpose —
the split is where a human fits, if you want one.

<Steps>
  <Step title="Create the post">
    ```bash theme={null}
    curl https://api.creatorline.io/v1/clips \
      -H "Authorization: Bearer $CREATORLINE_API_KEY" \
      -H "Content-Type: application/json" \
      -d '{
        "account_id": "e307923d-…",
        "asset_ids": ["9b2f7c10-…"],
        "caption": "morning ritual ☕️",
        "hashtags": ["skincare", "grwm"]
      }'
    ```

    The post is created at **`pending_review`**. Nothing is public yet — it shows up in
    the studio's review queue like any other post.

    `channels` defaults to that creator's **connected** channels, so most calls need only
    `account_id`, `asset_ids` and `caption`. The format (photo, carousel or video) is
    derived from the media and validated against every target platform before the post is
    written — a carousel that a platform cannot take is a `400`, not a failed publish an
    hour later.
  </Step>

  <Step title="Send it">
    ```bash theme={null}
    curl https://api.creatorline.io/v1/clips/{id}/publish \
      -H "Authorization: Bearer $CREATORLINE_API_KEY" \
      -H "Content-Type: application/json" \
      -d '{ "mode": "now" }'
    ```

    | mode       |                                                       |
    | ---------- | ----------------------------------------------------- |
    | `now`      | post immediately                                      |
    | `schedule` | post at `scheduled_for` (`"21:30"`)                   |
    | `queue`    | the creator's next free slot in their posting cadence |
  </Step>

  <Step title="Watch it land">
    ```bash theme={null}
    curl https://api.creatorline.io/v1/clips/{id} \
      -H "Authorization: Bearer $CREATORLINE_API_KEY"
    ```

    ```json theme={null}
    {
      "object": "clip",
      "status": "published",
      "channels": ["TikTok", "IG"],
      "channel_posts": [
        { "platform": "TikTok", "status": "published", "url": "https://tiktok.com/@…/video/…" },
        { "platform": "IG", "status": "published", "url": "https://instagram.com/p/…" }
      ]
    }
    ```
  </Step>
</Steps>

## The review gate

<Warning>
  `POST /v1/clips/{id}/publish` is the only call in this API that reaches a real audience,
  and it is not undoable. Treat it the way you would treat a deploy to production.
</Warning>

The states a post moves through:

```
pending_review  →  scheduled  →  published
      ↓
  rejected
```

Three rules hold regardless of who is calling:

* A post **enters** at `pending_review`. The API cannot create one in any other state.
* `scheduled` is reachable **only** from `pending_review`. Asking to publish an already
  published post is a `400` that says so.
* `published` is written **only** after the platform confirms. If the hand-off fails, the
  post stays `scheduled` with the reason recorded, and you can retry — it is never
  falsely marked live.

<Note>
  **Want a human in the loop?** Create posts and never call publish. They queue up in the
  studio's review screen, where an operator approves them exactly as they would a post
  the platform generated. The API gives you the operator's authority; it does not force
  you to use it.
</Note>

Every publish writes an audit entry naming the API key that triggered it, so
"who posted this?" always has an answer.

## Requirements

<AccordionGroup>
  <Accordion title="The creator needs a connected channel">
    Channels are connected in the studio, per creator. A creator with none gets a `400`
    telling you so — the API cannot connect accounts on your behalf.
  </Accordion>

  <Accordion title="Media has to be finished">
    Use asset ids from a `succeeded` generation, or from a completed upload. An asset that
    is still uploading is rejected.
  </Accordion>

  <Accordion title="Ten assets maximum">
    One video, or up to ten stills for a carousel. Platform-specific carousel limits are
    checked at creation.
  </Accordion>
</AccordionGroup>

## From the CLI

```bash theme={null}
ASSET=$(crl gen create text-to-photo --prompt "matcha flatlay" --wait --json | jq -r '.output[0].asset_id')
crl posts create --asset "$ASSET" --caption "morning ritual ☕️" --publish now
```

`crl posts create` alone leaves the post in review. `--publish now|schedule|queue` chains
the second call. Publishing from a terminal asks for confirmation; pass `--yes` in
scripts.


## Related topics

- [Creatorline API](/index.md)
- [Get one post](/api-reference/posts/get-one-post.md)
- [Authentication](/authentication.md)
- [MCP server](/mcp.md)
