# Sonari TTS API 接入文档

最后更新：2026-08-02

公开网页文档：https://tts-admin.sonari.dev/api-docs

## 选择 API 版本

| 版本 | Base URL | 适用场景 |
| --- | --- | --- |
| v4 | `https://audio-api2.sonari.dev` | 新接入推荐；七语 TTS、HTTP 流式和 WebSocket 流式 |
| v3 | `https://audio-api.sonari.dev` | 现有生产调用；兼容旧版接口与 Voice Skills |

所有 `/v1/*` HTTP 请求均使用 Bearer Token：

```http
Authorization: Bearer cosy_YOUR_API_KEY
```

Token 属于敏感凭证，请只保存在服务端环境变量中，不要提交到代码仓库或写入浏览器前端代码。

## v4 五分钟快速开始

### 1. 查看支持语言

```bash
export SONARI_TTS_TOKEN="cosy_YOUR_API_KEY"
curl https://audio-api2.sonari.dev/v1/langs \
  -H "Authorization: Bearer $SONARI_TTS_TOKEN"
```

v4 支持 `zh`、`en`、`tl`、`id`、`th`、`ar`、`hi`。

### 2. 查看可用音色

```bash
curl https://audio-api2.sonari.dev/v1/voices \
  -H "Authorization: Bearer $SONARI_TTS_TOKEN"
```

### 3. 合成语音

```bash
curl -X POST https://audio-api2.sonari.dev/v1/syntheses \
  -H "Authorization: Bearer $SONARI_TTS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "text": "你好，欢迎使用 Sonari TTS。",
    "voice": "YOUR_VOICE_SLUG",
    "lang": "zh",
    "format": "wav"
  }'
```

成功响应包含合成任务信息及 `audio_url` / `signed_audio_url`。服务端下载音频时可继续携带 Authorization；需要把音频链接交给浏览器或其他终端时，优先使用有时效的 `signed_audio_url`。

## 目录

