Studio · API Keys Đăng nhập Bảng giá
Developer docs · /api/v1

Tài liệu API HVoice

Tích hợp TTS, STT và nhân bản giọng vào sản phẩm của bạn. Base URL API: https://hvoice.app/api/v1

Base URLhttps://hvoice.app/api/v1
AuthAuthorization: Bearer hv_live_…
Max / synthesize100.000 ký tự / request
Async jobsynthesize → poll GET /tts/jobs/:id

Getting started

  1. Tạo API key trong Studio → Tích hợp API. Full key chỉ hiện một lần — lưu ngay.
  2. GET /voices lấy voice_id và xem credit_multiplier.
  3. POST /tts/synthesize với text + voice_id → nhận job_id.
  4. Poll GET /tts/jobs/:jobId đến completed → dùng audio_url (hoặc nhận webhook).

Authentication

Mọi request TTS/STT/voices gửi:

Authorization: Bearer hv_live_…
Hai loại token
• hv_live_… — API key cho tích hợp (TTS, voices, STT, usage, webhook).
• JWT đăng nhập Studio — bắt buộc để GET/POST/DELETE /account/api-keys (tạo/thu hồi key). Không dùng hv_live_ cho các endpoint quản lý key.
curl -X POST https://hvoice.app/api/v1/account/api-keys \
  -H "Authorization: Bearer <studio_jwt>" \
  -H "Content-Type: application/json" \
  -d '{"name":"Production"}'

Limits & billing

  • Mỗi POST /tts/synthesize / synthesize-srt tối đa 100.000 ký tự. Dài hơn → tách nhiều request.
  • Rate limit theo gói (mặc định ~30 req/phút; mini ~40 · basic ~60 · standard ~90). Response 429 khi vượt.
  • Concurrency: 1 job processing / user (+ có thể 1 queued). Hàng đợi đầy → QUEUE_FULL.
  • Trừ ký tự = độ dài text × credit_multiplier của giọng. Job fail → hoàn ký tự đã trừ.

Markup giọng

HạngHệ số1.000 ký tự text
Standard / free×11.000 credit
Clone×2.52.500 credit
Luxury×22.000 credit
Premium×33.000 credit
Platinum×44.000 credit

Gói dùng thử chỉ dùng một số giọng thư viện. Clone: encode miễn phí; Whisper chỉ trừ nếu không gửi ref_text.

Voices

GET/api/v1/voices

Danh sách giọng thư viện + clone của bạn. Dùng id làm voice_id. Xem credit_multiplier trước khi trừ.

curl https://hvoice.app/api/v1/voices \
  -H "Authorization: Bearer hv_live_…"
{
  "voices": [
    {
      "id": "YOUR_VOICE_ID",
      "name": "HN - Ngọc Huyền",
      "language": "vi",
      "gender": "female",
      "preview_url": "/library/audio/….mp3",
      "avatar_url": "/…",
      "beta_tier": "premium",
      "credit_multiplier": 3,
      "is_cloned": 0,
      "voice_source": "library"
    }
  ],
  "provider": "hvoice"
}
FieldMô tả
idTruyền vào voice_id khi synthesize.
credit_multiplierHệ số trừ credit (1–4).
beta_tierstandard / luxury / premium / platinum / clone / null.
preview_urlURL nghe thử (có thể relative).
is_cloned1 nếu giọng clone của bạn.
voice_sourcelibrary hoặc clone.

Alias: GET /voices/cloned · Preview: GET /voices/:voiceId/preview.

TTS Synthesize

POST/api/v1/tts/synthesize

Tạo job TTS bất đồng bộ. Poll job (hoặc webhook) đến completed.

FieldBắt buộcGhi chú
textCó*Tối đa 100.000 ký tự (*hoặc source_srt).
voice_idCóTừ GET /voices.
formatKhôngmp3 (mặc định) hoặc wav.
speedKhông0.5–2.0 (mặc định 1.0).
languageKhôngvd. vi, en.
titleKhôngTiêu đề job.
pitch / pitch_semitonesKhôngCao độ (semitone, khoảng −12…12).
volume / volume_pctKhôngÂm lượng %.
pause_settingsKhôngNgắt nghỉ theo dấu — xem Pause.
source_srtKhôngChuỗi SRT (thay / kèm text).

Response tạo job

{
  "job_id": "uuid-string",
  "status": "processing",
  "audio_url": null,
  "estimated_time": 60,
  "tokens_used": 120,
  "credit_multiplier": 3,
  "text_len": 40
}

Khi GPU đầy: status: "queued" kèm queue_position, eta_seconds. Credit trừ khi tạo job; hoàn nếu fail.

Pause settings

Giây nghỉ sau mỗi dấu (0–3). Schema đồng bộ Studio.

