Mapping the API surface by route group
A working map of Versely REST by route group: generate, status, social, workflows, analytics, and where each capability actually lives.
There is no public API reference site on versely.studio. The OpenAPI spec exists internally and is not served in production, so "go read /docs" is not a plan. What you can hold in your head is smaller and more useful: the REST surface is a set of mounted route groups under /api/v1, each group owns one kind of work, and a scoped key is allowed to call the prefixes it was minted for.
Once you know which group a capability lives in, you stop hunting through agent tool names. This is a map of the groups you actually automate against, in the order a working client hits them.
How a call is identified
Base URL: https://api.versely.studio/api/v1.
Two auth paths land on the same handlers. A Supabase JWT from the app, or a Versely API key in the Authorization header. Keys are vsk_ plus forty hex characters (160 bits), stored as a SHA-256 hash, with the raw value returned once at creation. You mint them at POST /auth/api-keys. Those management routes accept a JWT or an existing key; the raw secret is still shown only at creation. GET /auth/api-keys/scopes returns the catalog. DELETE /auth/api-keys/:keyId soft-revokes.
Every POST, PUT, and PATCH body is run through content moderation on text fields before the handler sees it. If a prompt is rejected, that is the moderation layer, not a model failure.
There is no customer-facing webhook. A generation returns a request_id. You poll GET /status/:requestId until the payload says generating, completed, or failed. Completed responses carry result_url, plus type and model when they are present. Failed jobs surface a reason, classified as transient or permanent. Retry the first. Do not retry the second as-is.
The developer page is the front door. CLI versus MCP versus the API is the decision about whether you should be on this surface at all. The rest of this post assumes you should.
Generate, then status
/generate is the media producer. It is the group a generate-scoped key is for. All of these sit behind auth and a cost-sensitive limiter (30 requests per minute). Generation POSTs consume credits from the same balance as the app. Listing models is not a generation.
| Method | Path | Job |
|---|---|---|
POST |
/generate/image |
Text-to-image, image-to-image, edits. model may be a string or an array; one request can fan out across named models. |
POST |
/generate/video |
Text-to-video, image-to-video, frame-to-frame. |
POST |
/generate/lipsync |
Image plus audio in, talking clip out. |
POST |
/generate/audio |
Text-to-speech. |
POST |
/generate/story |
Multi-scene storyboard generation. |
POST |
/generate/background-removal |
Still cutout. |
POST |
/generate/image-upscale |
Image upscale. |
POST |
/generate/video-upscale |
Video upscale. |
POST |
/generate/expand-scene |
Expand a scene description. |
GET |
/generate/models |
Models this group will accept. |
GET |
/generate/models/background-removal |
Cutout model list. |
GET |
/generate/models/image-upscale |
Image-upscale model list. |
GET |
/generate/models/video-upscale |
Video-upscale model list. |
GET |
/generate/story-models |
Storyboard model list. |
GET |
/generate/provider-check |
Provider health check. |
GET /status/:requestId is the other half of every call in that table. It is a different group on purpose: generate starts work, status reads it. A client that only POSTs and never polls will look broken while the job is fine.
Related producers live next door, under the same generate scope: /movie, /dubbing, /ltx-batch (including hook packs), /broll, /avatar, /suno, /audio. If the job is "make new media," it is in this family. If the job is "stitch media I already have," it is /features.
The web equivalent of /generate/video is the AI video generator. Use the tool when a person is picking a model. Use the route when a worker is.
Features, editor, media
/features is post-production on URLs you already host. It sits under the read scope, which surprises people who expect "read" to mean "only GET." The group is named for the product surface it backs, not for HTTP verbs.
| Method | Path | Job |
|---|---|---|
POST |
/features/editor-render |
One EDL (clips, transitions, texts, music, voiceover, overlay, captions) in, one video out. |
POST |
/features/extract-frames |
Pull stills from a clip. |
POST |
/features/merge-videos |
Concatenate clips. |
POST |
/features/attach-audio |
Mux audio onto a video. |
POST |
/features/add-captions |
Burn captions in. |
POST |
/features/audio-isolation |
Isolate vocals. |
POST |
/features/colorize-photo |
Colorize a still. |
POST |
/features/generate-prompt |
Prompt helper. |
POST |
/features/generate-caption |
Caption helper. |
POST |
/features/preview-rendition |
Small feed-preview derivative of a master. |
editor-render is the one that needs a careful reading. Pass preview: true and you get a 480p pass at no credit charge, with a short per-user cooldown (five seconds; a cooldown hit returns retry_after_ms). Preview is also capped on clip count and on duration (180 seconds). The final export, preview omitted or false, is charged once regardless of how many clips are on the EDL. That is the only free path on the whole platform, and it is a preview, not a plan. Iterate against it, wait out the cooldown, then export once. The same pipeline is what the video editor runs in the app.
/media is the library and /fonts is the caption font list; both are read. /editor-projects is saved editor documents. That prefix is not in the scope catalog, so scope matching does not apply to it — any authenticated key currently reaches it. Do not treat that as a designed read grant. There is no cursor-based pagination documented on the library reads; do not invent one. If you need a file the generate group will accept, it has to be an HTTPS URL. file:// is rejected.
Social, analytics, workflows
These three groups are how a finished asset leaves the building, how you see whether it worked, and how you repeat the recipe.
/social splits across two scopes on purpose. Account connection (GET /social/auth-url, GET /social/accounts, POST /social/accounts/refresh, DELETE /social/accounts/:accountId) is manage_accounts. Publishing (POST /social/posts, POST /social/preview, GET /social/posts, GET /social/posts/:postId, DELETE /social/posts/:postId) is post. A posting key should not be allowed to connect or disconnect accounts. Posting is 10 requests per minute per user; account refresh is 5. GET /social/posts takes limit and offset, default 20.
/social-analytics is analytics. Four routes: GET /social-analytics/overview, GET /social-analytics/:postId, POST /social-analytics/:postId/collect, GET /social-analytics/:postId/history. Live pulls cover TikTok, Instagram, YouTube, X, Facebook, and LinkedIn. Adjacent groups in the same scope: /trend-analysis and /trending-feed.
/workflows and /video-workflows are workflows. Saved recipes live under /workflows (GET /, GET /:id, POST /:id/run, PATCH /:id, mode and schedule patches, assets, duplicate, export, delete). Run documents and scene-level retry live under /video-workflows (GET /runs, GET /runs/:id, POST /runs/:id/cancel, POST /runs/:id/combine, POST /runs/:id/scenes/:order/retry). Public templates are /public-workflows. /templates is the one-tap template runner. The app-side version of this group is the workflows library and the agent workflow tools.
/slideshow and /ugc (plus /captions) are their own scopes. If the job is a carousel or a UGC overlay, do not stuff it through /generate/video just because you already have a generate key.
/agentic is the chat runtime: /chat, /chat/stream, /chat/answer-now, /quick-generate, conversations, tasks, plan execute/reject, brand-kit, memories. It is a read prefix. It is also 60 requests per minute per user. Use it when a person or an MCP client is driving. Do not use it as a hidden generate endpoint for a worker; the worker belongs on /generate.
/billing is the credit ledger the pricing page describes on the human side. API access draws on the same balance as the app. There is no API-only plan and no free API allowance.
What this map is not
A few things people bring from other vendors that are not route groups here:
- Outbound webhooks.
/webhook/*is inbound from providers. You poll/status. - A published SDK.
@versely/cliinstalls and authenticates. It is not a client library, and there is noversely generate. - A general queue API.
/ltx-batch/queue/statsis LTX-batch-specific. - Cursor pagination on library reads. List endpoints that take
limitandoffsetsay so. The others do not. - Per-key spend caps. Keys can store a
credit_limitfield. It is not enforced. Cap spend in your dispatcher if you need a ceiling.
Admin, gamification, email, referrals, and inbound webhooks are not part of a content-worker client. Hold the groups; they are what the key already matches on.
FAQ
Where do I look up which models a generate call will accept?
GET /api/v1/generate/models, plus the specialist lists for background removal, image upscale, video upscale, and story. /ai-models is the broader registry under read. Do not scrape the website catalog and hope the slugs match; ask the group you are about to POST to.
Is /features/editor-render a generate call?
No. It composites URLs you already have. The 480p preview pass with preview: true is free and carries a short per-user cooldown of five seconds. The final export is a single credit charge. New clips still come from /generate (or /movie, /ugc, and the rest of the producer family).
Why is /agentic not under the generate scope?
Because it is the chat runtime, not the media producer. A worker that only needs images should never see /agentic. A chat integration that needs to talk to the agent should be granted read, and should still not be the thing that publishes. Split those jobs across keys the same way the prefixes are split.
Can I register a URL and get a POST when a job finishes?
No. Poll GET /status/:requestId. That is the completion surface. Designing a client around a webhook you supply will not work, because that callback does not exist.