Developer documentation
Connect to Promeo
Manage your Projects and posts from scripts, integrations, and AI assistants with the REST API or Model Context Protocol (MCP).
REST API
Connect a script or application with JSON over HTTP.
https://promeo.ai/api/v1
MCP
Give a compatible AI assistant access to your Project context.
https://promeo.ai/mcp
Create an API key in your dashboard, choose its permissions, and connect through either interface. Read Projects and posts, edit Project profiles and editable posts, or delete saved records.
Get an API key
- Sign in to Promeo, verify your email, and complete Workspace setup.
- Open Manage → API keys in the dashboard and select Create API key.
- Give the integration a name, choose one Project or all Projects, and select its permissions. New keys start with read permissions only.
- Choose an expiry of 30 days, 90 days, or one year.
- Create the key and save its value in your integration's protected secret settings. The complete key is shown only once.
The dashboard lists each key's name, masked prefix, Project access, permissions, creation date, expiry, and last use. Last use is recorded at most once per minute. You can have up to 20 active keys. Selecting Revoke and confirming stops subsequent requests with that key; already accepted requests may finish. To rotate a key, create its replacement, update your integration, then revoke the old key.
A key scoped to All Projects also includes future Projects owned by the account. A key scoped to one Project cannot access another Project. Deleting that Project removes its scoped keys. Account deletion, a disabled account, or revoked email verification prevents API access.
Permissions
Scroll across to see all columns.
| Permission | Allowed action |
|---|---|
projects:read | List accessible Projects |
projects:update | Update Project profile fields |
projects:delete | Delete a Project, its content, and its connections |
posts:read | List recent posts in an accessible Project |
posts:update | Edit a draft or scheduled post's copy, tags, or timing |
posts:delete | Delete a Promeo post record and its stored media |
Permissions are independent. For example, an integration with posts:update
needs posts:read as well if it should inspect posts before editing them.
Grant delete permissions only to integrations that should remove saved data.
Deleting a Project includes its stored posts and assets even if the key does
not separately have posts:delete.
Authentication
Send your API key in the same header for REST and MCP:
Authorization: Bearer <promeo-api-key>The examples use PROMEO_API_KEY, supplied through your client's protected
credential facility. Never put a real key in source control, logs, URLs,
shared configuration files, or support messages. Promeo stores only a hash of
the key, so a lost value cannot be recovered; revoke it and create another.
Verified Firebase ID tokens issued for Promeo's Firebase project
(promeo-e55d3) remain supported in the same Bearer header. They expire and
must be refreshed through Firebase Authentication. Firebase sessions have the
account's full integration permissions and share the account's request limits.
For unattended integrations, use a scoped dashboard API key.
The account must retain a verified email and current business Terms acceptance.
The OpenAPI document and MCP initialize handshake are public. Data requests,
MCP tool discovery, and tool execution require authentication.
Rate limits
REST and MCP use shared limits. Switching interfaces, creating more keys, or refreshing a Firebase ID token does not reset the account's allowance.
Scroll across to see all columns.
| Limit | Allowance |
|---|---|
| Each API key | 60 requests per minute; 5,000 per day |
| Account, across all keys and Firebase sessions | 120 requests per minute; 10,000 per day |
| Update and delete attempts, per account | 20 per minute; 200 per day |
| Project profile saves, shared with the dashboard | 50 per day |
| Draft saves, shared with the dashboard | 60 per day |
| Scheduled post edits, shared with dashboard scheduling | 30 per day |
| Post deletions, shared with the dashboard | 100 per day |
| API/MCP Project deletions | 20 per day |
Account/key windows start with the first request after the previous window expires. Existing dashboard operation limits use fixed UTC time buckets. Authenticated requests count even when their route input or permissions are rejected. Requests rejected at an earlier limit do not consume later quotas. MCP tool discovery and authenticated pings also count. Requests are limited to 256 KiB; MCP accepts one JSON-RPC message per request, with no batches.
A 429 response includes Retry-After, the number of seconds to wait.
This applies to both REST errors and MCP JSON-RPC errors. Wait at least that
long and add backoff; do not immediately retry in a loop.
Promeo also applies shared pre-authentication limits to both surfaces, including public discovery: 1,200 requests per minute overall and 600 per minute per transport peer. Reverse proxies can share a peer allowance; these are conservative service protection limits, not individual customer quotas. They do not trust caller-supplied forwarding headers. These controls run in the application and are not a separately deployed network DDoS protection service.
REST API
Responses use JSON. The X-Promeo-API-Version response header is 2026-09-07.
The OpenAPI specification describes every
route, request body, and required permission. Encode path segments with your
HTTP client's URL library.
List Projects
curl --fail-with-body \
--header "Authorization: Bearer $PROMEO_API_KEY" \
https://promeo.ai/api/v1/projectsGET /api/v1/projects requires projects:read. Returns up to 100 accessible
Projects, newest first. A Project-scoped key sees only its selected Project.
{
"projects": [{
"id": "AbCdEfGhIjKlMnOpQrSt",
"name": "Example Project",
"description": "Short-form product education",
"audience": "Independent retailers",
"features": "Product demonstrations and customer stories",
"voice": "Clear, practical, and warm",
"createdAt": "2026-08-20T09:30:00.000Z",
"updatedAt": "2026-09-07T14:10:00.000Z"
}]
}Update a Project
PATCH /api/v1/projects/{projectId} requires projects:update. Include only
fields you want to change; omitted fields are preserved. Supported fields are
name (1–100 characters) and description, audience, features, and voice
(1–20,000 characters each). The resulting profile must have all its context
fields completed, just as when saving Project settings in the dashboard.
curl --fail-with-body --request PATCH \
--header "Authorization: Bearer $PROMEO_API_KEY" \
--header 'Content-Type: application/json' \
--data '{"name":"Updated Project","voice":"Friendly and concise"}' \
https://promeo.ai/api/v1/projects/AbCdEfGhIjKlMnOpQrStReturns { "project": { ... } } using the Project shape above. A concurrent
change detected during the update returns 409; read the current Project
before retrying.
Delete a Project
DELETE /api/v1/projects/{projectId} requires projects:delete. This removes
the Project, its stored content, and its connected accounts from Promeo.
Pending scheduled, processing, or publishing posts must be cancelled or
finished first. Existing provider revocation and media cleanup run as they do
for dashboard deletion. Already published social content remains on its
platform.
curl --fail-with-body --request DELETE \
--header "Authorization: Bearer $PROMEO_API_KEY" \
https://promeo.ai/api/v1/projects/AbCdEfGhIjKlMnOpQrStReturns { "deleted": true }. Confirm the user's intent in your integration
before sending this request.
List recent Project posts
GET /api/v1/projects/{projectId}/posts requires posts:read. Returns the 50
most recent posts, ordered by scheduled time from newest to oldest.
curl --fail-with-body \
--header "Authorization: Bearer $PROMEO_API_KEY" \
https://promeo.ai/api/v1/projects/AbCdEfGhIjKlMnOpQrSt/postsThe result is { "posts": [...] }. Each post includes:
Scroll across to see all columns.
| Field | Meaning |
|---|---|
id | Post ID for update or delete requests |
status | draft, scheduled, processing, publishing, published, failed, or cancelled |
title, description, tags | Shared title, caption, and up to ten tags |
scheduledAt | ISO 8601 UTC time; an unscheduled draft returns an empty string |
destinations | Selected channel snapshots with platform, connection ID, and display name |
destinationOverrides | Optional per-destination copy, media, and settings |
destinationResults | Optional per-destination publishing results |
media, mediaCount | Saved media metadata, including public delivery URLs |
privacyLevel, allowComment, autoAddMusic | Saved publishing settings |
brandContentToggle, brandOrganicToggle, photoCoverIndex | Disclosure and cover settings |
awaitingApproval | Whether a scheduled post still needs approval |
publishStatus, failReason | Optional processing details |
automationId, automationPostType | Optional automation provenance |
Update a post
PATCH /api/v1/projects/{projectId}/posts/{postId} requires posts:update.
Only drafts and scheduled posts can be edited. Include at least one editable
field and approved: true to confirm review of the exact changes.
Scroll across to see all columns.
| Editable field | Behavior |
|---|---|
title | Shared title, at most 90 characters |
description | Shared caption, at most 4,000 characters; destination limits still apply |
tags | Replace tags with up to ten values, each at most 32 user-perceived characters; [] clears them |
scheduledAt | Change an existing schedule, or a draft's proposed time; "" clears a draft's proposed time |
timezone | Timezone saved with the post |
Omitted fields, media, destinations, destination-specific overrides, and publishing settings are preserved. Shared copy changes do not replace existing destination-specific copy. A draft stays a draft; this endpoint cannot create a post, schedule a draft, change channels/media, generate content, or publish immediately. Scheduled saves must meet the existing scheduling and destination validation rules; ordinarily the time must be between two minutes and 90 days from now. Editing a generated draft detaches it from its automation, matching the dashboard.
For a post with a TikTok destination, also send musicUsageConfirmed: true
after the user accepts TikTok's Music Usage Confirmation. When applicable,
include brandedContentPolicyConfirmed: true after explicit acceptance.
Promeo never invents these confirmations for an integration.
curl --fail-with-body --request PATCH \
--header "Authorization: Bearer $PROMEO_API_KEY" \
--header 'Content-Type: application/json' \
--data '{"description":"Updated caption","tags":["launch"],"approved":true}' \
https://promeo.ai/api/v1/projects/AbCdEfGhIjKlMnOpQrSt/posts/PostDocumentIdReturns { "postId": "PostDocumentId", "status": "draft" } or the same
shape with "scheduled". A publishing claim or concurrent edit returns 409;
read the current post before retrying.
Delete a post
DELETE /api/v1/projects/{projectId}/posts/{postId} requires posts:delete.
It removes the Promeo post record and stored media. Processing or publishing
posts return 409. Deleting a published record does not delete the content
from its social platform.
curl --fail-with-body --request DELETE \
--header "Authorization: Bearer $PROMEO_API_KEY" \
https://promeo.ai/api/v1/projects/AbCdEfGhIjKlMnOpQrSt/posts/PostDocumentIdReturns { "deleted": true }. Confirm the user's intent before calling. A
repeat delete after successful removal returns 404.
Errors and status codes
{
"error": {
"code": "permission-denied",
"message": "This API key needs the posts:update permission."
}
}Scroll across to see all columns.
| HTTP status | Meaning and recovery |
|---|---|
400 | Invalid input, unknown fields, malformed ID, or excessive request size |
401 | Missing, invalid, expired, or revoked credential; use a valid replacement |
403 | Missing key permission or unsupported browser origin |
404 | Route or accessible resource was not found |
405 | Unsupported method; inspect Allow |
409 | Resolve account setup, required approval, resource state, or a concurrent edit |
415 | Send a JSON request with Content-Type: application/json |
429 | Wait for Retry-After, then retry with backoff |
500 | Unexpected failure; keep the safe reference code when contacting support |
Do not automatically replay updates or deletes after a timeout or server error: read the current resource first because the original request may have succeeded. Use bounded backoff for transient failures.
MCP
Promeo provides stateless Streamable HTTP at https://promeo.ai/mcp using
protocol version 2025-06-18. Send one JSON-RPC 2.0 message per HTTP POST.
There are no session IDs, batches, or SSE stream; GET returns 405.
Use a client that supports a custom Authorization header. Configuration names
vary between clients; a common shape is:
{
"mcpServers": {
"promeo": {
"type": "http",
"url": "https://promeo.ai/mcp",
"headers": {"Authorization": "Bearer ${PROMEO_API_KEY}"}
}
}
}Use your client's secret facility if it does not support environment-variable interpolation. Never commit a literal API key to the configuration.
Initialize
curl --fail-with-body https://promeo.ai/mcp \
--header 'Content-Type: application/json' \
--data '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-06-18","capabilities":{},"clientInfo":{"name":"example-client","version":"1.0.0"}}}'Send notifications/initialized after the handshake if your client requires
it. Notifications receive 202 and never execute tools.
List tools
curl --fail-with-body https://promeo.ai/mcp \
--header "Authorization: Bearer $PROMEO_API_KEY" \
--header 'Content-Type: application/json' \
--data '{"jsonrpc":"2.0","id":2,"method":"tools/list","params":{}}'The catalog includes only tools allowed by the key's permissions:
Scroll across to see all columns.
| Tool | Arguments | Result |
|---|---|---|
list_projects | {} | { "projects": [...] } |
list_posts | { "projectId": "..." } | { "posts": [...] } |
update_project | { "projectId": "...", "changes": { ... } } | { "project": { ... } } |
delete_project | { "projectId": "..." } | { "deleted": true } |
update_post | { "projectId": "...", "postId": "...", "changes": { ... } } | { "postId": "...", "status": "draft" } or "scheduled" |
delete_post | { "projectId": "...", "postId": "..." } | { "deleted": true } |
changes accepts exactly the corresponding REST PATCH fields and follows the
same approval and state rules. Write tools advertise destructive annotations.
The client should request user confirmation for destructive operations.
Call a tool
curl --fail-with-body https://promeo.ai/mcp \
--header "Authorization: Bearer $PROMEO_API_KEY" \
--header 'Content-Type: application/json' \
--data '{"jsonrpc":"2.0","id":3,"method":"tools/call","params":{"name":"update_post","arguments":{"projectId":"AbCdEfGhIjKlMnOpQrSt","postId":"PostDocumentId","changes":{"description":"Updated caption","approved":true}}}}'Successful results provide both content containing JSON text and an object
in structuredContent. Validation, permission, and ownership errors return a
tool result with isError: true. Authentication failures and rate limits use
HTTP errors with a JSON-RPC error; limits always return HTTP 429 and
Retry-After. Protocol errors use -32600 (invalid request), -32601 (unknown
method), or -32602 (invalid parameters); unexpected failures use -32603.
Compatibility and support
- Ignore unknown response fields unless your integration needs them.
- Keep the advertised API and MCP versions in safe diagnostics.
- The integration surface does not offer post creation, media upload, generation, immediate publishing, OAuth client registration, webhooks, or pagination cursors. Use the dashboard for those existing workflows.
- Never expose API keys, Firebase tokens, channel credentials, or full user content in logs or support requests.
For unexpected failures, include the request path or MCP method, timestamp, HTTP status, advertised version, and any safe error reference. Contact Promeo support with those details.