← Back to appExpand all

API Reference

REST API for the AI SNS · Social Studio publishing platform. All responses are JSON unless noted. This page is generated from the server source and reflects the deployed build.

… endpoints Base URL / · port 7860 Auth Bearer JWT Format JSON

Auth & conventions

How authentication, roles, plan limits and credits work across every route.

Register / login

Registering creates a user and a workspace — you become the owner — and returns a token together with the workspace id. Logging in returns the same pair. The token is a self-signed HS256 JWT valid for 30 days. There is no refresh endpoint, so sign in again once it expires.

POST /api/auth/register   {email, password, workspace?}    → {token, workspace_id}
POST /api/auth/login      {email, password, workspace_id?}  → {token, workspace_id}
JWT payload               {uid, ws, email}
Making authenticated calls
curl -H "Authorization: Bearer <token>" \
     https://<host>/api/posts

Every route below /api passes through requireAuth, which resolves your workspace, user id and role. Authentication is resolved in this order:

  1. a legacy API_TOKEN environment match, treated as the owner of workspace 1;
  2. a workspace API key (sk_studio_…, minted in Settings → API keys), acting as an editor of the one workspace it belongs to;
  3. a valid JWT, with workspace membership checked;
  4. open development or demo mode.
Open demo mode. When API_TOKEN is unset and OPEN_MODE is not set to "0", unauthenticated calls to /api are accepted as the owner of the default workspace. Set OPEN_MODE=0, or provide an API_TOKEN, to require real authentication in production.
Roles

AUTH routes accept any valid session. WRITE routes additionally require write permission, which only the owner and editor roles hold — a viewer or client is refused with 403 and a read-only role error. ADMIN routes need the x-admin-secret header and CRON routes need x-cron-secret. PUBLIC routes need no bearer token at all, because the token or id in the path is itself the credential.

OWNER routes are the seat and team decisions, and a session that is not a workspace owner is refused with 403 whatever its write permission.

PUBLIC AUTH WRITE ADMIN CRON OWNER
Plan limits — HTTP 402

Some writes check the workspace plan limit for posts, AI units and connected accounts. When a limit is exceeded the route responds:

402 { "error":"limit_reached", "kind":"posts|ai_units|accounts", "limit":30, "upgrade":true }

Monthly allowances per plan:

free       posts 30     ai_units 50     accounts 1
starter    posts 200    ai_units 500    accounts 3
pro        posts 1000   ai_units 1500   accounts 5
business   posts ∞      ai_units 3000   accounts 15
Credits

Credits are a separate ledger. AI actions spend them according to a cost map: a draft or a set of hashtags costs one credit, an image or an image reference costs twelve, a frontier video costs 120 and an audio clip costs eight. Credits are soft by default — usage is tracked but never blocked — unless credit enforcement is switched on. Only the influencer analysis route hard-checks credits and can refuse the request.

lib/billing.js · lib/plans.js
ENFORCE_CREDITS=1              → credits become blocking
POST /api/influencers/analyze  → 402 out_of_credits

AI Studio job lifecycle

How a generation moves from submit to a saved file, and when credits are charged, skipped or refunded.

Submit, poll, save

A generation is submitted once, polled until it reaches a terminal status, and then copied into the media library so it can be attached to a post before the platform's signed url expires.

GET  /api/hf/catalog                → pick a model id + its declared settings and media roles
POST /api/hf/generate               → 200 {job:{id, status:"queued", ...}, credits}
GET  /api/hf/jobs/:id  (repeat)     → 200 {job:{status, results:[...], error}}
                                       non-terminal: keep polling (the studio UI polls every 2.5 s)
                                       terminal:     completed | failed | nsfw
POST /api/hf/jobs/:id/save          → 200 {ok:true, media:{id, key, url}}   (completed jobs only)
POST /api/hf/jobs/:id/cancel        → stops a job that has not finished; it ends as failed
Job object
{id, request_id, model, surface:"image"|"video"|"audio", prompt, status, error:<string|null>,
 settings:{...as submitted}, media:{...as submitted}, results:[{type:"image"|"video"|"audio", url}],
 created_at, updated_at}