{
  "pause_settings": {
    "use_punctuation_pauses": true,
    "punctuation_pauses": {
      ".": 0.45,
      ",": 0.25,
      ":": 0.35,
      ";": 0.3,
      "?": 0.45,
      "!": 0.45,
      "\n": 0.6
    }
  }
}

Tắt: bỏ field hoặc use_punctuation_pauses: false.

Jobs & parts

MethodPathMô tả
GET/tts/jobsDanh sách (?limit=, max 50)
GET/tts/jobs/:jobIdPoll trạng thái / audio_url
POST/tts/jobs/:jobId/cancelHủy queued/processing
DELETE/tts/jobs/:jobIdXóa job
GET/tts/jobs/:jobId/parts.zipCác đoạn WAV (zip)
GET/tts/jobs/:jobId/transcript.srtTải SRT
POST/tts/jobs/:jobId/parts/:index/retryRetry một đoạn
POST/tts/jobs/:jobId/parts/retry-failedRetry mọi đoạn lỗi

Poll — processing

{
  "job_id": "uuid-string",
  "status": "processing",
  "audio_url": null,
  "progress": "Đang tạo · 2/11 đoạn xong · 45%",
  "estimated_time": 10
}

Poll — completed

{
  "job_id": "uuid-string",
  "status": "completed",
  "audio_url": "https://hvoice.app/audio/jobs/….mp3",
  "duration_seconds": 5.2,
  "text_len": 1200,
  "tokens_used": 3600
}

status: queued | processing | completed | failed | cancelled. Poll mỗi ~1.5–2s đến trạng thái kết thúc.

SRT → audio

POST/api/v1/tts/synthesize-srt

Upload file .srt → một job audio. Tổng ký tự lời cue ≤ 100.000 / request.

curl -X POST https://hvoice.app/api/v1/tts/synthesize-srt \
  -H "Authorization: Bearer hv_live_…" \
  -F "file=@subtitles.srt" \
  -F "voice_id=YOUR_VOICE_ID" \
  -F "format=mp3" \
  -F "language=vi" \
  -F "title=Phụ đề tập 1"

Hoặc JSON POST /tts/synthesize với source_srt.

Voice clone

POST/api/v1/voices/clone
  1. Gửi mẫu audio + ref_text → nhận voice_id.
  2. Đợi vài giây (enrolling); synthesize lần đầu dùng giọng mới.
  3. TTS với voice_id clone — trừ theo credit_multiplier (thường ×2.5).
  4. Xóa: DELETE /voices/:voiceId.
FieldBắt buộcGhi chú
audioCó*File mẫu (.wav/.mp3…). *Hoặc ref_audio.
nameCóTên hiển thị.
ref_textNên cóLời khớp audio (≥3 ký tự tránh trừ Whisper).
languageKhôngMặc định vi.
genderKhôngmale / female.
curl -X POST https://hvoice.app/api/v1/voices/clone \
  -H "Authorization: Bearer hv_live_…" \
  -F "name=Giọng thương hiệu" \
  -F "ref_text=Nội dung đọc đúng y như trong file audio mẫu." \
  -F "language=vi" \
  -F "audio=@/path/to/sample.wav"

Bạn phải có quyền hợp pháp với mẫu giọng — xem Điều khoản · Voice Clone.

Speech to text

POST/api/v1/stt
curl -X POST https://hvoice.app/api/v1/stt \
  -H "Authorization: Bearer hv_live_…" \
  -F "audio=@sample.mp3" \
  -F "lang=vi"

Audio/video tối đa ~40 MB. STT trừ ký tự theo thời lượng. Field: lang hoặc language. Alias nhận dạng mẫu: POST /voices/transcribe-ref.

Account

GET/api/v1/account/usage
curl https://hvoice.app/api/v1/account/usage \
  -H "Authorization: Bearer hv_live_…"

Trả số dư ký tự, ledger gần đây, và thông tin gói.

Webhooks

PUT/api/v1/account/webhook

Đăng ký URL HTTPS. Server POST JSON khi job completed hoặc failed.

curl -X PUT https://hvoice.app/api/v1/account/webhook \
  -H "Authorization: Bearer hv_live_…" \
  -H "Content-Type: application/json" \
  -d '{"webhook_url":"https://example.com/hooks/hvoice"}'

GET /account/webhook — xem URL hiện tại. Gửi webhook_url: "" để xoá.

Payload

{
  "event": "job.completed",
  "job_id": "uuid-string",
  "status": "completed",
  "audio_url": "https://hvoice.app/audio/jobs/….mp3",
  "error": null,
  "text_len": 1200,
  "tokens_used": 3600
}
  • Chỉ chấp nhận https://.
  • Timeout phía server ~8 giây. Header: User-Agent: hvoice-webhook/1, Content-Type: application/json.
  • Hiện chưa có chữ ký HMAC — xác thực bằng URL bí mật / firewall IP phía bạn.
  • event: job.completed hoặc job.failed.

