# Quickstart

> Get an API key, make an authenticated call, discover a node type and execute a run. Roughly fifteen minutes, no conversation with anyone required.

Source: https://kaitoi.io/docs/core/quickstart/
Section: Kaitoi Core
Written for: Developers calling Kaitoi from their own code for the first time.
Prerequisites: A Kaitoi Studio account; curl or any HTTP client
Last reviewed: 2026-09-16

---

Kaitoi Core is the programmable platform underneath Kaitoi Studio. The canvas is
one client of it. This page gets you to a working authenticated call, then to a
real run.

Every endpoint lives under one base URL:

```text
https://api.studio.kaitoi.io/api/v1
```

## 1. Get an API key

Keys are issued from your Kaitoi Studio account, under **Settings → Developer →
API Keys → New API Key**.

Keys are scoped. Grant only what the integration needs:

| Scope | Allows |
|---|---|
| `projects:read` | Read projects |
| `projects:write` | Create and update projects |
| `graphs:read` | Read project graphs |
| `node_types:read` | Discover node types and their schemas |
| `runs:execute` | Create and manage runs |
| `files:read`, `files:write` | Read and upload files |
| `templates:read`, `templates:run` | Read and run template endpoints |
| `account_credits:read` | Read the account credit balance |

There are two kinds of key, and the difference is not cosmetic:

- **Backend service keys** are the default. They are bearer secrets, for your
  own backend, CLI tools, desktop apps, workers and CI jobs. Do not ship one in
  browser JavaScript.
- **Browser app keys** are for calling Kaitoi directly from frontend JavaScript.
  They must be attached to a Developer App with an explicit list of allowed
  origins. The origin check is a containment measure, not a secret: anything in
  a browser is visible to the person using it, so keep these read-only or
  narrowly scoped.

For most production web apps the right shape is the boring one. Your frontend
calls your backend, and your backend calls Kaitoi with a service key.

Keep the key in an environment variable for the rest of this page:

```bash
export KAITOI_API_KEY="your key here"
```

## 2. Confirm the key works

The cheapest authenticated call reads your credit balance. It needs
`account_credits:read`.

```bash
curl -s https://api.studio.kaitoi.io/api/v1/account/credits \
  -H "Authorization: Bearer $KAITOI_API_KEY"
```

A working key returns your balance in three forms: credits, exact
microcredits, and dollars.

```json
{
  "balanceCents": 1250,
  "balanceMicrocredits": "1250110000",
  "balanceDollars": 12.5
}
```

Use `balanceMicrocredits` for anything that has to reconcile exactly.
`balanceCents` is rounded and will not always agree with it to the last unit.

If something is wrong, you will get a `401` with a body that says which problem
it is. Missing header:

```json
{
  "error": {
    "code": "UNAUTHORIZED",
    "message": "Missing Authorization header. Send 'Authorization: Bearer <api_key>'.",
    "details": {}
  }
}
```

Bad key:

```json
{
  "error": {
    "code": "UNAUTHORIZED",
    "message": "API key is invalid, expired, or revoked.",
    "details": {}
  }
}
```

Every error in the API uses that `{ "error": { "code", "message", "details" } }`
shape. Branch on `code`, not on the message text.

## 3. Find something to run

Do not hard-code node types. The catalogue differs per account and changes over
time. Search it:

```bash
curl -s "https://api.studio.kaitoi.io/api/v1/node-types?search=image&limit=5" \
  -H "Authorization: Bearer $KAITOI_API_KEY"
```

Pick a `type` from the results, then read its schema to learn its input pins:

```bash
curl -s "https://api.studio.kaitoi.io/api/v1/node-types/$NODE_TYPE" \
  -H "Authorization: Bearer $KAITOI_API_KEY"
```

You need the exact input names from that response for the next step.

## 4. Run it

You do not need to save a project first. Send the graph inline:

```bash
curl -s -X POST https://api.studio.kaitoi.io/api/v1/runs \
  -H "Authorization: Bearer $KAITOI_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{
    "graph": {
      "nodes": [
        {
          "id": "a",
          "type": "'"$NODE_TYPE"'",
          "inputs": {
            "<input name from step 3>": { "type": "string", "value": "<value>" }
          }
        }
      ]
    },
    "targetNodeIds": ["a"]
  }'
```

Four things about that request are worth not skipping.

Each input is a **typed pin object**, `{ "type": ..., "value": ... }`, not a bare
value. A bare value is rejected with `INVALID_INPUT_VALUE` before anything runs.
The types are `string`, `number`, `boolean`, `json`, `file` and `null`.

`targetNodeIds` is required for an inline graph. There is no saved project to
supply a default target.

`Idempotency-Key` should be on every run you create. If the connection drops
after the server accepted the run, a retry without the key starts a second run
and charges you twice.

The response is `202 Accepted` with a run record, not a result. Runs are
asynchronous.

```json
{
  "id": "run_...",
  "sourceType": "inline_graph",
  "status": "accepted",
  "targetNodeIds": ["a"],
  "createdAt": "..."
}
```

## 5. Wait for it

Poll the run until it reaches a terminal status:

```bash
curl -s "https://api.studio.kaitoi.io/api/v1/runs/$RUN_ID" \
  -H "Authorization: Bearer $KAITOI_API_KEY"
```

`status` moves through `accepted`, `queued` and `running` to one of `succeeded`,
`failed` or `canceled`. On success, `outputs` holds the result and `creditsUsed`
holds what it cost.

Outputs come back in the same typed shape the inputs use:

```json
{
  "status": "succeeded",
  "creditsUsed": 0,
  "executionTimeMs": 439,
  "outputs": {
    "outputText": { "type": "string", "value": "Kaitoi quickstart verification" }
  }
}
```

If you want progress rather than a result, stream the run's events instead:

```bash
curl -N "https://api.studio.kaitoi.io/api/v1/runs/$RUN_ID/events/stream" \
  -H "Authorization: Bearer $KAITOI_API_KEY"
```

That is Server-Sent Events. The event `id` is an opaque cursor you can send back
as `Last-Event-ID` to resume after a dropped connection.

## Rate limits

Requests are limited per client IP before authentication and per API key after
it. A `429 RATE_LIMITED` response carries a `Retry-After` header. Honour it.

In production the limiter is shared across backend instances and fails closed,
so a `503 RATE_LIMIT_UNAVAILABLE` means the limiter itself could not be reached,
not that you did anything wrong. Back off and retry.

## Where the full reference lives

This page is a starting point, not the contract. The contract is generated from
the running API, so it cannot drift from it:

- [Interactive API reference](https://api.studio.kaitoi.io/api/v1/docs), every endpoint, browsable
- [OpenAPI schema](https://api.studio.kaitoi.io/api/v1/openapi.json), machine-readable
- [Postman collection](https://api.studio.kaitoi.io/api/v1/postman.json), generated from the same schema
- [llms.txt](https://api.studio.kaitoi.io/api/v1/llms.txt) and [llms-full.txt](https://api.studio.kaitoi.io/api/v1/llms-full.txt), for coding agents

## Current limitations

- Runs are observed by polling or SSE. There is no run-level webhook yet.
- List cursors are opaque. Never parse or construct one.