// A finished job answers from the server's own row, so polling a completed job never calls the platform again.
// nsfw  → error "The model's safety filter rejected this generation."
// failed→ error is the platform's message, or "The generation failed."
Credits and passes

Credits are charged when the job is submitted, at a fixed price per surface, and the charge is skipped entirely when an active model pass covers the model.

hf_image  12 credits   hf_video  120 credits   hf_audio  8 credits     (lib/billing.js COST)
pass covers the model when:  scope == "all"  |  scope == model id  |  scope empty and surface matches
402 {error:"Not enough credits for this generation.", upgrade:true}   only when ENFORCE_CREDITS=1 and no pass applies

A job that ends as failed or nsfw, or that is cancelled before it finishes, is refunded in full unless a pass made it free in the first place, so a pass-covered failure never mints credits.

Usage is metered on every generation whether or not it was billed, and the job is recorded before the charge is taken, so a client can never pay for a generation that has no row to poll.

Audio is synchronous. The three audio models (speech, sound-effect, music) run on the studio's own ElevenLabs integration through POST /api/hf/audio and return a completed job at once, so there is nothing to poll. They still land in the same jobs gallery.

API keys, MCP & CLI

How a client outside the browser authenticates, and how the MCP server and the command line map onto the routes above.

Workspace API keys

A key is minted in the studio under Settings → API keys and is shown exactly once; only a salted hash is stored, so it cannot be recovered or listed again.

It belongs to one workspace and acts there as an editor: it can generate, poll and read, but it cannot change billing, seats or keys, and the key routes refuse a caller that is itself a key.

A revoked key is refused as unknown, and a key whose workspace owner or minter is suspended is refused with 403, on the same check the JWT path makes on every request.

Authorization: Bearer sk_studio_xxxx        → req.ws = the key's workspace, role editor, actor "api-key:<prefix>"
401 {error:"invalid api key"}               unknown, malformed or revoked
403 {error:"account suspended"}             the account behind it is suspended
MCP server — POST /mcp

The endpoint speaks the Model Context Protocol over streamable HTTP: one JSON-RPC 2.0 message per POST, answered as JSON, with notifications acknowledged by 202 and no server-initiated stream.

Every tool is a thin call into the same code the routes above run, with the workspace the key resolved to, so a generation made through MCP is priced, checked, charged and refunded exactly as one made in the studio.

initialize          → {protocolVersion, capabilities:{tools:{}}, serverInfo, instructions}
tools/list          → list_models · list_effects · quote_generation · generate_image · generate_video · job_status · account_balance
tools/call          → {content:[{type:"text", text}], structuredContent, isError}
                      a refused call (no credits, other tenant's job, bad model) is a result with isError:true and the route's own message
-32700 Parse error  -32600 Invalid Request  -32601 Method not found  -32602 Unknown tool       (HTTP 400 for the first two)
GET / DELETE /mcp   → 405
429                 → more than 120 calls a minute on one key
Command line — studio

The CLI is a single dependency-free file served by the studio; it reads the key from STUDIO_API_KEY or ~/.studio/config.json, quotes the price before a generation and asks unless --yes is given.

curl -fsSL https://studio.ai-sns.io/cli/studio.js -o /usr/local/bin/studio && chmod +x /usr/local/bin/studio
studio login · studio models · studio effects · studio balance
studio generate --model <id> --prompt "…" [--duration N] [--resolution R] [--aspect A] [--out file] [--yes]
studio jobs <id> [--wait] [--out file]
maps onto: POST /api/hf/quote → POST /api/hf/generate → GET /api/hf/jobs/:id   (GET /api/hf/models, /api/hf/presets, /api/billing)
Nothing is decided by the client. The CLI and the MCP tools never send a price, a workspace id or a job they do not own: the server sanitises the settings, prices them, checks the balance or the pass, charges, and answers with what it did.
Generated from the server source (server.js and lib/*.js). Auth badges: PUBLIC AUTH WRITE ADMIN CRON OWNER