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

> ## Agent Instructions
> PostQueen's hosted API is https://api.postqueen.ai. The public API base is https://api.postqueen.ai/public/v1 and takes the raw API key in the Authorization header, with no Bearer prefix.
> The MCP server for posting is https://api.postqueen.ai/mcp/YOUR_API_KEY (or /mcp with Authorization: Bearer YOUR_API_KEY). docs.postqueen.ai/mcp only searches these docs and cannot post.
> The API key is under Connections > API Keys in the app, and only workspace admins can see it.

# Schedule a post with the API

> List your channels, read a network's settings, upload media, create the post and check that it went out.

This walks through one post from start to finish: an image post to Bluesky, scheduled for a set time, then checked. It takes six calls. Bluesky needs no settings, so the post body stays short; the WordPress notes show what changes for a network that does.

You need an API key and at least one connected channel. To connect one, see [Connect a channel](/using/connect-a-channel).

<Info>
  **Where the API key is:** in the app, open **Connections > API Keys**. Only a workspace Admin or Super Admin can reveal or rotate it; other members see it masked. A workspace has one key, and each agent card on **Connections** shows your MCP address with the key already filled in.
</Info>

## 1. Check the key

```bash theme={"system"}
curl "https://api.postqueen.ai/public/v1/is-connected" \
  -H "Authorization: YOUR_API_KEY"
```

