> ## Documentation Index
> Fetch the complete documentation index at: https://docs.youka.io/llms.txt
> Use this file to discover all available pages before exploring further.

# AI Agents

> Operating rules and workflow patterns for agents calling the Youka CLI or API

Youka is designed to be driven by agents. Every CLI command returns a machine-readable JSON envelope, every write supports idempotency keys, and the public API exposes the same surface with the same semantics. This page collects the operating rules an agent author needs before wiring Youka into a workflow.

## Get started

If you're setting up an agent for the first time, install the Youka CLI first, then install the Youka karaoke skill:

```bash theme={null}
npm install -g @youka/cli
npx skills add https://github.com/youka-io/skills --skill youka-karaoke
```

That gives the agent a ready-made Youka skill backed by the CLI. Once that's installed, use the workflow patterns below to create karaoke videos, customize the style, and export MP4 files safely.

### Prompt your agent

After installing the CLI and skill, you can give your agent a direct task like this:

<CodeGroup>
  ```text local-file theme={null}
  Use Youka to create a karaoke video from /path/to/song.mp3. Transcribe the lyrics in English, use a clean karaoke subtitle style, export the final video in 1080p, and save it as ./karaoke.mp4.
  ```

  ```text url theme={null}
  Use Youka to create a karaoke video from https://example.com/song.mp4. If URL dependencies are missing, install them first. Then transcribe the lyrics automatically, use a clean karaoke subtitle style, export the final video, and save it as ./karaoke.mp4.
  ```
</CodeGroup>

## Operating rules

<CardGroup cols={2}>
  <Card title="Always use --json" icon="code">
    On the CLI, always pass `--json`. In the API, always set `Accept:
            application/json`. Never parse human output.
  </Card>

  <Card title="Always send an idempotency key" icon="rotate">
    Every write should carry a stable idempotency key so retries after a timeout
    return the original result instead of duplicating work.
  </Card>

  <Card title="Re-read durable state" icon="database">
    Don't trust stale mutation responses. After a terminal task, re-read the
    project or export to get the final state.
  </Card>

  <Card title="Poll with backoff" icon="hourglass">
    Start with a 2–3 second interval. Exponentially back off for long jobs.
    Respect 429 responses.
  </Card>
</CardGroup>

## Output contract

Every CLI command in `--json` mode writes exactly one envelope to stdout.

Success:

```json theme={null}
{
  "schemaVersion": "1",
  "ok": true,
  "data": { "id": "prj_abc123", "title": "My Song" }
}
```

Failure:

```json theme={null}
{
  "schemaVersion": "1",
  "ok": false,
  "error": {
    "code": "CONFLICT",
    "message": "A request with this Idempotency-Key is already in progress.",
    "retryable": true
  }
}
```

Exit codes:

| Code | Meaning                                        | Retry?                   |
| ---- | ---------------------------------------------- | ------------------------ |
| `0`  | Success.                                       | N/A                      |
| `1`  | Runtime error (network, API, rendering).       | Check `error.retryable`. |
| `2`  | Invalid input (bad flags, unreadable payload). | No — fix the input.      |

## End-to-end workflow

The canonical agent workflow is: create a project, wait, export, download the result. Here's the full pattern with both CLI and SDK implementations.

<CodeGroup>
  ```bash CLI theme={null}
  # 1. Create the project and wait for stems + lyrics sync to finish
  PROJECT_JSON=$(youka project create ./song.mp3 \
    --mode transcribe \
    --lang en \
    --wait \
    --idempotency-key "create-song-2026-04-08-001" \
    --json)

  PROJECT_ID=$(echo "$PROJECT_JSON" | jq -r '.data.projectId')

  # 2. Start and wait for an export
  EXPORT_JSON=$(youka export create "$PROJECT_ID" \
    --resolution 1080p \
    --quality high \
    --wait \
    --download \
    --output ./karaoke.mp4 \
    --idempotency-key "export-$PROJECT_ID-v1" \
    --json)

  echo "Done:" "$(echo "$EXPORT_JSON" | jq -r '.data.fileUrl')"
  ```

  ```ts SDK theme={null}
  import { YoukaClient, YoukaRequestError, YoukaTaskError } from "@youka/sdk";

  const client = new YoukaClient({ apiKey: process.env.YOUKA_API_KEY! });

  async function createAndExport() {
    // 1. Create the project and wait for stems + lyrics sync
    const created = await client.projects.create(
      {
        source: { type: "path", path: "./song.mp3" },
        lyricsSource: { type: "transcribe", language: "en" },
      },
      { idempotencyKey: "create-song-2026-04-08-001" },
    );

    const { project } = await client.projects.wait(created);

    // 2. Start and wait for an export
    const exportResult = await client.exports.create(
      project.id,
      { resolution: "1080p", quality: "high" },
      { idempotencyKey: `export-${project.id}-v1` },
    );

    const finalized = await client.exports.wait(exportResult);

    // 3. Download the file
    await client.exports.download(finalized, {
      output: "./karaoke.mp4",
    });
  }

  createAndExport().catch((error) => {
    if (error instanceof YoukaRequestError && error.retryable) {
      // Retry with the same idempotency keys
    }
    if (error instanceof YoukaTaskError) {
      console.error("Task failed:", error.code, error.task.error);
    }
    throw error;
  });
  ```
