# Create a post

From a theme to a draft you can review, with the polling loop.

Creating is asynchronous. You send a theme, get `202` with an id, and poll until the post leaves the working states. Median 43 s, p99 87 s, measured in production.

You need a `projectId`. If you do not have one, `GET /projects` lists them.

## Theme to draft

The whole sequence. This block is meant to be handed to an agent as is.

### 1. Find the project you are writing for.

```bash
curl -H "Authorization: Bearer $POSTTAR_KEY" "https://api.posttar.com/v1/projects"
```

```json
{
  "projects": [
    { "id": "c85848ba-...", "name": "My brand", "instagram": null, "createdAt": "2026-07-31T14:29:14Z" }
  ],
  "limit": 1,
  "canCreateMore": false
}
```

### 2. Start the post. Answers 202, the work runs in the background.

```bash
curl -X POST -H "Authorization: Bearer $POSTTAR_KEY" -H "Content-Type: application/json" \
  -d '{"projectId":"c85848ba-...","theme":"three signs your niche is too broad"}' \
  "https://api.posttar.com/v1/posts"
```

```json
{ "postId": "dd8d142c-...", "format": "carousel", "state": "generating", "remainingCredits": 5 }
```

### 3. Poll until status is no longer queued or generating.

```bash
curl -H "Authorization: Bearer $POSTTAR_KEY" "https://api.posttar.com/v1/posts/dd8d142c-..."
```

```json
{
  "id": "dd8d142c-...",
  "status": "draft",
  "projectId": "c85848ba-...",
  "workspaceId": "2bd5c04d-...",
  "scheduledFor": null,
  "textContent": "Your niche might be broader than you think...",
  "mediaUrls": [{ "url": "https://.../slide-1.webp", "type": "image" }]
}
```

### 4. Read the slides, if you want to edit them before publishing.

```bash
curl -H "Authorization: Bearer $POSTTAR_KEY" "https://api.posttar.com/v1/posts/dd8d142c-.../slides"
```

## Writing from a link

`POST /posts` also takes a `link`. When you send one, Posttar reads that page first, and `theme` becomes the instruction for what to do with it ("turn this into three tips", for example). You can send either one alone, but not neither.

## What can go wrong

| `code` | What it means here |
| --- | --- |
| `invalid_input` | Neither `theme` nor `link` was sent, or `projectId` is not a valid id |
| `not_found` | The project is not in this workspace |
| `invalid_state` | No credits left on the plan |

> Credits are spent when generation starts, not when it succeeds. A post that ends in `failed` has already cost its credit.
