# Test mode: try the whole API in 5 minutes

Test mode runs the full Turboyap pipeline against simulated accounts: planning, media conversion, approvals, scheduling, publishing and webhooks. Nothing reaches a real platform, so it doesn't wait on Meta App Review or TikTok's audit. Like the rest of the API, it's in early access.

## How test mode works

- **Test keys** start with `ty_agent_test_` or `ty_human_test_`. They get the same default scopes as live keys (agent: `read`, `draft`, `schedule`; human: all four), and `GET /v1/keys/me` shows `"mode": "test"` and `"livemode": false`.
- **Test objects stay separate.** Test keys only see test objects, and live keys only see live ones. Accounts, media, posts, webhook endpoints and idempotency keys are all separate per mode, and every test object has `"livemode": false`. Using an id from the other mode returns `not_found`.
- **Simulated accounts.** `POST /v1/accounts/test` creates an account on any platform in early access or beta (TikTok, Instagram, Bluesky, Telegram or Discord) instantly, on autonomy level 1 like a newly connected real account. A live key gets `test_mode_only`.
- **Simulated publishing.** Test posts always publish through a simulated platform, never a real one. Instagram, Bluesky, Telegram and Discord targets end `published`, with a fake `platform_post_id` and a `platform_url` on `<platform>.test`, such as `instagram.test`. TikTok targets in inbox mode (the default) stop at `sent_to_inbox`, as they would for real; in direct mode they end `published`.
- **Test webhooks.** An endpoint registered with a test key gets only test events, and live endpoints never see them. See [webhooks](/docs/webhooks).

## Quickstart

You need your workspace's two test keys and an image. In early access, Turboyap creates the keys when it sets up your workspace.

```bash
export AGENT=ty_agent_test_…   # what your agent uses
export HUMAN=ty_human_test_…   # what you approve with
export API=https://api.turboyap.com
```

### 1. Create two simulated accounts

```bash
curl $API/v1/accounts/test -H "Authorization: Bearer $AGENT" \
  -H "Content-Type: application/json" -d '{"platform": "tiktok"}'
curl $API/v1/accounts/test -H "Authorization: Bearer $AGENT" \
  -H "Content-Type: application/json" -d '{"platform": "instagram", "handle": "yourbrand_test"}'
```

```json
{
  "account": {
    "id": "acct_…",
    "platform": "tiktok",
    "handle": "test_tiktok_7ad2",
    "livemode": false,
    "display_name": "Test account",
    "autonomy_level": 1,
    "autonomy": "review everything: agent posts wait for human approval",
    "status": "active",
    "status_reason": null,
    "token_expires_at": null,
    "next_step": null,
    "connected_at": "2026-10-20T15:00:00.000Z"
  }
}
```

Note both account ids. `handle` is optional: up to 30 letters, digits, dots or underscores.

### 2. Upload an image

```bash
curl $API/v1/media -H "Authorization: Bearer $AGENT" \
  -H "Content-Type: image/png" -H "X-Filename: label.png" \
  --data-binary @label.png
```

```json
{ "media": { "id": "med_…", "livemode": false, "kind": "image", "mime": "image/png", "width": 1080, "height": 1920, … }, "deduplicated": false }
```

### 3. Preview the post as the agent

Replace the ids, and set `schedule_at` to a couple of minutes from now, with a timezone (for example `2026-10-20T15:05:00Z`). The default agent key can schedule a future time but can't publish with `"now"`.

```bash
curl $API/v1/posts -H "Authorization: Bearer $AGENT" -H "Content-Type: application/json" -d '{
  "type": "image",
  "media_ids": ["med_…"],
  "caption": "Read the label.",
  "targets": [{ "account_id": "acct_…" }, { "account_id": "acct_…" }],
  "fit": { "aspect": "pad" },
  "schedule_at": "2026-10-20T15:05:00Z",
  "idempotency_key": "test-mode-quickstart-1",
  "dry_run": true
}'
```

```json
{
  "dry_run": true,
  "ok": true,
  "schedule_at": "2026-10-20T15:05:00.000Z",
  "targets": [
    { "platform": "tiktok", "post_type": "image", "slides": 1, "status_on_create": "pending_approval", "media_changes": ["convert PNG → JPEG", "fit within 1080×1920"], "tiktok_mode": "inbox", … },
    { "platform": "instagram", "post_type": "image", "slides": 1, "status_on_create": "pending_approval", "media_changes": ["convert PNG → JPEG", "pad to 0.8:1", "max width 1440px"], … }
  ],
  "errors": [],
  "warnings": [{ "code": "aspect_ratio_out_of_range", "message": "1 slide(s) will be padded to 4:5 for instagram.", … }]
}
```

Nothing was created. Both targets would start as `pending_approval`, because an agent made the post and the accounts are on level 1.

### 4. Create it

Send the same request without `"dry_run": true`. You get `201` and the post, with `"livemode": false` and `created_by.type` set to `"agent"`. Sending it again with the same `idempotency_key` returns the same post with `"replayed": true` instead of a second one.

### 5. Approve it as the human

```bash
curl -X POST $API/v1/posts/post_…/approve -H "Authorization: Bearer $HUMAN"
```

Both targets move to `scheduled`, and `approved_by` holds your human key's id. Try the same call with `$AGENT` and you get `forbidden_scope`, because agents can't approve.

### 6. Check the result

Once the scheduled time has passed:

```bash
curl $API/v1/posts/post_… -H "Authorization: Bearer $AGENT"
```

```json
{
  "post": {
    "id": "post_…",
    "livemode": false,
    "created_by": { "type": "agent", "key_id": "key_…", "client": null },
    "targets": [
      { "platform": "tiktok", "status": "sent_to_inbox", "next_step": "Delivered to the TikTok inbox. Open TikTok, add a sound, and post. Turboyap marks it published once it's live.", … },
      { "platform": "instagram", "status": "published", "platform_post_id": "instagram_post_1_f1afdb", "platform_url": "https://instagram.test/p/instagram_post_1_f1afdb", … }
    ],
    …
  }
}
```

That's the whole loop: an agent drafted, a human approved, and Turboyap published, without touching a real account. In test mode, nobody posts the TikTok draft, so that target stays at `sent_to_inbox`.

## Next steps

- **Skip review:** move a test account to full autopilot with the human key, and the agent's next post schedules directly:

  ```bash
  curl -X PATCH $API/v1/accounts/acct_… -H "Authorization: Bearer $HUMAN" \
    -H "Content-Type: application/json" -d '{"autonomy_level": 3}'
  ```

- **Hand it to your agent:** use the agent test key in the [MCP quickstart](/docs/mcp-quickstart).
- **Get events:** register a webhook endpoint with the human test key; see [webhooks](/docs/webhooks).
- **Use the SDK:** `@turboyap/sdk` has `createTestAccount({ platform, handle })` plus the same `uploadMedia`, `createPost`, `approvePost` and `getPost` calls. It isn't on npm yet.
- **Go live:** switch to live keys and connect real accounts through each platform's own sign-in, as each platform opens to early-access users.
