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

# API reference

> Full machine-readable spec is published as openapi.json — Mintlify renders the interactive playground below.

The Postbreeze REST API exposes everything you need to drive the
scheduling pipeline from outside the dashboard.

* **Base URL**: `https://api.postbreeze.ai/api/v1`
* **Auth**: `Authorization: Bearer pb_live_…`
* **Content type**: `application/json` request and response bodies
* **Errors**: standard HTTP status codes; bodies are
  `{ "statusCode", "code", "message", "requestId" }` (see [Errors](/concepts/errors))
* **Rate limits**: 60 req/min burst, 1000 req/15min sustained per key
  (see [Rate limits](/concepts/rate-limits))

## Workspace resolution

API keys are issued in one of two modes:

* **Full access** (default) — the key can act on every workspace its
  owner is a member of.
* **Scoped** — the key is restricted to an explicit allow-list of
  workspaces.

How endpoints find the workspace for each request:

| Endpoint shape                                                          | Source of `workspaceId`                                                              |       |          |                                 |
| ----------------------------------------------------------------------- | ------------------------------------------------------------------------------------ | ----- | -------- | ------------------------------- |
| `GET /posts/{id}`, `PATCH /posts/{id}`, `DELETE /posts/{id}`            | Derived from `Post.workspaceId`                                                      |       |          |                                 |
| \`POST /posts/{id}/schedule                                             | reschedule                                                                           | retry | cancel\` | Derived from `Post.workspaceId` |
| `POST /posts`                                                           | Derived from the first account in `platforms[]` / `targets[]`                        |       |          |                                 |
| `GET /social-accounts/{id}`, `DELETE /social-accounts/{id}`             | Derived from `SocialAccount.workspaceId`                                             |       |          |                                 |
| `POST /comments/{id}/reply`, `POST /comments/{id}/read`                 | Derived from `Comment.workspaceId`                                                   |       |          |                                 |
| `GET /media/{id}`, `PATCH /media/{id}`, `DELETE /media/{id}`            | Derived from the media's owner workspace                                             |       |          |                                 |
| `GET /posts`, `GET /social-accounts`, `GET /comments`                   | Optional `?workspaceId=` — omit to fan out across every workspace the key can act on |       |          |                                 |
| `GET /media`                                                            | **Required** `?workspaceId=` (media is account-global)                               |       |          |                                 |
| `POST /media/presign`, `POST /media/from-url`, `POST /comments/refresh` | **Required** `?workspaceId=`                                                         |       |          |                                 |

Derivation is enforced at the resolver layer; the server never widens
a scoped key's reach. A request to a resource that belongs to a
workspace outside the key's allow-list returns `403`.

## Endpoints in v1

| Resource            | Methods                                                                                                                                                                                                                           |
| ------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Workspaces**      | `GET /workspaces`, `GET /workspaces/{id}`, `POST /workspaces` (create)                                                                                                                                                            |
| **Posts**           | `GET /posts`, `POST /posts`, `GET /posts/{id}`, `PATCH /posts/{id}`, `DELETE /posts/{id}`, `POST /posts/{id}/schedule`, `POST /posts/{id}/cancel`, `POST /posts/{id}/retry`, `POST /posts/{id}/targets/{tid}/retry-first-comment` |
| **Social accounts** | `GET /social-accounts`, `GET /social-accounts/{id}`, `DELETE /social-accounts/{id}`                                                                                                                                               |
| **Media**           | `GET /media`, `POST /media/presign`, `POST /media/from-url`, `POST /media/{id}/complete`, `GET /media/{id}`, `PATCH /media/{id}`, `DELETE /media/{id}`                                                                            |
| **Comments**        | `GET /comments`, `POST /comments/refresh`, `POST /comments/{id}/reply`, `POST /comments/{id}/read`                                                                                                                                |

## Two body shapes for `POST /posts`

The post-create + post-update endpoints accept a discriminated body —
**send exactly one** of `platforms` (flat) or `targets` (nested).
Sending both returns `400 MUST_SPECIFY_EXACTLY_ONE`.

* **Flat** (`{ content, platforms: [{ accountId }] }`) — Zernio-style,
  recommended for most calls. Same caption for every platform.
* **Nested** (`{ caption, targets: [{ socialAccountId, platformOptions }] }`) —
  full control over per-platform options, per-target media overrides,
  and platform-specific `kind` discriminators.

See [Platforms → Overview](/platforms/overview) for the full schema of
each shape.

## URL ingest for media

`POST /media/from-url` accepts a public HTTPS URL and fetches the bytes
server-side. The fetch is **SSRF-guarded** — private IPs, link-local
addresses, metadata-IMDS hostnames, and DNS-rebinding tricks are
rejected. Redirects are not followed (`redirect: manual`). By the time
the call returns, the bytes live in R2; the source URL is never
re-fetched at publish time.

## Dashboard-internal routes

A second set of routes under `/api/v1/me/media/*` powers the dashboard
UI — they take an `x-workspace-id` header instead of inferring from a
key. SDK and MCP callers should ignore these; they're not part of the
public surface.

## OpenAPI spec

The full machine-readable spec lives at `/openapi.json` on this site
and is regenerated on every Postbreeze deploy. The TypeScript SDK
`@postbreeze/node` is generated from this spec.
