---
title: Quickstart
description: Create an organization, publish an agent version, and run your first session — start to finish, from the terminal and one HTTP call.
sidebar:
  order: 2
---

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

**macOS / Linux**

```bash
curl --proto '=https' --tlsv1.2 -LsSf https://github.com/subako-ai/subako-cli/releases/latest/download/subako-cli-installer.sh | sh
```

The installer puts `subako` in `$XDG_BIN_HOME` when you have one set, and `~/.local/bin` otherwise. Check it:

```bash
subako --version
```

## Set up an organization

1. **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](/concepts/plans-and-billing) for what the paid plans
    add and how to subscribe.

    ```bash
    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.

2. **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.

    ```bash
    subako login --org acme
    ```

    Use `--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.

    ```bash
    subako whoami
    ```

3. **Create 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.

    ```bash
    subako workspace create --name production
    subako workspace use production
    subako workspace list
    ```

## Publish an agent

1. **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.

    ```bash
    subako model-provider list
    ```

    ```txt
    workspace: 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=Sparrow
    ```

    A 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.

    ```bash
    subako model-provider create \
      --format anthropic \
      --display-name "Anthropic production" \
      --base-url https://api.anthropic.com < key.txt
    ```

    `subako 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.

2. **Create the agent**

    An agent is just a name and an identity. It holds no configuration of its
    own — its versions do.

    ```bash
    subako agent create --name support-triage
    ```

    It prints the agent id, which `agent publish` takes as its argument.
    `subako agent list` shows it again later.

3. **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.

    ```bash
    subako agent config-example --format openai_responses > agent.json
    ```

    ```json 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.

4. **Publish it**

    ```bash
    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.

:::warning
The model id is not checked at publish time. A wrong one is discovered when a session runs, not when the version is published.
:::

## 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.

1. **Mint an API key**

    An API key is the machine credential your product runs under. Its secret
    is printed once and never again.

    ```bash
    subako api-key mint \
      --label "support-triage backend" \
      --permission session.create \
      --permission session.read \
      --permission session.manage
    ```

    ```txt
    minted 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_4f50c2aa8XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
    ```

    Copy 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:

    ```bash
    export SUBAKO_API_KEY=sbk_ak_4f50c2aa8XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
    ```

    This 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](/reference/permissions).

2. **Create the session**

    ```bash
    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"}'
    ```

    ```json
    {
      "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:

    ```bash
    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.

3. **Send a message**

    Appending a user turn starts a run.

    ```bash
    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:

    ```json
    { "run_id": "01a08780-611d-7290-a9a0-e8a27b64ad9c", "outcome": "new_run" }
    ```

4. **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.

    ```bash
    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`.

    ```txt
    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`.

## Next steps

**[Sessions](/concepts/sessions)**

Runs, the event log, and what binds to what.

**[Client tools](/guides/client-tools)**

Give the agent functions only your app can run.

**[Vaults](/concepts/vaults)**

Let the agent reach a real system without seeing the credential.

**[Skills](/concepts/skills)**

Package a procedure once and grant it to an agent version.