- [v4 完整接口](#sonari-tts-api-v4)
- [v3 完整接口](#sonari-tts-api-v3)

---
# Sonari TTS API v4

Version: v4 independent beta
Last updated: 2026-07-05
Base URL: https://audio-api2.sonari.dev
Auth: Authorization: Bearer cosy_<token>

## Current Usage

- Status: independent beta, available for PepLive validation and new multilingual integrations.
- Current production v3 traffic is not routed through v4.
- Engine: sonari-3 / VoxCPM2 on 117.
- Public domain: https://audio-api2.sonari.dev
- Internal `/api/*` and `/docs` are not exposed on the public v4 domain.
- v4 uses a separate process, separate domain, and separate worker from v3.

## Health Checks

```bash
curl https://audio-api2.sonari.dev/

curl https://audio-api2.sonari.dev/v1/langs \
  -H "Authorization: Bearer cosy_xxx"

curl -i https://audio-api2.sonari.dev/api/status
# expected: 404
```

## Authentication

All `/v1/*` endpoints require:

```http
Authorization: Bearer cosy_<token>
```

For WebSocket, the token can be sent in the first JSON frame as `token`.

## Languages

```http
GET /v1/langs
```

Supported language codes:

| Code | Language | Clone | Stream | Status |
|---|---|---:|---:|---|
| `zh` | Chinese | yes | yes | ga |
| `en` | English | yes | yes | ga |
| `tl` | Filipino | yes | yes | beta |
| `id` | Indonesian | yes | yes | beta |
| `th` | Thai | yes | yes | beta |
| `ar` | Arabic | yes | yes | beta |
| `hi` | Hindi | yes | yes | beta |

Note: small-language status is currently marked beta in the live API until native-speaker and automated quality acceptance is complete.

## Voices

### List Voices

```http
GET /v1/voices
```

### Upload Voice

```http
POST /v1/voices
Content-Type: multipart/form-data
```

Fields:

| Field | Required | Notes |
|---|---:|---|
| `audio` or `wav` | yes | Reference audio. Server decodes and stores 16 kHz wav prompt. |
| `voice_slug` or `name` | yes | Voice slug. Letters, digits, `_`, `-` are accepted. |
| `prompt_text` | recommended | Exact transcript of the reference audio. |

### Other Voice Endpoints

```http
GET    /v1/voices/{voice_slug}
GET    /v1/voices/{voice_slug}/audio
DELETE /v1/voices/{voice_slug}
```

## Synthesis

### Sync Synthesis

```http
POST /v1/syntheses
Content-Type: application/json
```

Request:

```json
{
  "text": "สวัสดีครับ ยินดีต้อนรับ",
  "lang": "th",
  "voice_slug": "v_a04a9284e1be4012",
  "output_sample_rate": 24000,
  "share_ttl_seconds": 3600
}
```

Fields:

| Field | Required | Default | Notes |
|---|---:|---|---|
| `text` | yes | - | 1-2000 chars. |
| `lang` | no | null | Recommended. One of `zh/en/tl/id/th/ar/hi`. |
| `voice_slug` or `voice` | no | default voice | Uploaded clone voice slug. |
| `output_sample_rate` or `sample_rate` | no | `24000` | `24000` or `48000`. |
| `share_ttl_seconds` | no | null | Adds `signed_audio_url`. Range is clamped to 60s-30d. |

Response:

```json
{
  "data": {
    "id": "abc123",
    "audio_url": "https://audio-api2.sonari.dev/v1/syntheses/abc123/audio",
    "signed_audio_url": "https://audio-api2.sonari.dev/v1/syntheses/abc123/audio?exp=...&sig=...",
    "sample_rate": 24000,
    "audio_s": 2.6,
    "first_chunk_ms": 72,
    "total_s": 1.2,
    "rtf": 0.46,
    "voice_slug": "v_a04a9284e1be4012",
    "lang": "th",
    "engine": "sonari-3",
    "instruction_applied": false
  }
}
```

### Audio Fetch

```http
GET /v1/syntheses/{id}/audio
```

Use either Bearer auth or a valid signed URL with `exp` and `sig`.

### HTTP Streaming

```http
POST /v1/syntheses/stream_http
Content-Type: application/json
```

Returns raw PCM s16le stream. Use for server-side low-latency integrations that prefer HTTP chunking over WebSocket.

### WebSocket Streaming

```text
wss://audio-api2.sonari.dev/v1/syntheses/stream
```

First client frame:

```json
{
  "token": "cosy_xxx",
  "text": "Selamat malam semuanya.",
  "lang": "id",
  "voice_slug": "v_a04a9284e1be4012",
  "sample_rate": 24000
}
```

Server frames:

```text
text   {"type":"meta","sample_rate":24000,"native_sample_rate":48000,"voice_slug":"...","lang":"id","engine":"sonari-3"}
binary PCM s16le
binary PCM s16le
text   {"type":"done","first_chunk_ms":72,"total_s":1.35}
```

## Operational Notes

- v4 is independent from v3 and must not be mounted on `audio-api.sonari.dev`.
- v4 has a single-model lock on 117. High concurrency queues rather than concurrently corrupting the model.
- Current E2E shows server-side first chunk around tens of milliseconds for sonari-3 routes; public client latency still includes DNS/TCP/TLS/nginx/tunnel.

---

# Sonari TTS API v3

Version: v3 production legacy
Last updated: 2026-07-05
Base URL: https://audio-api.sonari.dev
Auth: Authorization: Bearer cosy_<token>

## Current Usage

- Status: production, keep online for existing callers.
- Current business users: PepLive and other existing integrations.
- Engine: CosyVoice 2 on 106.
- Public domain: https://audio-api.sonari.dev
- v4 is not mounted here. `/v1/langs` must return 404 on this domain.
- v3 has a global inference lock to protect the single CosyVoice model from concurrent GPU/model corruption.

## Health Checks

```bash
curl https://audio-api.sonari.dev/healthz

curl https://audio-api.sonari.dev/v1/me \
  -H "Authorization: Bearer cosy_xxx"

curl -i https://audio-api.sonari.dev/v1/langs
# expected: 404
```

## Authentication

All `/v1/*` endpoints require:

```http
Authorization: Bearer cosy_<token>
```

Do not put API keys in mobile apps, browser bundles, or public repos. Keep the key on your backend and pass signed audio URLs to clients when direct playback is needed.

## Voices

### List Voices

```http
GET /v1/voices
GET /v1/voices?scope=system
GET /v1/voices?scope=tenant
```

### Upload Voice

```http
POST /v1/voices
Content-Type: multipart/form-data
```

Fields:

| Field | Required | Notes |
|---|---:|---|
| `wav` | yes | Reference audio. wav/mp3/m4a/flac/ogg are accepted and decoded server-side. |
| `slug` | yes | Tenant voice slug. Use letters, digits, and `_`. |
| `prompt_text` | yes | Exact transcript of the reference audio. |
| `persona` | no | Free-form label for your own sorting. |

### Other Voice Endpoints

```http
GET    /v1/voices/{voice_id}
GET    /v1/voices/{voice_id}/audio
DELETE /v1/voices/{voice_id}
```

## Synthesis

### Sync Synthesis

```http
POST /v1/syntheses
Content-Type: application/json
```

Request:

```json
{
  "text": "你好，欢迎来到 PepLive。",
  "voice_slug": "peplive_103203071",
  "speed": 1.0,
  "save": true,
  "share_ttl_seconds": 3600,
  "instruct_text": "用自然亲切的语气说"
}
```

Fields:

| Field | Required | Default | Notes |
|---|---:|---|---|
| `text` | yes | - | 1-2000 chars. |
| `voice_slug` | yes | - | System or tenant voice slug. |
| `speed` | no | `1.0` | 0.5-2.0. |
| `save` | no | `true` | `false` returns WAV bytes directly. |
| `share_ttl_seconds` | no | null | When `save=true`, response includes `signed_audio_url`. |
| `instruct_text` | no | null | CosyVoice instruct/prosody prompt. |

When `save=true`, response is JSON:

```json
{
  "data": {
    "id": "abc123",
    "voice_slug": "peplive_103203071",
    "audio_url": "/v1/syntheses/abc123/audio",
    "signed_audio_url": "https://audio-api.sonari.dev/v1/syntheses/abc123/audio?exp=...&sig=..."
  }
}
```

When `save=false`, response body is `audio/wav`.

### Audio Fetch

```http
GET /v1/syntheses/{id}/audio
```

Use either Bearer auth or a valid signed URL with `exp` and `sig`.

### WebSocket Streaming

```text
wss://audio-api.sonari.dev/v1/syntheses/stream
```

First client text frame:

```json
{
  "token": "cosy_xxx",
  "text": "Hello, welcome to Sonari.",
  "voice_slug": "peplive_103203071",
  "speed": 1.0,
  "save": false,
  "instruct_text": null
}
```

Server frames:

```text
text   {"type":"meta","sample_rate":24000,"voice_slug":"..."}
binary PCM s16le
binary PCM s16le
text   {"type":"done","id":null}
```

## Voice Skills

Voice skills convert an existing performance into any visible Cosy voice. The
reference recording supplies words or non-verbal content, melody, rhythm,
breathing and intensity; `voice_slug` supplies the output speaker identity.

List skills:

```bash
curl https://audio-api.sonari.dev/v1/voice-skills \
  -H "Authorization: Bearer cosy_xxx"
```

For repeated calls, register a performance directly on one target voice:

```bash
curl -X POST https://audio-api.sonari.dev/v1/voice-skills/registrations \
  -H "Authorization: Bearer cosy_xxx" \
  -F "voice_slug=mj003" \
  -F "skill=moaning" \
  -F "name=soft-moaning-01" \
  -F "source_audio=@performance.mp3" \
  -F "engine=seed_vc" \
  -F "identity_strength=1.0"
```

After registration, third-party calls only need the target voice and skill:

```bash
curl -X POST https://audio-api.sonari.dev/v1/voice-skills/apply \
  -H "Authorization: Bearer cosy_xxx" \
  -F "voice_slug=mj003" \
  -F "skill=moaning" \
  -F "save=true" \
  -F "share_ttl_seconds=3600"
```

An enterprise/system voice can be registered the same way. Migrate its skill to
another target without re-uploading the performance:

```bash
curl -X POST https://audio-api.sonari.dev/v1/voice-skills/registrations/migrate \
  -H "Authorization: Bearer cosy_xxx" \
  -H "Content-Type: application/json" \
  -d '{"source_registration_id":"<REGISTRATION_ID>","target_voice_slug":"target_b","overwrite":true}'
```

Management endpoints:

```text
GET    /v1/voice-skills/registrations
POST   /v1/voice-skills/registrations
POST   /v1/voice-skills/registrations/migrate
PATCH  /v1/voice-skills/registrations/{registration_id}
DELETE /v1/voice-skills/registrations/{registration_id}
```

The previous reusable `reference_id` flow and one-off
`source_audio=@performance.mp3` flow remain supported. `save=false` returns
`audio/wav` directly; `save=true` returns JSON containing `audio_url` and
optionally `signed_audio_url`. The signed-in management UI is available at
`https://tts-admin.sonari.dev/`.

Supported skills: `neutral`, `dialogue`, `narration`, `happy`, `sad`, `angry`,
`breathy`, `shout`, `singing`, `humming`, `panting`, `moaning`.

Reference limits: 50 MB, 0.25-300 seconds, up to 200 saved references per
tenant. Voice-skill conversion is full-file processing and is not streamed.

## Operational Notes

- v3 is the compatibility surface. Do not add v4-only fields to old callers unless they explicitly migrate.
- v3 has no `/v1/langs` endpoint. A 404 here is expected and is monitored.
- Current latency is dominated by public TLS/proxy path and CosyVoice model chunking, not by 106 local health. 106 local `/healthz` is around millisecond-level.
