Quickstart
Create an organization, publish an agent version, and run your first session — start to finish, from the terminal and one HTTP call.
Everything here happens in a terminal. By the end you will have an organization, a workspace, a published agent version, an API key, and a session that answers a message.
Install the CLI
curl --proto '=https' --tlsv1.2 -LsSf https://github.com/subako-ai/subako-cli/releases/latest/download/subako-cli-installer.sh | shThe installer puts subako in $XDG_BIN_HOME when you have one set, and ~/.local/bin otherwise. Check it:
subako --version
Set up an organization
Sign up
An organization is the billing and identity boundary. It starts on the Free plan with 500 SC to try things with — see Plans and billing for what the paid plans add and how to subscribe.
subako cloud signup --org acme --name "Acme, Inc."--org is the organization’s handle. It is what identifies the
organization: no two share one, it appears in its URLs, and it cannot be
changed once the organization exists. It is also the name you sign in
against — subako login --org acme in the next step, and every login
after that — so pick one you can live with, and keep it. --name is
only the display name, and that one you can change at any time.
This stores nothing locally. It prints the Core server your organization
lives on — https://api.us.cloud.subako.ai unless you asked for another
one — which is what you sign in against next.
Sign in
Signing in runs the OAuth device-authorization flow: the CLI prints a code
and opens your browser, and the credential lands in
~/.config/subako/credentials.json once you approve it.
subako login --org acmeUse --profile to store the login under a name other than the
organization handle — that is how you hold several organizations, or a
staging and a production login, side by side. --server points the login
at a Core server other than the default, https://api.us.cloud.subako.ai;
subako profile list prints the server each stored profile is bound to.
subako whoamiCreate a workspace
Every unit of work belongs to a workspace: agents, skills, vaults, API
keys, and the sessions they run. use records the selection in your
profile, so later commands need no --workspace flag.
subako workspace create --name production
subako workspace use production
subako workspace listPublish an agent
Pick a model provider
Subako calls the model on your behalf, so a version publishes against a provider. Every workspace already sees the platform providers — they are configured for you and need no key of your own, so start there.
subako model-provider listworkspace: production (01a0877c-03d9-7823-b997-f42147b8b607)
01a07f90-238c-7de3-b55e-6fe53de063cf type=platform format=openai_responses label=Subako description=(none)
model=crow label=Crow
model=hawk label=Hawk
model=sparrow label=SparrowA workspace that has created nothing of its own sees exactly this: the Subako platform provider and the three models it serves. That is enough to finish the quickstart — providers you create yourself are listed alongside it.
Keep two things from the listing: the provider id you will publish against, and one of the model ids indented under it. Nothing else tells you which model ids a provider accepts.
To run against your own account instead, create a provider of your own. The key is read from stdin — never a flag — so it stays out of your shell history and out of the process list.
subako model-provider create \
--format anthropic \
--display-name "Anthropic production" \
--base-url https://api.anthropic.com < key.txtsubako model-provider formats lists the formats this server accepts. An
agent version may only publish against a provider whose format matches the
one its model speaks.
Create the agent
An agent is just a name and an identity. It holds no configuration of its own — its versions do.
subako agent create --name support-triageIt prints the agent id, which agent publish takes as its argument.
subako agent list shows it again later.
Write a version config
agent config-example prints the smallest configuration that publishes as it
stands. It needs no server, so you can start editing before anything else
exists.
Pass the format of the provider you picked, since the example differs between them.
subako agent config-example --format openai_responses > agent.json{
"system_prompt": "You are a helpful assistant.",
"model": {
"format": "openai_responses",
"model": "your-model-id",
"max_tokens": 8192,
"context_window": 128000
}
}Replace your-model-id with a model id from the provider’s listing —
hawk, say. The placeholder publishes happily and then fails at run time,
so this edit is not optional.
Grants — MCP servers and skills — are optional and go in this same
document. The provider is named by --model-provider at publish time, not
in the file.
Publish it
subako agent publish <agent-id> \
--config agent.json \
--model-provider <provider-id>The version is immutable. Publishing again makes a new one, and new sessions bind the newest live version; sessions already running stay on the version they bound.
Run a session
The rest is HTTP, against the same Core server you signed in to — the default,
https://api.us.cloud.subako.ai, in the calls below. If you signed in against
another one, subako profile list prints the server each profile is bound to.
Mint an API key
An API key is the machine credential your product runs under. Its secret is printed once and never again.
subako api-key mint \
--label "support-triage backend" \
--permission session.create \
--permission session.read \
--permission session.manageminted api key support-triage backend (01a0877e-…) in workspace production (01a0877c-…)
permissions: session.create, session.read, session.manage
expires: (never)
prefix: sbk_ak_4f50c2aa
the secret below is shown once and cannot be retrieved again:
sbk_ak_4f50c2aa8XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXCopy that last line — the whole sbk_ak_… secret, not the prefix, which
is only a label for recognizing the key in api-key list. The calls below
read it from SUBAKO_API_KEY, so put it there now:
export SUBAKO_API_KEY=sbk_ak_4f50c2aa8XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXThis is a shell variable of your own, for the curl examples. The CLI
never reads it — subako authenticates with the stored login from
subako login, not with an API key.
subako api-key permissions lists what this server will grant a key. Grant
the narrowest set that works — see Permissions.
Create the session
curl -X POST https://api.us.cloud.subako.ai/v1/sessions \
-H "Authorization: Bearer $SUBAKO_API_KEY" \
-H "Content-Type: application/json" \
-d '{"agent_id": "<agent-id>", "display_name": "ticket-4192"}'{
"id": "01a0877f-d3b3-7e73-9eed-a16caee8cff7",
"status": "idle",
"session_token": "sbk_st_...",
"session_token_expires_at": "2026-09-09T19:48:16Z"
}session_token is a credential scoped to this one session, carrying no
permissions, shown once. It is what you hand a browser tab; the API key
stays on your server. It expires at session_token_expires_at — mint more
from POST /v1/sessions/{session_id}/tokens with the API key.
The two remaining steps need the session’s id and that token, so keep
both — again as shell variables of your own:
export SESSION_ID=<the id from the response>
export SUBAKO_SESSION_TOKEN=<the session_token from the response>Note which credential each call takes: creating a session is the API key’s job, and everything inside the session is the session token’s.
Send a message
Appending a user turn starts a run.
curl -X POST https://api.us.cloud.subako.ai/v1/sessions/$SESSION_ID/events \
-H "Authorization: Bearer $SUBAKO_SESSION_TOKEN" \
-H "Content-Type: application/json" \
-d '{"type": "input", "text": "Summarize ticket 4192."}'The reply names the run this started:
{ "run_id": "01a08780-611d-7290-a9a0-e8a27b64ad9c", "outcome": "new_run" }Watch it work
The stream tails from the log’s head, so a run that already finished has
nothing left to send. Pass after_seq=0 to replay the session from its
first event and then continue live.
curl -N "https://api.us.cloud.subako.ai/v1/sessions/$SESSION_ID/events/stream?after_seq=0" \
-H "Authorization: Bearer $SUBAKO_SESSION_TOKEN"Events arrive as SSE, each with the sequence number a reconnect resumes
from. The run is over at run_completed, run_failed, or
run_cancelled.
id: 2
event: message_user
data: {"seq":2,"type":"message_user",...}
id: 5
event: message_assistant
data: {"seq":5,"type":"message_assistant",...}
id: 6
event: run_completed
data: {"seq":6,"type":"run_completed","run_id":"01a08780-..."}The stream stays open after that, waiting for the next run — curl will
not exit on its own, so interrupt it once you see run_completed.