DocsReference
OpenPromo REST API
The OpenPromo REST API: API keys, the response format and every route for projects, brand profiles, assets, ideas, creations, renders and usage.
Everything the dashboard does goes through this API, so scripts and your own tools can do the same. The MCP server wraps the same routes for AI agents. The base address is https://api.openpromo.gg/api; on a self-hosted install, use your own API address.
Authentication
Create an API key in Settings and usage under API keys. Keys start with opro_, are shown once and act as your account. Send one as a bearer token:
curl https://api.openpromo.gg/api/me \
-H "Authorization: Bearer $OPENPROMO_API_KEY"A few account settings can only be changed from the dashboard while signed in: your YepAPI key, the spending limit and creating or deleting API keys. With an API key, those routes answer 403 forbidden.
Responses
// Success
{ "ok": true, "data": { ... } }
// Success of a paid call: what it cost on your YepAPI key
{ "ok": true, "data": { ... }, "costUsd": 0.04 }
// Error
{ "ok": false, "error": { "code": "budget_reached", "message": "...", "details": ... } }Error codes are listed in Errors and troubleshooting. Request bodies are JSON unless noted; an invalid field answers 400 validation_error with the field in details.
From website to reel
# 1. Add a website. Reading it runs in the background (about a minute).
curl -X POST https://api.openpromo.gg/api/projects \
-H "Authorization: Bearer $OPENPROMO_API_KEY" -H "Content-Type: application/json" \
-d '{"url": "crumbandco.example"}'
# 2. Poll until brandStatus is "ready".
curl https://api.openpromo.gg/api/projects/PROJECT_ID -H "Authorization: Bearer $OPENPROMO_API_KEY"
# 3. Write a reel from a brief (one writing call).
curl -X POST https://api.openpromo.gg/api/projects/PROJECT_ID/creations \
-H "Authorization: Bearer $OPENPROMO_API_KEY" -H "Content-Type: application/json" \
-d '{"type": "reel", "brief": "Weekend offer: 20% off a first subscription"}'
# 4. Render it, then poll the render until status is "done".
curl -X POST https://api.openpromo.gg/api/creations/CREATION_ID/renders \
-H "Authorization: Bearer $OPENPROMO_API_KEY" -H "Content-Type: application/json" -d '{}'
curl https://api.openpromo.gg/api/renders/RENDER_ID -H "Authorization: Bearer $OPENPROMO_API_KEY"
# 5. Download an output file by its asset id.
curl -L -o reel.mp4 "https://api.openpromo.gg/api/assets/ASSET_ID/file?download=1" \
-H "Authorization: Bearer $OPENPROMO_API_KEY"Account and usage
| Route | What it does |
|---|---|
| GET /api/me | You, whether a YepAPI key is connected, your spending limit, and this install’s MCP and docs addresses. |
| GET /api/balance | Your remaining YepAPI balance, with this month’s spend, paid calls and limit. Free. |
| GET /api/usage | This month’s spend: total, by feature, by day, and the 50 latest calls. |
| GET /api/api-keys | Your API keys: name, prefix, last used. Never the key itself. |
| POST /api/api-keys | Create a key. Body: { "name": string }. Returns the full key once. Dashboard only. |
| DELETE /api/api-keys/:id | Revoke a key. Dashboard only. |
| PUT /api/settings/yepapi-key | Save your YepAPI key after checking it. Body: { "apiKey": string }. Dashboard only. |
| DELETE /api/settings/yepapi-key | Remove your YepAPI key. Dashboard only. |
| PUT /api/settings/budget | Set the monthly limit in dollars, or remove it. Body: { "monthlyBudgetUsd": number | null }. Dashboard only. |
Projects and brand profiles
| Route | What it does |
|---|---|
| GET /api/projects | Your projects with brand status, tagline, main colors and logo asset id. |
| POST /api/projects | Add a website and start reading it. Body: { "url": string, "name"?: string }. Needs a YepAPI key. Answers 201 with brandStatus: "ingesting". Costs about $0.05 – $0.15. |
| GET /api/projects/:id | The project with its full brand profile, read status and progress, and agent notes (context). |
| PATCH /api/projects/:id | Rename it or replace the agent notes. Body: { "name"?: string, "context"?: object }. |
| DELETE /api/projects/:id | Delete the project, everything in it and its files. |
| PUT /api/projects/:id/brand | Save an edited brand profile. Body: { "brand": BrandProfile }, the whole profile as read from the project. Changed fields are remembered and survive a refresh. |
| POST /api/projects/:id/brand/refresh | Read the website again. Body: { "reset"?: boolean }; reset: true also forgets your edits. 409 while a read is running. |
| GET /api/projects/:id/pages | The pages the profile was built from, with their text, for checking where a claim came from. |
Assets and files
| Route | What it does |
|---|---|
| GET /api/projects/:id/assets | The library. Query: role (comma-separated), creationId, limit (1–500, default 200). |
| POST /api/projects/:id/assets | Upload a file as multipart form data: field file, optional field role (logo, product, photo, screenshot, background, broll, music or voice; default photo). PNG, JPEG, WebP, SVG, GIF, MP4, WebM, MP3, WAV or M4A, up to 200 MB. |
| GET /api/assets/:id/file | The file itself. Supports Range requests for video seeking; add ?download=1 to download it with a file name. |
| PATCH /api/assets/:id | Rename it or change its role. Body: { "name"?: string, "role"?: string }. |
| DELETE /api/assets/:id | Delete the asset and its file. |
curl -X POST https://api.openpromo.gg/api/projects/PROJECT_ID/assets \
-H "Authorization: Bearer $OPENPROMO_API_KEY" \
-F "[email protected]" -F "role=product"Ideas
| Route | What it does |
|---|---|
| GET /api/projects/:id/ideas | Saved ideas, newest first. |
| POST /api/projects/:id/ideas | Write new ideas from the brand profile. Body: { "count"?: 1–20, "goal"?, "channel"?, "type"?, "notes"?: string }. Count defaults to 8. Answers 201 with the ideas and costUsd. About $0.02 – $0.08. |
| PATCH /api/ideas/:id | Edit an idea. Body: any of title, hook, body, caption, hashtags (array), status (new, used or archived) and plannedFor (YYYY-MM-DD or null). |
| DELETE /api/ideas/:id | Delete an idea. |
Goals: "launch", "promo", "education", "proof", "feature", "story". Channels: "instagram", "tiktok", "youtube", "linkedin", "facebook", "x".
Creations and renders
| Route | What it does |
|---|---|
| GET /api/projects/:id/creations | Creations with their latest render. Query: type. |
| POST /api/projects/:id/creations | Write a creation from an idea or a brief. Body: { "type", "format"?, "ideaId"?, "brief"?, "voice"?: boolean, "allowGenerated"?: boolean }. Give ideaId or brief. voice and allowGenerated default to true. Takes 10 to 30 seconds; answers 201 with the scenes, checks and costUsd. |
| GET /api/creations/:id | The creation with its scenes (spec), checks, caption and its last 20 renders. |
| PUT /api/creations/:id | Save edits. Body: { "spec"?, "title"?, "caption"?, "format"? }; send the whole spec as read. Checks run again. 409 while it is rendering. |
| DELETE /api/creations/:id | Delete the creation. |
| GET /api/creations/:id/preview | One scene as an HTML page, as the editor shows it. Query: scene (default 0), format. Free. |
| POST /api/creations/:id/renders | Start a render. Body: { "format"?, "quality"?: "draft" | "final" }. Answers 202. Refused while checks have errors or another render is running. Spends only on AI pieces that are not cached yet. |
| GET /api/renders/:id | Status and progress; when done, the output, poster and caption file asset ids. |
Types and the sizes each one can have:
| Type | Formats |
|---|---|
| image | square, portrait, story, link, x_header, linkedin_banner |
| carousel | square, portrait |
| reel | story |
| promo | landscape, square, story |
| tutorial | landscape, story |
All formats: "square", "portrait", "story", "landscape", "link", "x_header", "linkedin_banner".