# BrainyShorts Docs > Publish and schedule agent-made media to TikTok, Instagram, YouTube, LinkedIn, X, and article platforms through a REST API, a CLI, or a hosted MCP server. # BrainyShorts for agents Source: https://docs.brainyshorts.com/ BrainyShorts is a free publishing control plane. You connect your social accounts once in the dashboard. After that, an agent can upload finished media and publish or schedule posts without ever holding a platform token. BrainyShorts does not generate media. Your agent, your runner, or your own tooling makes the images, video, or article; BrainyShorts stores it, schedules it, and hands it to each platform. ## Addresses Everything lives on one host, `www.brainyshorts.com`. Don't configure `api.brainyshorts.com`: it only exists as a fallback for REST calls and doesn't serve MCP or OAuth. | What | Address | | --- | --- | | REST API | `https://www.brainyshorts.com/api/cli` | | Agent runner API | `https://www.brainyshorts.com/api/agent` | | OpenAPI spec | `https://www.brainyshorts.com/openapi.json` | | Hosted MCP server | `https://www.brainyshorts.com/api/mcp` | | CLI | `npm install -g brainy-shorts` (binary `brainy`) | | API keys | `https://www.brainyshorts.com/dashboard/api-keys` | ## Pick an interface **[MCP server](/mcp)** Best for Claude, Cursor, ChatGPT, and other MCP clients. OAuth sign-in, no key to paste. **[REST API](/api)** Plain HTTPS with a Bearer key. Works from any language or agent framework. **[CLI](/cli)** `brainy` wraps the REST API for shell scripts and coding agents. **[Scheduled runs](/automations)** BrainyShorts schedules the work and your own runner does it. ## For agents reading this - Machine-readable index: [`/llms.txt`](/llms.txt), full corpus: [`/llms-full.txt`](/llms-full.txt). - The same addresses are served from the app itself at `https://www.brainyshorts.com/llms.txt`, `/.well-known/mcp.json`, and `/.well-known/api-catalog`. - An installable agent skill is published at [`/.well-known/agent-skills/index.json`](/.well-known/agent-skills/index.json). --- # REST API Source: https://docs.brainyshorts.com/api 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 --- # Authentication Source: https://docs.brainyshorts.com/authentication BrainyShorts has two ways to authenticate, one per surface. | Surface | Credential | How to send it | | --- | --- | --- | | REST API, CLI, runners | API key (`ba_sk_...`) | `Authorization: Bearer ` | | Hosted MCP server | OAuth access token | Your MCP client handles it | ## API keys 1. **Sign in** Create a free account at [brainyshorts.com](https://www.brainyshorts.com/signup) and connect at least one social account. 2. **Create a key** Open [Dashboard → API keys](https://www.brainyshorts.com/dashboard/api-keys) and create a key. The full key is shown once and never again, so store it in your secret manager right away. 3. **Send it as a Bearer token** ```bash curl https://www.brainyshorts.com/api/cli/accounts \ -H "Authorization: Bearer $BRAINY_API_KEY" ``` Keys: - start with `ba_sk_`, and only a hash is stored - expire 365 days after creation - carry the scopes `cli:access`, `media:write`, and `automation:runs:claim` - can be revoked from the same dashboard page :::warning Send the key in the `Authorization` header. The `x-api-key` header is not accepted, and keys are never read from query strings. ::: ## OAuth for MCP The hosted MCP server at `https://www.brainyshorts.com/api/mcp` uses OAuth 2.1 with PKCE and dynamic client registration. You don't create anything by hand: add the server URL to your client, and it discovers the flow from: - `https://www.brainyshorts.com/.well-known/oauth-protected-resource/api/mcp` - `https://www.brainyshorts.com/.well-known/oauth-authorization-server` The client opens a browser window, you sign in and approve, and the client stores the token. Scopes are `brainy:read` and `brainy:write`. See [MCP server](/mcp) for per-client setup. --- # Scheduled runs Source: https://docs.brainyshorts.com/automations An automation is a skill that runs on a schedule. BrainyShorts stores what should happen (skill version, cron, targets, publish mode). A runner you control decides when to wake your agent and does the actual work. BrainyShorts never calls a model and never hands your agent a social platform token. ## How a run works 1. **Create the automation** An agent connected over [MCP](/mcp) loads a skill with `brainy_get_skill`, registers the machine with `brainy_register_executor`, and calls `brainy_create_automation`. BrainyShorts validates it, stores revision 1, and returns an install manifest. 2. **The runner syncs** `brainy-shorts-runner tick` calls `GET /api/agent/automations/sync`, installs new revisions, removes paused or deleted ones, and acknowledges each applied revision with `POST /api/agent/automations/{id}/ack`. Idle ticks never invoke a model. 3. **A run starts** When the cron is due, the runner calls `POST /api/agent/automations/{id}/start` with the idempotency key `::`. BrainyShorts checks the automation is still enabled and current, then returns a `ba_run_...` token that is valid for 6 hours and bound to that single run. 4. **Your agent does the work** The runner launches your agent with `BRAINY_RUN_TOKEN` in its environment. The agent posts progress to `/api/agent/runs/{id}/events` and uploads media through `/api/agent/runs/{id}/media/uploads`. 5. **The run completes** The agent calls `POST /api/agent/runs/{id}/complete-local`, or `/fail`. BrainyShorts then creates a draft, or publishes when the automation's publication mode is `auto`. ## Automation settings | Setting | Values | | --- | --- | | Publication mode | `draft` (default) or `auto` | | Schedule | cron expression plus an IANA timezone | | Missed runs | `skip`, `run_once`, or `catch_up` | | Skill | id and pinned version, see [Skills](/skills) | Turning on `auto` is a separate decision from creating the automation. Pausing or disconnecting an account takes effect immediately, because every run start and completion is checked on the server. ## Runner The reference runner `brainy-shorts-runner` currently ships inside the BrainyShorts repository rather than on npm. It needs Node 20 or newer and uses the `codex` adapter. ```bash cd runner && npm install && npm link export BRAINY_API_KEY="ba_sk_..." brainy-shorts-runner install ./automation.json brainy-shorts-runner tick # run this from cron or launchd every minute ``` | Command | What it does | | --- | --- | | `install ` | Install a manifest and acknowledge its revision | | `list` | Installed manifests | | `remove ` | Uninstall one | | `sync ` | Pull desired revisions | | `run` | Start due runs, using no network when nothing is due | | `tick ` | `sync` followed by `run`, which is the normal scheduler command | Any process that speaks the HTTP endpoints below can act as a runner, so you can write your own in any language. ## Runner endpoints All of these live under `https://www.brainyshorts.com/api/agent`. The first three take an API key; the rest take the run token. | Method | Path | Auth | | --- | --- | --- | | `POST` | `/executors` | API key | | `GET` | `/automations/sync` | API key | | `POST` | `/automations/{id}/ack`, `/automations/{id}/start` | API key | | `GET` | `/runs/{id}` | run token | | `POST` | `/runs/{id}/events` | run token | | `POST`, `PATCH` | `/runs/{id}/media/uploads` | run token | | `POST` | `/runs/{id}/direct-upload`, `GET /runs/{id}/direct-upload/status` | run token | | `POST` | `/runs/{id}/complete-local`, `/runs/{id}/fail` | run token | `direct-upload` returns a provider upload session so TikTok and YouTube video bytes go straight from your machine to the platform, up to 64 MiB. A YouTube handoff also needs the parent runner's API key in `x-brainy-runner-key`, so only the trusted runner process, not the spawned agent, can stage a YouTube upload. Instagram, LinkedIn, and X use managed media instead. ## Legacy claim queue Automations created before `local_push` use a pull queue. A runner claims work with an API key, then renews, completes, skips, or fails it with the returned `claim_token`: `POST /api/agent/runs/claim` → `/runs/{id}/renew` → `/runs/{id}/complete` | `/skip` | `/fail` The queue still works for existing Instagram automations. Create new automations over MCP. --- # Bulk scheduling Source: https://docs.brainyshorts.com/bulk-scheduling The bulk endpoint creates up to 100 drafts or scheduled posts per call. Each call is idempotent, so a timeout never creates duplicates: send the same request again. | | | | --- | --- | | REST | `POST`, `GET`, `PATCH` on `https://www.brainyshorts.com/api/cli/posts/bulk` | | MCP | `brainy_create_posts_bulk`, `brainy_get_post_batch`, `brainy_update_posts_bulk` | | Auth | API key with `cli:access`, or an MCP token with `brainy:write` (`brainy:read` for status) | ## How idempotency works - Pick one UUID `batch_id` for the whole import. - Give every item a stable `item_key` (letters, digits, `.`, `_`, `:`, `-`, up to 100 characters), such as `2027-01-01-tip-001`. - Send at most 100 items per call. A year of 1,000 posts is ten calls that share one `batch_id`. - Resending an identical item is a no-op. Resending a **different** item under an existing key returns `409`, because that is almost always a bug. Bulk accepts only `draft` and `schedule`. It never publishes immediately. ## Create a batch Media comes from completed managed uploads, referenced by `asset_id`. See the [Quickstart](/quickstart) for the upload steps. ```bash curl -s -X POST "https://www.brainyshorts.com/api/cli/posts/bulk" \ -H "Authorization: Bearer $BRAINY_API_KEY" \ -H "Content-Type: application/json" \ -d @batch.json ``` ```json { "batch_id": "11111111-1111-4111-8111-111111111111", "items": [ { "item_key": "2027-01-01-tip-001", "title": "One precise tip", "caption": "Ask for the verification evidence you care about.", "media_type": "video", "media": [{ "asset_id": "22222222-2222-4222-8222-222222222222" }], "targets": [ { "social_account_id": "33333333-3333-4333-8333-333333333333", "platform": "instagram" } ], "action": "schedule", "scheduled_at": "2027-01-01T18:00:00Z" } ] } ``` | Item field | Rules | | --- | --- | | `item_key` | Unique within the batch | | `title` | 1–120 characters | | `caption` | 1–2,200 characters | | `media_type` | `image` (1 asset), `carousel` (2–10), or `video` (1) | | `media` | `[{ asset_id, text?, aspect_ratio? }]` | | `targets` | Up to 12; each account at most once per item | | `action` | `draft` or `schedule` | | `scheduled_at` | Required for `schedule`; not allowed for `draft` | The response is `201` and includes `warnings` and a `next` hint. When posts are scheduled less than five minutes apart, a warning says so. They stay scheduled, but delivery can queue behind earlier posts, so leave five minutes or more between them where you can. ## Check a batch ```bash curl -s "https://www.brainyshorts.com/api/cli/posts/bulk?batch_id=$BATCH&offset=0&limit=100" \ -H "Authorization: Bearer $BRAINY_API_KEY" ``` The response has `total`, `has_more`, and one entry per item. Page with `offset` until `has_more` is `false`. ## Update scheduled posts `PATCH /posts/bulk` takes `{ "updates": [...] }` with up to 100 entries. Each entry has a `post_id` and the same fields as a single [`PATCH /posts`](/api#editing-and-deleting): `caption`, `scheduled_at`, `add_targets`, or `status: "draft"`. The response reports `updated_count`, `failed_count`, and a per-post `results` list. Updates aren't all-or-nothing, so retry only the `post_id` values that failed. --- # CLI Source: https://docs.brainyshorts.com/cli `brainy` is a thin client for the [REST API](/api). Every command accepts `--json` for machine-readable output. The package is [`brainy-shorts`](https://www.npmjs.com/package/brainy-shorts) on npm and needs Node 18 or newer. Every command maps to one REST endpoint, so anything the CLI can't do is still available through the [REST API](/api) or the [MCP server](/mcp). ## Configure ```bash npm install -g brainy-shorts brainy config --api-key ba_sk_... ``` For unattended runners, prefer environment variables: ```bash export BRAINY_API_KEY="ba_sk_..." export BRAINY_API_URL="https://www.brainyshorts.com" # optional, this is the default ``` The key comes from the flag, then `BRAINY_API_KEY`, then `~/.brainy/config.json`. The host comes from `BRAINY_API_URL`, then the config file, then `https://www.brainyshorts.com`. Requests go to `/api/cli/...` with `Authorization: Bearer `. ## Accounts ```bash brainy accounts --json ``` ## Posts `post create` makes a draft unless you pass `--publish` or a schedule. ```bash # Draft brainy post create -c "caption" -p tiktok -a # Scheduled, with media brainy post create -c "caption" -p tiktok -a \ -m "https://storage.brainyshorts.com/video.mp4" \ -s "2026-10-01T10:00:00Z" # Publish now brainy post create -c "caption" -p tiktok -a --publish # Several accounts at once brainy post create -c "caption" -i , # From a JSON file brainy post create --from-json post.json brainy post list -s scheduled -p tiktok --json brainy post update -c "new caption" brainy post update -s "2026-10-02T10:00:00Z" brainy post update --unschedule brainy post reschedule "2026-10-01T14:00:00Z" brainy post delete ``` `post update` accepts a parent post id or a platform delivery id. Every still-scheduled delivery under the parent is updated together. A `post.json` file looks like this: ```json post.json { "caption": "Post caption text", "media_urls": ["https://storage.brainyshorts.com/video.mp4"], "scheduled_time": "2026-10-01T10:00:00Z", "integrations": [ { "social_account_id": "uuid-1", "platform": "tiktok" }, { "social_account_id": "uuid-2", "platform": "youtube" } ] } ``` `media_urls` must be a JSON array, not a comma-separated string. ## Bulk scheduling ```bash brainy post bulk --from-json batch.json --json brainy post bulk-status --offset 0 --limit 100 --json brainy post bulk-update --from-json updates.json --json ``` See [Bulk scheduling](/bulk-scheduling) for the batch format and idempotency rules. ## Schedules ```bash brainy schedule list -p youtube brainy schedule cancel # reverts the post to a draft ``` ## Analytics ```bash brainy analytics overview --json brainy analytics overview -a brainy analytics overview -p x brainy analytics post ``` ## Runner commands For [scheduled runs](/automations): ```bash brainy automations list --json brainy agent claim --json brainy agent renew --claim-token --lease-seconds 900 --json brainy agent complete --claim-token --caption "..." --media "https://..." --json brainy agent complete --claim-token --caption "..." --slides ./slides.json --json brainy agent complete --from-json ./result.json --json brainy agent skip --claim-token --code NO_QUALIFYING_STORY --reason "..." --json brainy agent fail --claim-token --error "Source image download failed" --json ``` --- # Limits and pricing Source: https://docs.brainyshorts.com/limits ## Pricing BrainyShorts is free. There is no paid plan, no card on file, and no billing in the product today. Platform APIs can have their own costs. X in particular bills or meters API access separately, and that is between you and X. ## Connected accounts Each user can connect up to **5 social accounts** across all platforms. Reconnecting an account you already have does not count against the limit. ## Media | Limit | Value | | --- | --- | | Managed image size | 15 MiB | | Managed MP4 size | 200 MiB | | LinkedIn and X video | 32 MB | | TikTok photo post | up to 12 images | | TikTok photo title | truncated to 80 characters, hashtags stripped | ## Retries A failed publish is retried up to 3 times with exponential backoff. The last error is stored on the post and returned by the API. --- # MCP server Source: https://docs.brainyshorts.com/mcp BrainyShorts runs a hosted Model Context Protocol server. Your client signs in with OAuth, so there's no API key to paste. | | | | --- | --- | | URL | `https://www.brainyshorts.com/api/mcp` | | Transport | Streamable HTTP (stateless) | | Auth | OAuth 2.1, PKCE (S256), dynamic client registration | | Scopes | `brainy:read`, `brainy:write` | | Discovery | `https://www.brainyshorts.com/.well-known/mcp.json` | ## Connect a client **Claude Code** ```bash claude mcp add --transport http brainy-shorts https://www.brainyshorts.com/api/mcp ``` Then run `/mcp` inside Claude Code and choose **Authenticate**. **Claude.ai / Desktop** Open **Settings → Connectors → Add custom connector**, name it BrainyShorts, and paste `https://www.brainyshorts.com/api/mcp`. Claude opens the sign-in page when you first connect. **ChatGPT** With developer mode enabled, open **Settings → Apps & Connectors → Create**, paste `https://www.brainyshorts.com/api/mcp`, and choose OAuth authentication. **Codex** Add this to `~/.codex/config.toml`: ```toml [mcp_servers.brainy-shorts] url = "https://www.brainyshorts.com/api/mcp" ``` Then run `codex mcp login brainy-shorts`. **Cursor** Add this to `~/.cursor/mcp.json`. The `mcp-remote` bridge runs the OAuth flow on a local callback: ```json { "mcpServers": { "brainy-shorts": { "command": "npx", "args": ["-y", "mcp-remote", "https://www.brainyshorts.com/api/mcp"] } } } ``` **VS Code** Add this to `.vscode/mcp.json`: ```json { "servers": { "brainy-shorts": { "type": "http", "url": "https://www.brainyshorts.com/api/mcp" } } } ``` ## Tools The server has 26 tools. It has no resources or prompts. ### Read (`brainy:read`) | Tool | What it does | | --- | --- | | `brainy_list_accounts` | Connected social and article accounts | | `brainy_list_posts` | Posts and their per-platform delivery status | | `brainy_get_post_batch` | Status of a bulk batch | | `brainy_get_analytics` | Metrics by platform, account, or post | | `brainy_list_skills` | Available skills and versions | | `brainy_get_skill` | One skill's instructions | | `brainy_list_automations` | Scheduled automations | | `brainy_list_articles` | Long-form articles | | `brainy_get_article` | One article and its destinations | ### Write (`brainy:write`) | Tool | What it does | | --- | --- | | `brainy_reserve_media_upload` | Reserve a managed upload and get a presigned URL | | `brainy_complete_media_upload` | Verify and finish the upload | | `brainy_create_post` | Create a draft or scheduled post; it never publishes immediately | | `brainy_create_posts_bulk` | Up to 100 drafts or schedules, idempotent by `batch_id` and `item_key` | | `brainy_update_scheduled_post` | Edit caption, time, or targets | | `brainy_update_posts_bulk` | Update up to 100 scheduled posts | | `brainy_cancel_scheduled_post` | Return a scheduled post to draft | | `brainy_publish_post` | Publish now; requires `confirmation: "USER_REQUESTED_PUBLISH_NOW"` | | `brainy_create_article` | Create or schedule an article | | `brainy_publish_article` | Publish an article to its destinations | | `brainy_delete_article` | Delete an article | | `brainy_register_executor` | Register a local runner for scheduled runs | | `brainy_create_automation` | Create a scheduled automation from a skill | | `brainy_update_automation` | Change an automation | | `brainy_pause_automation` / `brainy_resume_automation` | Pause or resume one | | `brainy_delete_automation` | Delete one | :::note Immediate publishing is gated on purpose. An agent should call `brainy_publish_post` only when the person has asked for it in so many words. Everything else creates drafts or schedules that a person can review in the dashboard. ::: ## Typical flow 1. `brainy_list_accounts` to find target account ids. 2. `brainy_reserve_media_upload` with the file's type, byte length, SHA-256, and MD5. PUT the bytes to `upload_url` with exactly the returned `upload_headers`. Skip the upload when the response says `already_uploaded: true`. 3. `brainy_complete_media_upload` to verify the upload. 4. `brainy_create_post` with `action: "schedule"` and a future `scheduled_at`. ## OAuth details For client authors: an unauthenticated request returns `401` with ``` WWW-Authenticate: Bearer resource_metadata="https://www.brainyshorts.com/.well-known/oauth-protected-resource/api/mcp" ``` - Authorization server metadata: `/.well-known/oauth-authorization-server` - Registration: `POST /oauth/register`, public clients only (`token_endpoint_auth_method: none`) - Redirect URIs must be HTTPS, or HTTP on `localhost`, `127.0.0.1`, or `[::1]` - The authorize request must include `resource=https://www.brainyshorts.com/api/mcp` and an S256 `code_challenge` - Access tokens last 1 hour; refresh tokens last 30 days and rotate on every use --- # Platforms Source: https://docs.brainyshorts.com/platforms Connect accounts at [Dashboard → Social accounts](https://www.brainyshorts.com/dashboard/social-accounts). Each target in a post names a `social_account_id` and its `platform`. BrainyShorts checks these rules when you create the post, so a bad target fails with a `400` rather than failing silently later. ## Social platforms | Platform | `platform` | Media | Notes | | --- | --- | --- | --- | | TikTok | `tiktok` | Photo posts (carousel of images), video | Media must be on `storage.brainyshorts.com` | | Instagram | `instagram` | Single image, 2–10 image carousel, Reels | Professional (Business or Creator) accounts only | | YouTube | `youtube` | Video | Drafts only through the REST API and MCP | | LinkedIn | `linkedin` | Single image, multiple images, MP4 video | Personal profiles only | | X | `x` | Up to 4 images, or 1 MP4 video | X bills API access separately | ### TikTok - Photo posts pull images by URL, and TikTok only accepts URLs on a verified domain. Upload through [`/media/uploads`](/quickstart) and use the returned `public_url`. - TikTok rejects long photo titles, so BrainyShorts strips hashtags from the title and truncates it to 80 characters. Put hashtags in the caption. ### Instagram - Connect with Instagram Login. The account must be a Business or Creator account; no Facebook Page is needed. - Instagram processes media asynchronously, so a delivery can sit in `processing` for a minute or two before it becomes `posted`. ### YouTube - `publish` and `schedule` are rejected for YouTube targets in the REST API and MCP. Create a `draft`, or publish YouTube videos through a [scheduled run](/automations) with skill version `1.2.0` or later, which sends the bytes straight from your machine to Google. ### LinkedIn - Posts go to your personal profile. Company Page publishing needs LinkedIn's separate Community Management API approval and isn't supported. - Videos are capped at 32 MB. ### X - Captions are limited to 280 characters when any X target is present. - Up to 4 images (5 MB each) or 1 video (32 MB). - X charges for API access separately from BrainyShorts. ## Articles Long-form articles use `POST /api/cli/articles` or the MCP article tools. A single article can go to several destinations at once. | Destination | `platform` | Connection | Target `settings` | | --- | --- | --- | --- | | DEV.to | `devto` | API key | none | | Hashnode | `hashnode` | Personal access token | `publication_id` (required) | | Reddit | `reddit` | OAuth | `subreddit` (required), `flair_id`, `flair_text`, `nsfw`, `spoiler`, `send_replies` | | LinkedIn | `linkedin` | Existing LinkedIn connection | none | | X | `x` | Existing X connection | none | - **DEV.to** takes at most 4 tags. Get a key under **Settings → Extensions → DEV Community API Keys**. - **Hashnode** needs the publication ID from that publication's dashboard settings on every target. - **Reddit** takes a bare subreddit name (`programming`, not `r/programming`). Titles are limited to 300 characters. - **LinkedIn** has no public API for native articles. BrainyShorts publishes a post whose text is the article intro (up to 3,000 characters) with a link card to `canonical_url`, so set a canonical URL. - **X** publishes a native long-form X Article, not a thread. The account needs an X Premium subscription. An article counts as `published` once any destination succeeds, and `failed` only when all of them fail. Retrying resends only the failed destinations. --- # Quickstart Source: https://docs.brainyshorts.com/quickstart This walks through the REST API end to end: list accounts, upload a video, and schedule it. If you use Claude, Cursor, or ChatGPT, the [MCP server](/mcp) does the same thing without a key. Every request goes to `https://www.brainyshorts.com`. Don't use `api.brainyshorts.com`. 1. **Get a key and connect an account** Sign in at [brainyshorts.com](https://www.brainyshorts.com), connect a social account, and create a key at [Dashboard → API keys](https://www.brainyshorts.com/dashboard/api-keys). ```bash export BRAINY_API_KEY="ba_sk_..." export BRAINY="https://www.brainyshorts.com/api/cli" ``` 2. **List connected accounts** ```bash curl -s "$BRAINY/accounts" -H "Authorization: Bearer $BRAINY_API_KEY" ``` ```json { "accounts": [ { "id": "3f0c...", "platform": "instagram", "account_handle": "@example", "token_valid": true, "created_at": "..." } ] } ``` Keep the `id` and `platform` of each account you want to post to. 3. **Reserve an upload** BrainyShorts checks the file's hashes before it accepts it, so compute them first. ```bash FILE=clip.mp4 SIZE=$(wc -c < "$FILE" | tr -d ' ') SHA256=$(shasum -a 256 "$FILE" | cut -d' ' -f1) MD5=$(openssl md5 -r "$FILE" | cut -d' ' -f1) curl -s -X POST "$BRAINY/media/uploads" \ -H "Authorization: Bearer $BRAINY_API_KEY" \ -H "Content-Type: application/json" \ -d "{\"content_type\":\"video/mp4\",\"content_length\":$SIZE,\"content_sha256\":\"$SHA256\",\"content_md5\":\"$MD5\"}" \ > reservation.json ``` `content_type` is one of `image/jpeg`, `image/png`, `image/webp`, or `video/mp4`. The response holds an `asset_id`, a presigned `upload_url`, and the exact `upload_headers` to send. If the response says `"already_uploaded": true`, you uploaded this exact file before. Skip the next two steps and use `media.public_url`. 4. **Upload the bytes** Send every header from `upload_headers` exactly as returned. The URL is signed against them, so a missing or changed header fails. ```bash HEADERS=() while IFS= read -r h; do HEADERS+=(-H "$h"); done \ < <(jq -r '.upload_headers | to_entries[] | "\(.key): \(.value)"' reservation.json) curl -s -X PUT "$(jq -r .upload_url reservation.json)" "${HEADERS[@]}" --data-binary @"$FILE" ``` 5. **Complete the upload** ```bash curl -s -X PATCH "$BRAINY/media/uploads" \ -H "Authorization: Bearer $BRAINY_API_KEY" \ -H "Content-Type: application/json" \ -d "{\"asset_id\":\"$(jq -r .asset_id reservation.json)\"}" \ > media.json ``` The response has a `public_url` on `storage.brainyshorts.com`, a domain every supported platform accepts. 6. **Schedule the post** ```bash curl -s -X POST "$BRAINY/posts" \ -H "Authorization: Bearer $BRAINY_API_KEY" \ -H "Content-Type: application/json" \ -d @- <