`{ "connected": true }` means the key works. A `401` says why it does not: see [Limits and errors](/public-api/limits-and-errors#401-messages).

## 2. Find the channel

```bash theme={"system"}
curl "https://api.postqueen.ai/public/v1/integrations" \
  -H "Authorization: YOUR_API_KEY"
```

```json theme={"system"}
[
  {
    "id": "cmg1ozx2c0000qz0h8c7w5v3n",
    "name": "PostQueen",
    "identifier": "bluesky",
    "disabled": false,
    "profile": "postqueen.bsky.social",
    "picture": "https://cdn.example.com/avatar.png"
  }
]
```

Keep the `id` of the channel you want. It is `YOUR_CHANNEL_ID` below.

## 3. Read what the network needs

```bash theme={"system"}
curl "https://api.postqueen.ai/public/v1/integration-settings/YOUR_CHANNEL_ID" \
  -H "Authorization: YOUR_API_KEY"
```

```json theme={"system"}
{
  "output": {
    "rules": "Bluesky can have maximum 1 video or 4 pictures in one post, it can also be without attachments",
    "maxLength": 300,
    "settings": "No additional settings required",
    "tools": []
  }
}
```

Bluesky takes up to 300 characters, up to four pictures or one video, and no settings.

<Accordion title="For WordPress: fill the settings with a tool" icon="wrench">
  A WordPress channel answers with a `settings` schema that requires `title` and `type`. `type` is a post type such as `posts`, and the `postTypes` tool lists them:

  ```bash theme={"system"}
  curl -X POST "https://api.postqueen.ai/public/v1/integration-trigger/YOUR_CHANNEL_ID" \
    -H "Authorization: YOUR_API_KEY" \
    -H "Content-Type: application/json" \
    -d '{"methodName": "postTypes", "data": {}}'
  ```

  ```json theme={"system"}
  { "output": [{ "id": "posts", "name": "Posts" }, { "id": "pages", "name": "Pages" }] }
  ```

  The post then carries `"settings": { "title": "...", "type": "posts" }`. Every network's settings: [Posting settings by network](/public-api/providers/overview).
</Accordion>

## 4. Upload the image

```bash theme={"system"}
curl -X POST "https://api.postqueen.ai/public/v1/upload" \
  -H "Authorization: YOUR_API_KEY" \
  -F "file=@launch-banner.png"
```

```json theme={"system"}
{
  "id": "e639003b-f727-4a1e-87bd-74a2c48ae41e",
  "name": "a1b2c3d4e5.png",
  "originalName": "launch-banner.png",
  "path": "https://cdn.example.com/a1b2c3d4e5.png",
  "thumbnail": null,
  "alt": null,
  "status": "ready"
}
```

Keep `id` and `path`. An image is `ready` at once. A video comes back `processing`: call [Get media status](/public-api/uploads/media-status) every few seconds until it is `ready`, and use the `path` from that answer.

## 5. Create the post

Pick a time in UTC that is still ahead: with `schedule`, a time that has already passed publishes the post at once. To use the channel's next free posting time instead, [Find a free slot](/public-api/integrations/find-slot) returns one.

<CodeGroup>
  ```bash curl theme={"system"}
  curl -X POST "https://api.postqueen.ai/public/v1/posts" \
    -H "Authorization: YOUR_API_KEY" \
    -H "Content-Type: application/json" \
    -d '{
      "type": "schedule",
      "date": "2026-10-01T09:00:00.000Z",
      "shortLink": false,
      "tags": [],
      "posts": [
        {
          "integration": { "id": "YOUR_CHANNEL_ID" },
          "value": [
            {
              "content": "The new calendar is live.",
              "image": [
                { "id": "e639003b-f727-4a1e-87bd-74a2c48ae41e", "path": "https://cdn.example.com/a1b2c3d4e5.png" }
              ]
            }
          ]
        }
      ]
    }'
  ```

  ```js Node.js theme={"system"}
  const res = await fetch("https://api.postqueen.ai/public/v1/posts", {
    method: "POST",
    headers: {
      Authorization: process.env.POSTQUEEN_API_KEY,
      "Content-Type": "application/json",
    },
    body: JSON.stringify({
      type: "schedule",
      date: "2026-10-01T09:00:00.000Z",
      shortLink: false,
      tags: [],
      posts: [
        {
          integration: { id: "YOUR_CHANNEL_ID" },
          value: [
            {
              content: "The new calendar is live.",
              image: [{ id: "e639003b-f727-4a1e-87bd-74a2c48ae41e", path: "https://cdn.example.com/a1b2c3d4e5.png" }],
            },
          ],
        },
      ],
    }),
  });
  if (res.status !== 201) throw new Error(JSON.stringify(await res.json()));
  const [{ postId }] = await res.json();
  ```
</CodeGroup>

The answer is `201` with the new post's id:

```json theme={"system"}
[{ "postId": "cmg1p0s7k0001qz0h4yq2a8r1", "integration": "cmg1ozx2c0000qz0h8c7w5v3n" }]
```

A `400` means nothing was saved, and the body says what to fix. For example, text over 300 characters on Bluesky answers `post is too long, please fix it`.

<Tip>
  To post the same thing on several channels, add one entry per channel to `posts`. To add a thread, add more items to `value`, each with `"image": []` if it has no media and an optional `delay` in minutes. It is still one request.
</Tip>

## 6. Check that it went out

List the posts of that day:

```bash theme={"system"}
curl "https://api.postqueen.ai/public/v1/posts?startDate=2026-10-01T00:00:00.000Z&endDate=2026-10-01T23:59:59.000Z" \
  -H "Authorization: YOUR_API_KEY"
```

Find your `postId` and read its `state`:

| `state`     | What it means                                        |
| ----------- | ---------------------------------------------------- |
| `QUEUE`     | Scheduled, not published yet                         |
| `PUBLISHED` | Live. `releaseURL` links to it on the network        |
| `ERROR`     | Publishing failed. `error` has the network's reason  |
| `DRAFT`     | A draft. It keeps its date, and nothing publishes it |

<Check>
  `PUBLISHED` with a `releaseURL` means the post is live on Bluesky.
</Check>

To be told the moment a post goes out instead of asking, add a [webhook](/using/webhooks).

## Change or cancel it

| To                               | Call                                                                                   |
| -------------------------------- | -------------------------------------------------------------------------------------- |
| Hold it back without deleting it | [Change post status](/public-api/posts/change-status) to `draft`                       |
| Change its network settings      | [Update post settings](/public-api/posts/settings)                                     |
| Change the text or the time      | [Create a post](/public-api/posts/create) again with `value[].id` set to the post's id |
| Remove it                        | [Delete a post](/public-api/posts/delete)                                              |

## Next steps

<CardGroup cols={2}>
  <Card title="Create a post" icon="send" href="/public-api/posts/create">
    Every field of the request, the post types and the settings each network requires.
  </Card>

  <Card title="Posting settings by network" icon="sliders-horizontal" href="/public-api/providers/overview">
    The settings of each network, with examples.
  </Card>

  <Card title="Limits and errors" icon="gauge" href="/public-api/limits-and-errors">
    The hourly limits, and what each status code means.
  </Card>

  <Card title="Node.js SDK" icon="package" href="/public-api/sdk">
    The same calls from JavaScript or TypeScript.
  </Card>
</CardGroup>
