# The autonomy dial

Every connected account has an autonomy level. It decides what happens when an **agent** schedules a post. Posts created by humans always schedule directly.

| Level | Name | Agent posts | Status |
|---|---|---|---|
| 1 | Review everything (default) | Wait in the approval queue until a human approves | Early access |
| 2 | Autopilot with guardrails | Posting windows, volume caps, keyword filters. Until they ship, level 2 behaves like level 1 | Coming soon |
| 3 | Full autopilot | Schedule directly; every action is logged | Early access |

Early access means it's built and being tested. Each platform opens to early-access users as it approves Turboyap. A human changes a level with `PATCH /v1/accounts/:id` and a human key that has the `publish` scope.

## Rules that keep agents honest

- **Agents can't approve.** Approving, rejecting, changing autonomy levels, pausing or resuming the workspace, and managing webhooks all need a human key. No scope unlocks them for an agent key, so an agent can't approve its own drafts or raise its own level.
- **Scopes never bypass the dial.** An agent key with the `publish` scope still waits for approval on a level-1 account.
- **Edits re-open review.** If an agent changes the caption or time of an approved post on a level-1 account, the post goes back to the approval queue and [`post.pending_approval`](/docs/webhooks) fires again.
- **Everything is logged.** Every create, approval, edit, publish and failure is written to the audit log: human or agent, the key that did it, and the client name from the `X-Turboyap-Client` header.
- **One kill switch.** Any human key can pause the workspace, which stops every publish immediately. Resuming takes a human key with the `publish` scope and picks up where it left off.
- **No double posts from a restart.** If Turboyap stops partway through publishing, it marks the post failed with `unknown_outcome` instead of publishing it again.

## TikTok: a person picks the privacy level

TikTok requires the creator to choose the privacy level of every direct post themselves, from a list with no default. So TikTok targets default to inbox mode, which means autopilot on TikTok sends posts to the creator's inbox as drafts unless a post asks for direct mode. The creator picks the privacy level, adds a sound and posts them in TikTok.

Direct mode (`tiktok.mode: "direct"`) needs a `tiktok.privacy_level`, which should be the creator's own choice, not the agent's. Until Turboyap passes TikTok's audit, direct posts are private (`SELF_ONLY`).

## Keys

| Key | Prefix | Default scopes |
|---|---|---|
| Agent | `ty_agent_` | `read`, `draft`, `schedule` |
| Human | `ty_human_` | `read`, `draft`, `schedule`, `publish` |
| Test agent | `ty_agent_test_` | `read`, `draft`, `schedule` |
| Test human | `ty_human_test_` | `read`, `draft`, `schedule`, `publish` |

Test keys see only test objects and publish to simulated platforms; see [test mode](/docs/test-mode). In early access, Turboyap creates the keys for your workspace, and self-serve key management comes with the dashboard.

## Scopes

| Scope | Allows |
|---|---|
| `read` | List and read accounts, media and posts |
| `draft` | Upload media, create and edit drafts, cancel posts |
| `schedule` | Set a future `schedule_at`; for human keys, approve and reject |
| `publish` | Publish immediately (`schedule_at: "now"`); for human keys, change autonomy levels and resume the workspace |

A key can also be limited to some accounts (`account_ids`). It then lists only those accounts and the posts that go to them, and gets `account_not_allowed` if it targets any other account. `GET /v1/keys/me` (MCP: `get_workspace`) shows the calling key's type, mode, scopes, allowlist and a plain-language `can` summary.

## Who can do what

| Action | Key | Scopes |
|---|---|---|
| Read accounts, media and posts | Any | `read` |
| Upload media, create or edit a draft, cancel a post | Any | `draft` |
| Schedule a post for a future time | Any | `draft`, `schedule` |
| Edit a post that's already queued | Any | `draft`, `schedule` |
| Publish immediately (`"now"`) | Any | `draft`, `schedule`, `publish` |
| Approve or reject a post | Human | `schedule` |
| Change an account's autonomy level | Human | `publish` |
| Pause the workspace (kill switch) | Human | None |
| Resume the workspace | Human | `publish` |
| Create a webhook endpoint | Human | `read` |
| List or delete webhook endpoints | Human | None |
| Create a test account | Test key | `draft` |
| See the calling key (`GET /v1/keys/me`, `GET /v1/me`) | Any | None |

A missing scope returns `insufficient_scope`, with the `field` that needs it and a `fix`. A human-only action with an agent key returns `forbidden_scope`.

Give agents agent keys only, with the fewest scopes that do the job: `read` and `draft` for an agent that only writes drafts, or the default `read`, `draft` and `schedule` for one that schedules. Keep human keys with people, and approve from your own scripts or the [REST API](/docs/api) until the dashboard ships.
