Strategy

    Pin to endpoints, not agent tool names

    Agent tool names move. REST route groups do not. Build integrations against endpoints, and treat tool names as a convenience layer on top.

    Versely Team8 min read

    Versely's API keys do not know what generate_images is. Scope checks match the longest REST prefix on the path you actually called. POST /api/v1/generate/image needs generate. POST /api/v1/social/posts needs post. The agent tool name never enters the decision.

    That is the whole argument for pinning an integration to route groups instead of to the names an agent uses in chat. Tool names are a convenience layer on top of the HTTP surface. They are renamed, split, folded into sub-agents, and occasionally removed. The route groups those tools wrap are what the key, the rate limiter, and the poll loop all bind to.

    If you are choosing a door into the catalog at all, CLI, MCP, or the API is the decision. This post is the next one: once you have chosen HTTP, what do you actually hard-code.

    Route groups are the contract

    Every authenticated call hits https://api.versely.studio/api/v1 with a bearer key (vsk_ plus forty hex characters). The key carries an explicit list of scopes. Empty lists are rejected at creation. "all" expands to the eight named scopes and is stored as that list, so a scope added later does not silently widen a key you minted last quarter.

    The eight scopes, and the prefixes they own:

    Scope Prefixes it covers
    generate /generate, plus the other media producers (/movie, /dubbing, /ltx-batch, /suno, /audio, /avatar, /broll, …)
    post /social/posts, /social/preview
    manage_accounts /social/accounts, /social/auth-url
    slideshow /slideshow
    ugc /ugc, /captions
    workflows /workflows, /video-workflows, /workflow-assets, /templates, /public-workflows
    analytics /social-analytics, /trend-analysis, /trending-feed
    read /user, /ai-models, /agentic, /media, /fonts, /features

    Longest prefix wins. /social/posts is post, /social/accounts is manage_accounts, and those two are separate on purpose so a publisher key cannot connect or disconnect accounts.

    Two consequences follow for anyone writing a client:

    1. The path is the permission. A key with generate can call POST /generate/video and cannot call POST /social/posts. The same key talking to an agent is a different path: /agentic lives under read. Chat-shaped automation and HTTP-shaped generation are not interchangeable credentials.
    2. The durable identifier of a capability is the route group, not the helper that wraps it. POST /generate/image will still be the image call when the agent tool that wraps it is named something else.

    Rate limits follow the same grain. Generation sits behind a cost-sensitive limiter (30 requests per minute). Each key also has its own RPM, default 60, clamped between 1 and 1,000, returned as X-RateLimit-Limit, X-RateLimit-Remaining, and X-RateLimit-Reset. Social posting is 10 requests per minute per user. You back off against those headers. You do not back off against a tool name.

    Tool names are a convenience layer

    Inside chat, MCP, and the skill packs, the same catalog shows up as tools. There are 139 of them in the current agent-tool list. The orchestrator does not dump all 139 on every turn. It routes through six domain sub-agents (generation, slideshow, social, workflow, video_studio, web_editor), each exposed as a meta-tool (run_generation_agent, run_social_agent, and so on).

    That is a good interface for a person in a conversation. It is a bad interface to pin a production job to.

    A mapping you can actually ship against:

    What you mean Agent tool you might see HTTP to pin to
    Make an image generate_images POST /api/v1/generate/image
    Make a video generate_videos POST /api/v1/generate/video
    Poll the job check_generation_status GET /api/v1/status/:requestId
    Publish a post post_to_social_media POST /api/v1/social/posts
    List connected accounts get_social_accounts GET /api/v1/social/accounts
    Pull performance get_social_analytics GET /api/v1/social-analytics/overview
    Run a saved recipe run_workflow POST /api/v1/workflows/:id/run
    Pull frames from a clip extract_video_frames POST /api/v1/features/extract-frames
    Render an EDL edit_video POST /api/v1/features/editor-render

    The left column is intent. The middle column is what an agent happens to call this quarter. The right column is what you put in a client, a runbook, and a scope grant.

    MCP still matters for the interactive path. The connector lives at https://mcp.versely.studio/mcp, the MCP page is the setup, and npx skills add AI-XLabs-Innovation/versely-skills installs the eight skill packs (generate, slideshow, movie, ugc, music, social, analytics, content-pipeline) so the agent gets procedures rather than a raw tool list. The CLI is the local install path: versely auth login in a browser, then MCP wiring on that machine. Headless jobs still need a minted vsk_ key in the environment. None of that is a substitute for pinning the job to an HTTP path once a human is no longer in the loop. The developer page is the entry point for that path; the skills page is the entry point for the conversational one.

    Where the convenience layer diverges from the route

    Pinning to tools is not only brittle when names change. It is wrong when the tool and the route do not do the same thing.

    The clean example is workflow re-runs. The agent tool run_workflow accepts refresh. Omit it on a second run of a themed recipe and the plot is regenerated while characters, assets, style, aspect ratio, scene count, and per-scene durations stay put. POST /api/v1/workflows/:id/run instantiates the saved scenes and does not take that flag. A client that "just calls run_workflow" in an agent, and a client that posts to the workflows route, will disagree about what "run it again" means. The route is the one you can write a test against. The flag is a property of the tool.

    The other example is removal. generate_via_queue and check_queue_status were taken out on 2026-08-07. They were never declared on the live tool list, and they are not a general queue API. A blog post or a wrapper that still names them is pinned to a tool that does not exist. POST /generate/video plus GET /status/:requestId is the job that survived.

    Generations are poll-only. The call returns a request_id. Status is generating, completed, or failed, and a completed payload carries result_url. There is no customer-facing webhook to register, and no idempotency key to send. A durable client is a POST plus a poll loop against /status, not a tool call that "comes back when it is done."

    How to pin an integration so it still works next quarter

    1. Write the job as a path and a scope. "This worker may POST /social/posts with scope post." Not "this worker may call post_to_social_media."
    2. Mint a key that contains only that scope. Discover the catalog at GET /api/v1/auth/api-keys/scopes rather than hard-coding the eight names. Store the raw key once. Rotate by creating a new key, flipping the caller, then deleting the old keyId (soft revoke via revoked_at).
    3. Clamp rate_limit_rpm to the job. A publisher does not need the default 60. A generate worker that submits many one-shot POSTs does. A models array on a single /generate call is still one request. The clamp is 1 to 1,000.
    4. Treat MCP tool names as display. Log them, sure. Do not branch on them. If you must call the agent HTTP surface, you are on /agentic, which is a read grant, not a generate grant.
    5. Keep the poll loop on /status. Same for workflow runs: GET /api/v1/video-workflows/runs/:id (or the workflows run detail route) is the status document, not a chat message.

    Do that and a tool rename becomes a UI change. Do the opposite and a tool rename becomes an outage.

    The web app, the agent, and MCP remain the right surfaces for taste, iteration, and anything a person is watching. The surface comparison covers that split. Production traffic belongs on the route groups, because those are what the key already believes in.

    FAQ

    Are agent tool names stable enough to use in a client?

    No. They are renamed, grouped under sub-agents, and sometimes removed. The 2026-08-07 queue-tool removal is the working example: a name you might still see in an old write-up is not an endpoint you can call. Pin to /generate and /status.

    Do I need a generate-scoped key to talk to the agent?

    Not in the way the name suggests. /agentic is owned by the read scope. /generate is owned by generate. A headless image worker should call POST /generate/image with a generate key. A chat integration is a different grant on a different prefix. Mixing them is how a "generate key" fails in MCP and works in curl.

    What if I only use MCP and never touch REST?

    Then you are not pinning a production job to a tool name either. You are using a conversational surface whose setup lives on the MCP page. The moment a cron, a worker, or another product needs to generate without a person in the loop, switch that job to the route group and a scoped key. Keep MCP for the part a person is still steering.

    How do I see the current scope list without copying it from a blog post?

    GET /api/v1/auth/api-keys/scopes. It returns the catalog, the longest-prefix match strategy, and the "all" sentinel. That response is the source of truth. This post is a map, not a substitute for it.