# Beatra API — Voice clone

> Clone a reusable voice from an authorized audio sample.
> Self-contained integration guide for AI coding assistants.
> Human-readable page: https://docs.beatra.ai/en/docs/capabilities/speech/voice-clone

- Base URL: `https://api.beatra.ai/v1`
- Auth: `Authorization: Bearer <BEATRA_API_KEY>` — create keys in the console at https://console.beatra.ai (Developer → API keys)
- Headers: `Content-Type: application/json`; send `Idempotency-Key` (stable UUID) on create-style POSTs (24h dedupe window); optional `X-Request-Id` is echoed back.
- Interaction model: async task — create returns `202` with a `task_id`; poll `GET /v1/tasks/{task_id}` until terminal status (`succeeded` / `failed` / `canceled`), or pass `callback_url` to be notified (see appendix).

---

## Primary agent path

Use the Universal Skill with the MCP tool `beatra.voices.clone`. The tool is
billable, consent-gated, and returns an asynchronous task.

1. Confirm with the user that they hold the rights to this voice. Set
   `consent_attested: true` only after that explicit confirmation.
2. Upload the local sample with `beatra.assets.upload` and use its artifact ID.
   10 seconds to 5 minutes of clean, single-speaker audio works best.
3. Create one opaque `client_request_id` and submit the final arguments once.
4. Poll the returned task with `beatra.tasks.get`; the terminal response
   returns the new `voice_id`.

## Tool arguments

```json
{
  "sample": { "type": "artifact", "artifact_id": "artifact_01JX..." },
  "display_name": "Brand narration - female",
  "consent_attested": true,
  "client_request_id": "voice-clone-opaque-1"
}
```

`sample` accepts an artifact, HTTPS URL, or data URI. `display_name` is
required (≤255 characters). A request without `consent_attested: true` is
rejected.

## Models, controls, and cost

Omit `model` (or use `auto`) unless the user names one. `beatra.models.list`
with `capability: "voice_clone"` is the current source for selectable models,
accepted sample kinds, and the fixed price.

Voice cloning has one fixed charge when the activated voice is created
successfully. Success means Beatra already ran a short formal synthesis to
activate the clone — that activation is included in the charge; later speech
synthesis is billed separately. A failed clone or activation does not retain
the charge.

## Task status and recovery

`queued` and `running` are not failures. Keep polling the same `task_id`; never
submit a replacement because work is still running. On a lost create response,
retry the identical arguments with the same `client_request_id`. If any input
changes, use a new ID. The returned `voice_id` works directly as the `voice`
field in [text to speech](https://docs.beatra.ai/en/docs/capabilities/speech/text-to-speech), stays
valid long-term, and can be inspected or deleted in
[voices](https://docs.beatra.ai/en/docs/capabilities/speech/voices).

## REST API

Direct protocol integrations may use `POST /v1/voices` and the shared task
endpoints. Follow the generated [API operation](https://docs.beatra.ai/en/docs/api-reference/operations/voices/clone_voice_v1_voices_post).
Skill + MCP is the recommended integration; use REST for custom, non-agent
applications.

---

## Appendix: shared contract for async tasks

### Callbacks (optional)

Pass `callback_url` (HTTPS) when creating the task. After the task reaches a
    terminal state Beatra POSTs to it and expects a 2xx within 10 seconds:

```json
{ "event_id": "evt_...", "event_type": "task.succeeded", "event_time": "...", "task": { "...": "full task envelope" } }
```

Headers: `X-Event-Id` (stable across retries — use for idempotent dedupe),
`X-Delivery-Id`, `X-Delivery-Attempt`, `X-Task-Id`, `X-Event-Type`, `X-Timestamp`,
and, for signed delivery, `X-Signature-Key-Id` plus
`X-Signature: t=<unix_ts>,v1=<hex>` where `v1 = HMAC-SHA256(signing_secret, "<unix_ts>." + raw_body)`.
Callback signing keys are independent of API keys and are managed through the
account signing-key API or Console. Pass `callback_signing_key_id` with
`callback_url` for signed delivery, or omit it for unsigned delivery. The
secret is returned once when a signing key is created or rotated. Reject
timestamps more than 300s off. Retries on failure: 1m / 5m / 30m / 2h / 12h.
Polling `GET /v1/tasks/{task_id}` always remains the source of truth.

### Billing

Credits are an independent billing unit with no fixed exchange rate to a fiat
currency. Media tasks are charged up-front at creation; insufficient balance returns `402 insufficient_balance`
and no task is created. Failed tasks are refunded automatically — see
`billing.charged_credits` / `billing.refunded_credits` in the task envelope.

### Errors

Every non-2xx response uses one envelope:

```json
{ "error": { "code": "...", "type": "...", "message": "...", "retryable": false, "request_id": "req_..." } }
```

Retry only when `retryable` is `true`, with exponential backoff and the SAME
`Idempotency-Key`. `429` responses include `X-RateLimit-*` headers.
