Skip to content

The management API

There is no bearer-key HTTP API for managing agents, keys or usage. A secret key (sk_…) starts calls; it does not create agents, mint keys or read your account. The programmatic management surface is the MCP server, and the interactive one is the console.

That is worth saying plainly, because it is the opposite of what most products do, and because you can waste an afternoon looking for the endpoint that would have done it.

Exactly one route accepts a key rather than a console session, and it does one thing: start a call.

POST https://app.voqalize.com/api/v1/sessions.connect
Authorization: Bearer sk_…
Content-Type: application/json

It answers with the pipecat transport’s argument and nothing else — webrtc_request_params and session_id. Relay that body to the browser verbatim: it is what startBotAndConnect takes, so there is no field to pick out and no object to rebuild.

There was once a create_meeting then start_meeting pair. It is gone: nothing ever created a connection and then declined to use it, and the gap between the two steps was a state nobody guarded. Start one call. There is no second step.

The route was called sessions.create until 2026-08-26. That name is gone, not aliased: it answers 404. If you find it in an older example, change the word — the body and the credential are unchanged, and the response is the connect params rather than the whole session record every caller had to dig through for the three fields it wanted. If you want the record too, read it back with get_session — over MCP or in the console, where reads belong.

One required field, and four optional ones. It is strict: an undeclared top-level key is a 422 naming it, rather than a session that quietly runs on defaults.

Field
agent_idrequiredWhich agent answers. The key is scoped to it.
initoptionalThe opaque blob your brain reads as session.init. Same word at every hop. Send identifiers, not personal data — it is stored on the session record.
configoptionalThis call’s tts, stt and idle, plus record. Parsed as canonical proto3 JSON, so a bad field or an unservable language is a 400 naming it — see error codes.
display_nameoptionalA label for your own console and lists.
metadataoptionalYour own flat string map for correlating a call with your systems. At most 10 keys, each value at most 256 characters.

Which key signs the request, how a publishable key differs, and what the browser does with the response are all on connect a browser — that page owns the handshake and this one does not repeat it.

Creating an agent, pointing its brain_url at your route, minting and revoking keys, listing sessions, reading logs and usage — every one of those is an MCP tool, authenticated over OAuth, rate limited at 300 requests per minute per workspace. The MCP server is the reference for all of them.

This is not a workaround. The audience for a management API is a developer and the developer’s coding agent, and MCP is the surface an agent already holds: the tools describe themselves, the auth is interactive and revocable, and there is no long-lived credential sitting in an environment variable to leak.

The obvious design is a tenant-scoped bearer key. We had one, called ak_, and removed it on 2026-08-12.

Every key now names an agent, of every kind. ak_ did not, which meant a credential could start a session for any agent in the tenant — so a key baked into one public demo page could spend on every other agent that tenant owned. Nothing was narrowing it. A credential that names no resource is one whose blast radius can only be discovered by reading code.

Reintroducing a bearer key for management reintroduces exactly that: the operations a management API is for — create an agent, mint a key — cannot be authorized by a credential issued by one agent, so such a key has to be tenant-wide by construction. It would be the same key, with the same reach, back under a different name.

The OpenAPI document is generated by the service and served in development only, for the same reason: publishing a schema for routes whose only credential is a console cookie describes a surface nobody can call.

This is a decision about defaults, not a refusal. If your integration genuinely cannot hold an OAuth client — a CI job that provisions agents, a product creating tenants on behalf of its own customers, a stack where MCP is not available — HTTP access can be enabled for your account. Ask us, and tell us which operations you need; the narrower the answer, the faster it is.

What we will not do is hand every account a tenant-wide key by default and call it convenience.