---
title: REST API
description: Base URL, authentication, request conventions, and the main endpoints of the BrainyShorts REST API.
sidebar:
  label: REST API
  icon: braces
---

The REST API is the base every other surface builds on. The CLI calls it, and the MCP server exposes the same operations as tools.

| | |
| --- | --- |
| Base URL | `https://www.brainyshorts.com/api/cli` |
| Auth | `Authorization: Bearer ba_sk_...` ([Authentication](/authentication)) |
| Format | JSON in and out; send `Content-Type: application/json` |
| Spec | [`/openapi.json`](https://www.brainyshorts.com/openapi.json), also browsable under [API reference](/reference) |

:::note
Use `www.brainyshorts.com`. `api.brainyshorts.com` is only a fallback alias: it answers `/api/cli`, `/api/agent`, and `/api/skills`, and redirects everything else, including MCP and OAuth, to `www`.
:::

## Endpoints

| Method | Path | Purpose |
| --- | --- | --- |
| `GET` | `/accounts` | Connected social accounts |
| `GET` | `/posts?status=&platform=&limit=` | List post deliveries (max 100) |
| `POST` | `/posts` | Create a draft, scheduled, or immediate post |
| `PATCH` | `/posts` | Edit or reschedule a draft or scheduled post |
| `DELETE` | `/posts` | Delete a draft, scheduled, or failed post (`{"id": "..."}`) |
| `POST` | `/posts/bulk` | Create up to 100 drafts or schedules idempotently |
| `GET` | `/posts/bulk?batch_id=&offset=&limit=` | Batch status |
| `PATCH` | `/posts/bulk` | Update up to 100 scheduled posts |
| `POST` | `/media/uploads` | Reserve a managed upload |
| `PATCH` | `/media/uploads` | Complete a managed upload |
| `GET` | `/articles` | List long-form articles |
| `POST` | `/articles` | Create, schedule, or publish an article |
| `DELETE` | `/articles` | Delete an article (`{"id": "..."}`) |
| `GET` | `/analytics` | Metrics by platform, account, post, and date |

Skills and runner endpoints live elsewhere: see [Skills](/skills) and [Scheduled runs](/automations).

## Creating a post

```json
{
  "title": "Launch teaser",
  "caption": "Something new is coming.",
  "media_type": "video",
  "media_urls": ["https://storage.brainyshorts.com/..."],
  "targets": [{ "social_account_id": "3f0c...", "platform": "instagram" }],
  "action": "schedule",
  "scheduled_at": "2026-10-01T17:00:00Z"
}
```

| Field | Rules |
| --- | --- |
| `title` | 1–120 characters. Defaults to the start of the caption. |
| `caption` | 1–2,200 characters (280 when an X target is present) |
| `media_type` | `image` (exactly 1 URL), `carousel` (2–10), or `video` (exactly 1). Inferred when omitted. |
| `media_urls` | JSON array of HTTPS URLs, up to 10. A comma-separated string is rejected. |
| `targets` | Up to 12 `{ social_account_id, platform, settings? }` |
| `action` | `draft`, `schedule`, or `publish` |
| `scheduled_at` | ISO 8601 with offset, in the future. `scheduled_time` is accepted as an alias. |

The legacy `integrations` field is still accepted in place of `targets`.

:::warning
**Omitting `action` publishes.** When `action` is missing, the REST API publishes right away, or schedules when `scheduled_at` is set. The CLI and the MCP server default to drafts; raw HTTP does not. Always send `action` explicitly.
:::

The response is `201` with `{ "post": {...}, "social_posts": [...] }`, one delivery per target.

### Media URLs

Upload through `/media/uploads` (see the [Quickstart](/quickstart)) and use the returned `public_url`. You can also pass your own durable HTTPS URL, but several platforms only pull from verified domains. TikTok, for example, rejects URLs outside `storage.brainyshorts.com`. Temporary model-output URLs such as `replicate.delivery` are rejected.

## Editing and deleting

`PATCH /posts` takes `id` (either the parent post id or any delivery id) plus at least one of `caption`, `scheduled_at`, or `add_targets`. Send `status: "draft"` instead to unschedule. Every still-scheduled delivery under the parent changes together.

`DELETE /posts` removes a post in `draft`, `scheduled`, `retry_scheduled`, or `failed` status. Published posts can't be deleted through the API.

## Articles

```json
{
  "title": "How we cut build times in half",
  "markdown": "# ...",
  "tags": ["devops", "ci"],
  "canonical_url": "https://example.com/blog/builds",
  "targets": [
    { "social_account_id": "...", "platform": "devto" },
    { "social_account_id": "...", "platform": "reddit", "settings": { "subreddit": "programming" } }
  ],
  "action": "publish"
}
```

Article `action` defaults to `draft`. With `publish`, delivery happens inline and the response includes per-destination `results` with the live URLs. Platforms are `devto`, `hashnode`, `reddit`, `linkedin`, and `x`; see [Platforms](/platforms#articles).

## Errors

Errors are JSON with an HTTP status:

```json
{ "error": "Human-readable message", "code": "MACHINE_CODE" }
```

Validation failures return `400` with the field-level problems:

```json
{ "error": "Invalid post", "issues": { "formErrors": [], "fieldErrors": { "scheduled_at": ["scheduled_at must be in the future"] } } }
```

| Status | Meaning |
| --- | --- |
| `400` | Invalid body; read `issues` |
| `401` | Missing, malformed, expired, or revoked key, or a key without the scope the endpoint needs |
| `404` | The resource does not exist or isn't yours. Unknown `/api/...` paths also return JSON 404s with links to these docs. |
| `409` | Idempotency conflict (bulk item reused with different content) |
| `410` | Removed hosted-processing endpoint; see below |
| `429` | Media storage quota exceeded (`MEDIA_STORAGE_QUOTA_EXCEEDED`) |

### Removed endpoints

BrainyShorts no longer generates or processes media on its servers. These endpoints return `410 Gone` with a pointer to the replacement:

- `POST /api/cli/upload`: use `/api/cli/media/uploads`
- `POST /api/cli/generate-photo-content`: generate with your own agent, then create a post
