API documentation
Last updated August 15, 2026
Introduction
The Brandefer API lets your systems do what your team does in the app: list a project's connected social accounts, upload media, and create posts that go out as drafts, on a schedule, or immediately. Platform-specific mechanics such as Instagram container processing, TikTok upload flows, LinkedIn asset uploads and token refresh are handled by Brandefer; the API accepts text and media and reports per-channel results.
All requests and responses use JSON, except media uploads, which take the file bytes as the request body. The base URL is:
https://brandefer.com/api/v1
Authentication
Every request carries an API key in the Authorization header:
Authorization: Bearer bfr_YOUR_KEY
Keys are created in the app and shown once, at creation. There are two scopes:
| Key scope | Type | Description |
|---|---|---|
Project key | project settings | Created under a project's Settings, in the Agent access card. Reaches only that project: its channels, its media library, its posts. The projectId parameter is not needed and, if sent, must match. |
Organization key | organization settings | Created by an organization owner or admin. Reaches every project in the organization; each call must say which one via the projectId parameter. |
Each key carries a set of capabilities chosen at creation. A request that needs a capability the key does not have is refused with status 403.
| Capability | Type | Description |
|---|---|---|
read | List channels and read post status. | |
media | Upload files to the media library. | |
draft | Create and edit posts as drafts. | |
schedule | Schedule posts for a future time. | |
publish | Publish immediately (mode now). | |
comment | Read and reply to comments (MCP). | |
message | Read and reply to direct messages (MCP). |
Keys can be revoked at any time from the same card. A key also stops working automatically when the person who created it loses access to the project or organization.
Errors
Errors return a JSON body of the form { "error": "human-readable reason" } with a matching status code:
| Status | Type | Description |
|---|---|---|
401 | Missing, malformed, revoked or unknown API key. | |
403 | The key is valid but lacks the required capability. | |
404 | The object does not exist, or belongs to a project outside this key's scope. | |
413 | Media upload larger than 100 MB. | |
415 | Media Content-Type outside the allowed set. | |
422 | The request was understood but rejected: unknown channel or media ids, empty content, missing scheduledAt. |
Channels
A channel is a connected social account inside a project. Channel ids are the addresses that posts are sent to.
/api/v1/channels| Query parameter | Type | Description |
|---|---|---|
projectId | string, optional | Required for organization keys; project keys omit it. |
Requires the read capability. Returns:
{
"channels": [
{
"id": "5b2e…", // use this in channelIds when creating posts
"platform": "linkedin", // facebook · instagram · linkedin · x · tiktok · youtube
"name": "Acme Inc",
"username": "acme", // handle where the platform has one
"status": "active" // active · expired (needs reconnecting in the app)
}
]
}Media
Media is uploaded before it is referenced by a post. The request body is the file itself, sent with its real Content-Type; there is no multipart or base64 wrapping.
/api/v1/media| Query parameter | Type | Description |
|---|---|---|
filename | string, optional | Stored display name of the file. |
projectId | string, optional | Required for organization keys. |
Requires the media capability. Accepted types are image/jpeg, image/png, image/webp, image/gif and video/mp4, up to 100 MB.
curl -X POST "https://brandefer.com/api/v1/media?filename=hero.png" \ -H "Authorization: Bearer bfr_YOUR_KEY" \ -H "Content-Type: image/png" \ --data-binary @hero.png
Returns status 201:
{
"mediaIds": ["9f41…"], // pass through to a post's mediaIds
"url": "https://media.brandefer.com/…",
"mimeType": "image/png"
}Create a post
One request creates a post across any number of the project's channels. The shared text is used for every channel unless a per-channel override replaces it. Every selected channel must end up with text or at least one media attachment.
/api/v1/posts| Body field | Type | Description |
|---|---|---|
text | string | Shared text for every channel. |
channelIds | string[] | Channel ids from the channels endpoint. All must belong to the key's project. |
mode | string | draft, schedule or now. Draft needs the draft capability, schedule additionally needs schedule, now additionally needs publish. |
scheduledAt | string (ISO 8601) | Publish time. Required when mode is schedule, ignored otherwise. |
mediaIds | string[] | Media ids from the media endpoint. Unknown ids are rejected with 422. A single id may be sent as a plain string. |
overrides | object, optional | Map of channel id to replacement text for that channel. |
firstComments | object, optional | Map of channel id to a comment posted right after that channel publishes (Facebook, Instagram, X, LinkedIn). |
projectId | string, optional | Required for organization keys. |
curl -X POST https://brandefer.com/api/v1/posts \
-H "Authorization: Bearer bfr_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{
"text": "Shared text for every channel",
"channelIds": ["5b2e…"],
"mode": "schedule",
"scheduledAt": "2026-08-20T15:00:00+03:00",
"mediaIds": ["9f41…"]
}'Returns status 201:
{
"postId": "ecf1…",
"mode": "schedule",
"status": "scheduled" // draft · scheduled · publishing
}Publishing is asynchronous. When mode is now the request returns at once with status publishing while delivery happens in the background; poll the post endpoint below for the outcome. Multi-image posts become the appropriate format per network: a carousel on Instagram, a multi-image post on LinkedIn, an album on Facebook. A single video sent to Instagram publishes as a Reel.
Retrieve a post
Returns the post and the state of each channel it targets, including the live URL and the platform's own post id once a channel has published.
/api/v1/posts/{postId}Requires the read capability. Returns:
{
"postId": "ecf1…",
"status": "published", // draft · scheduled · publishing · published · partial · failed
"text": "…",
"scheduledAt": null,
"createdAt": "2026-08-14T21:45:13Z",
"channels": [
{
"channelId": "5b2e…",
"channel": "Acme Inc",
"platform": "linkedin",
"status": "published", // pending · publishing · published · failed
"scheduledAt": null,
"publishedAt": "2026-08-14T21:45:17Z",
"url": "https://www.linkedin.com/feed/update/…",
"externalPostId": "urn:li:share:…",
"error": null // short sanitized reason when a channel failed
}
]
}| Post status | Type | Description |
|---|---|---|
draft | Saved, nothing scheduled. | |
scheduled | Waiting for its publish time. | |
publishing | Delivery in progress right now. | |
published | Every channel delivered. | |
partial | Some channels delivered, some failed. | |
failed | No channel delivered. |
A practical polling loop after mode now: wait 10 to 15 seconds, fetch the post, and stop when its status is published, partial or failed. Image posts usually complete within seconds; video and Instagram carousels can take a minute or two.
AI agents (MCP)
Brandefer also runs a Model Context Protocol server for AI agents such as Claude, Claude Code and other MCP-capable clients. It uses the same API keys and capabilities and exposes a wider tool set: posting and per-channel rescheduling, media, per-post analytics, comment management and Instagram direct messages. Ready-made connect commands for Claude Code and Claude Desktop are shown next to every key you create, in the Agent access card of the project settings.