Errors

Body lỗi thường dạng:

{
  "error": "Mô tả lỗi",
  "code": "QUEUE_FULL",
  "needed": 5000,
  "balance": 1200
}
HTTPcodeÝ nghĩa
401—Thiếu / sai Bearer; API key không hợp lệ; JWT hết hạn.
401—Cần đăng nhập (JWT) — gọi api-keys bằng hv_live_.
402PACK_EXPIREDGói hết hạn.
402—Không đủ ký tự / hết credit (needed, balance).
403NEED_PHONEKhông bắt buộc (legacy) — trial cấp ngay khi đăng nhập Google.
403TRIAL_VOICE_LOCKEDGói dùng thử không dùng được giọng này.
400TRIAL_JOB_TOO_LONGJob vượt giới hạn trial.
409USER_BUSYĐã có job processing/queued — đợi xong.
409QUEUE_FULLHàng đợi hệ thống đầy.
409—Tên API key trùng; job không ở trạng thái phù hợp.
429—Vượt rate limit theo gói.
500PARTS_INCOMPLETEThiếu đoạn sau retry — gọi retry-failed / retry từng đoạn.
503—ASR/Whisper chưa cấu hình; pool GPU trống.

Code samples

cURL

curl https://hvoice.app/api/v1/voices \
  -H "Authorization: Bearer hv_live_…"

curl -X POST https://hvoice.app/api/v1/tts/synthesize \
  -H "Authorization: Bearer hv_live_…" \
  -H "Content-Type: application/json" \
  -d '{
    "text": "Xin chào, đây là ví dụ chuyển văn bản thành giọng nói.",
    "voice_id": "YOUR_VOICE_ID",
    "format": "mp3",
    "speed": 1.0,
    "language": "vi"
  }'

curl https://hvoice.app/api/v1/tts/jobs/JOB_ID \
  -H "Authorization: Bearer hv_live_…"

Python — synthesize + poll

import time
import requests

API_KEY = "hv_live_…"
BASE = "https://hvoice.app/api/v1"
HEADERS = {
    "Authorization": f"Bearer {API_KEY}",
    "Content-Type": "application/json",
}

def synthesize(text, voice_id, pause_settings=None):
    payload = {
        "text": text,
        "voice_id": voice_id,
        "format": "mp3",
        "speed": 1.0,
        "language": "vi",
    }
    if pause_settings:
        payload["pause_settings"] = pause_settings
    r = requests.post(f"{BASE}/tts/synthesize", headers=HEADERS, json=payload, timeout=120)
    r.raise_for_status()
    return r.json()["job_id"]

def wait_job(job_id, timeout=600):
    t0 = time.time()
    while time.time() - t0 < timeout:
        r = requests.get(f"{BASE}/tts/jobs/{job_id}", headers=HEADERS, timeout=60)
        r.raise_for_status()
        d = r.json()
        if d.get("status") in ("completed", "failed", "cancelled"):
            return d
        time.sleep(2)
    raise TimeoutError(job_id)

job_id = synthesize(
    "Xin chào. Đây là ví dụ.",
    "YOUR_VOICE_ID",
    pause_settings={
        "use_punctuation_pauses": True,
        "punctuation_pauses": {".": 0.5, ",": 0.2, "\n": 0.6},
    },
)
print(wait_job(job_id))

JavaScript — synthesize + poll

const API_KEY = "hv_live_…";
const BASE = "https://hvoice.app/api/v1";

async function synthesize(text, voiceId, pauseSettings) {
  const body = {
    text,
    voice_id: voiceId,
    format: "mp3",
    speed: 1.0,
    language: "vi",
  };
  if (pauseSettings) body.pause_settings = pauseSettings;
  const res = await fetch(`${BASE}/tts/synthesize`, {
    method: "POST",
    headers: {
      Authorization: `Bearer ${API_KEY}`,
      "Content-Type": "application/json",
    },
    body: JSON.stringify(body),
  });
  if (!res.ok) throw new Error(await res.text());
  const { job_id } = await res.json();
  for (;;) {
    const j = await fetch(`${BASE}/tts/jobs/${job_id}`, {
      headers: { Authorization: `Bearer ${API_KEY}` },
    }).then((r) => r.json());
    if (["completed", "failed", "cancelled"].includes(j.status)) return j;
    await new Promise((r) => setTimeout(r, 2000));
  }
}

// synthesize("Xin chào, đây là ví dụ.", "YOUR_VOICE_ID")
//   .then((j) => console.log("Audio URL", j.audio_url))
//   .catch(console.error);