</CodeGroup>

## Polling guidance

Most writes return operation handles in the SDK. Prefer `client.projects.wait(...)` and `client.exports.wait(...)`, and drop to `client.tasks.*` only when you explicitly need low-level task access.

| Job type                             | Recommended initial interval | Max wait |
| ------------------------------------ | ---------------------------- | -------- |
| Project create (stems + lyrics sync) | 3 s                          | 15 min   |
| Stem separation only                 | 3 s                          | 10 min   |
| Lyrics sync only                     | 2 s                          | 5 min    |
| Export render                        | 3 s                          | 30 min   |

On the CLI, `--wait` handles polling for you. On the SDK, the wait helpers use a 2 s interval by default and accept `pollIntervalMs`.

<Tip>
  Respect 429 responses. On rate limits, back off by at least the `Retry-After`
  header (or 30 s if absent).
</Tip>

## Idempotency keys

Every create and update operation supports an idempotency key. Rules:

* Use a **stable, unique, deterministic** key per logical mutation. Good: `create-song-${sourceHash}`. Bad: a fresh UUID per retry.
* Reuse the **same** key across retries. The server recognizes the replay and returns the original result.
* Keys are scoped per-account and expire after 24 hours.
* If the server returns `IDEMPOTENT_REPLAY_IN_PROGRESS` (HTTP 202), the original request is still running. Wait and retry with the same key.

Example pattern:

```ts theme={null}
const key = `create-${sha256(sourceBytes)}`;

try {
  return await client.projects.create(body, { idempotencyKey: key });
} catch (error) {
  if (error instanceof YoukaRequestError && error.retryable) {
    // Safe to retry with the same key
    return await client.projects.create(body, { idempotencyKey: key });
  }
  throw error;
}
```

See [API idempotency](/en/api/idempotency) for the full contract.

## Error recovery

Branch on `error.code` and `error.retryable`. The common cases:

| Code                                  | Cause                                      | Action                                              |
| ------------------------------------- | ------------------------------------------ | --------------------------------------------------- |
| `INVALID_REQUEST`                     | Request body failed schema validation.     | Fix the payload. No retry.                          |
| `UNAUTHORIZED`                        | Missing or invalid API key.                | Surface to the caller. No retry.                    |
| `NOT_FOUND`                           | Resource doesn't exist or you lack access. | No retry.                                           |
| `CONFLICT` (409)                      | Version conflict or idempotency collision. | Retry with the same idempotency key.                |
| `TOO_MANY_REQUESTS` (429)             | Rate limit.                                | Back off by `Retry-After` or 30 s.                  |
| `INTERNAL_SERVER_ERROR` (500)         | Transient server failure.                  | Retry up to 3 times with backoff.                   |
| `IDEMPOTENT_REPLAY_IN_PROGRESS` (202) | Prior request still running.               | Wait and retry with the same key.                   |
| `TASK_FAILED`                         | The async task ended in failure.           | Surface `error.task.error` to the caller. No retry. |
| `TASK_TIMED_OUT`                      | The async task exceeded its budget.        | Safe to retry once.                                 |

## Discovering mutable fields

Before mutating presets, fetch the preset schema so the model knows the valid fields and value types:

<CodeGroup>
  ```bash CLI theme={null}
  # Preset body schema
  youka preset schema --json
  ```

  ```ts SDK theme={null}
  import { KaraokePresetSchema } from "@youka/sdk";

  const presetSchema = KaraokePresetSchema.toJSONSchema();
  ```
</CodeGroup>

For project settings, prefer reading the current project settings first, then submitting a minimal update body through `youka project settings <id> --body ...`.

Fetch once per agent session and cache the result.

## Parallel agents

When multiple agents run concurrently against the same account:

* Scope idempotency keys to **both** the agent identity and the logical mutation: `agent-${agentId}-create-${sourceHash}`. This prevents one agent's retry from colliding with another agent's fresh request.
* Keep reads unbounded — `GET` requests are cheap and have no side effects.
* Use a single queue for `POST /projects/{projectId}/exports` per project if you need ordered export history. Otherwise exports may arrive out of order.

## What's next

* [CLI global options](/en/cli/global-options) — every flag available to agents
* [SDK errors](/en/sdk/errors) — full error class reference
* [API async jobs](/en/api/async-jobs) — the polling contract
* [API idempotency](/en/api/idempotency) — the idempotency contract
