> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://developers.omicx.one/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://developers.omicx.one/_mcp/server.

# Chuyển giọng nói thành văn bản

POST https://api.omicx.one/stt
Content-Type: multipart/form-data

Tải lên một file audio và nhận về văn bản phiên âm (transcript), xử
lý đồng bộ.

**Backpressure:** hệ thống giới hạn tối đa 18 request STT xử lý đồng
thời — nếu đạt giới hạn, trả về `503 SERVICE_OVERLOADED` kèm
`Retry-After: 2`.

**Circuit breaker:** nếu nhà cung cấp lỗi 5 lần trong 10 lần gọi gần
nhất, circuit breaker mở trong 30 giây — trong lúc này mọi request
nhận `503 AI_PROVIDER_UNAVAILABLE` kèm `Retry-After` là số giây còn
lại.


Reference: https://developers.omicx.one/integrations/speech-to-text/transcribe-speech

## Request

### Headers

- `X-Api-Key` (string, required)
- `Idempotency-Key` (string, optional) — Key tuỳ chọn, tự sinh phía client, giúp request tính phí retry an toàn. Gửi lại nguyên vẹn cùng một giá trị khi retry: lần gọi thành công đầu tiên được tính phí một lần, kết quả được replay lại cho các lần gọi sau với cùng key (trong vòng 24 giờ). Gửi lại trong lúc request gốc vẫn đang xử lý sẽ nhận `409 DUPLICATE_REQUEST`.

### Body (multipart/form-data)

This endpoint expects a multipart form containing a file.

- `file` (file, required) — File âm thanh (mọi định dạng ffmpeg giải mã được — wav, mp3, m4a, ogg, flac...). Tối đa 25 MB.
- `languageCode` (string, optional) — Gợi ý ngôn ngữ, ví dụ `vi`.

## Response

### 200

Thành công.

- `trackingId` (string, required) — Mã tra cứu request, dùng để tra cứu lại qua `GET /stt/jobs`.
- `transcript` (string, required) — Văn bản phiên âm được từ audio.
- `audioDuration` (integer, required) — Độ dài audio đầu vào, làm tròn tới giây.
- `fileSizeBytes` (long, required) — Dung lượng file audio đầu vào (byte), đo phía server.
- `processingTimeMs` (long, required) — Tổng thời gian xử lý end-to-end (mili-giây).
- `inputAudioFileName` (string, optional, nullable) — Tên file gốc đã upload.
- `inputAudioUrl` (string, optional, nullable) — Link tạm thời (presigned URL) để nghe lại audio đã gửi ngay sau khi nhận response — hết hạn nhanh, file input tự động xoá (giọng nói là dữ liệu cá nhân — PII); không dùng để lưu trữ lâu dài.

## Errors

### 400 Bad Request Error

`VALIDATION_ERROR` — thiếu `file`, sai định dạng, hoặc vượt quá 25 MB.

- `error` (ErrorResponseError, optional)

### 401 Unauthorized Error

`API_KEY_REQUIRED` hoặc `API_KEY_INVALID`.

- `error` (ErrorResponseError, optional)

### 403 Forbidden Error

`INSUFFICIENT_SCOPE` — key hợp lệ nhưng thiếu scope `stt:execute`.

- `error` (ErrorResponseError, optional)

### 409 Conflict Error

`DUPLICATE_REQUEST` — retry khi request idempotent gốc vẫn đang xử lý.

- `error` (ErrorResponseError, optional)

### 429 Too Many Requests Error

`RATE_LIMIT_EXCEEDED` hoặc `QUOTA_EXCEEDED`.

- `error` (ErrorResponseError, optional)

### 502 Bad Gateway Error

`AI_PROVIDER_ERROR`.

- `error` (ErrorResponseError, optional)

### 503 Service Unavailable Error

`SERVICE_OVERLOADED` hoặc `AI_PROVIDER_UNAVAILABLE` — thử lại sau `Retry-After`.

- `error` (ErrorResponseError, optional)

## Types

### ErrorResponseError

- `code` (string, optional) — Nhóm lỗi (dạng máy đọc được), ổn định theo từng HTTP status.
- `subCode` (string, optional) — Mã cụ thể, ổn định — dùng để phân nhánh xử lý / dịch i18n. Hãy xử lý logic dựa trên `subCode`, không dựa vào chuỗi `message`.
- `message` (string, optional) — Mô tả cho con người đọc (dùng để debug), không nên dịch trực tiếp.
- `timestamp` (datetime, optional)
- `path` (string, optional) — Đường dẫn request đầy đủ.
- `details` (list of ErrorResponseErrorDetailsItems, optional) — Chỉ xuất hiện khi `code` là `VALIDATION_ERROR` — danh sách lỗi theo từng trường.

### ErrorResponseErrorDetailsItems

## Examples

**Request**

```json
{
  "file": "<file: recording.wav>",
  "languageCode": "vi"
}
```

**Response**

```json
{
  "trackingId": "018f8b3c-5e2a-7c1a-9b3e-3a2c1e4f5a6b",
  "transcript": "Xin chào, đây là bản ghi âm từ OMICX",
  "audioDuration": 5,
  "fileSizeBytes": 245760,
  "processingTimeMs": 842,
  "inputAudioFileName": "recording.wav",
  "inputAudioUrl": "https://storage.omicx.one/stt-input/xyz789.wav?X-Amz-Expires=300"
}
```

**SDK Code**

```python Ví dụ tải file ghi âm
import requests

url = "https://api.omicx.one/stt"

files = { "file": "open('recording.wav', 'rb')" }
payload = { "languageCode": "vi" }
headers = {"X-Api-Key": "omx_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"}

response = requests.post(url, data=payload, files=files, headers=headers)

print(response.json())
```

```javascript Ví dụ tải file ghi âm
const url = 'https://api.omicx.one/stt';
const form = new FormData();
form.append('file', 'recording.wav');
form.append('languageCode', 'vi');

const options = {method: 'POST', headers: {'X-Api-Key': 'omx_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'}};

options.body = form;

try {
  const response = await fetch(url, options);
  const data = await response.json();
  console.log(data);
} catch (error) {
  console.error(error);
}
```

```go Ví dụ tải file ghi âm
package main

import (
	"fmt"
	"strings"
	"net/http"
	"io"
)

func main() {

	url := "https://api.omicx.one/stt"

	payload := strings.NewReader("-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"file\"; filename=\"recording.wav\"\r\nContent-Type: application/octet-stream\r\n\r\n\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"languageCode\"\r\n\r\nvi\r\n-----011000010111000001101001--\r\n")

	req, _ := http.NewRequest("POST", url, payload)

	req.Header.Add("X-Api-Key", "omx_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx")

	res, _ := http.DefaultClient.Do(req)

	defer res.Body.Close()
	body, _ := io.ReadAll(res.Body)

	fmt.Println(res)
	fmt.Println(string(body))

}
```

```ruby Ví dụ tải file ghi âm
require 'uri'
require 'net/http'

url = URI("https://api.omicx.one/stt")

http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true

request = Net::HTTP::Post.new(url)
request["X-Api-Key"] = 'omx_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'
request.body = "-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"file\"; filename=\"recording.wav\"\r\nContent-Type: application/octet-stream\r\n\r\n\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"languageCode\"\r\n\r\nvi\r\n-----011000010111000001101001--\r\n"

response = http.request(request)
puts response.read_body
```

```java Ví dụ tải file ghi âm
import com.mashape.unirest.http.HttpResponse;
import com.mashape.unirest.http.Unirest;

HttpResponse<String> response = Unirest.post("https://api.omicx.one/stt")
  .header("X-Api-Key", "omx_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx")
  .body("-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"file\"; filename=\"recording.wav\"\r\nContent-Type: application/octet-stream\r\n\r\n\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"languageCode\"\r\n\r\nvi\r\n-----011000010111000001101001--\r\n")
  .asString();
```

```php Ví dụ tải file ghi âm
<?php
require_once('vendor/autoload.php');

$client = new \GuzzleHttp\Client();

$response = $client->request('POST', 'https://api.omicx.one/stt', [
  'multipart' => [
    [
        'name' => 'file',
        'filename' => 'recording.wav',
        'contents' => null
    ],
    [
        'name' => 'languageCode',
        'contents' => 'vi'
    ]
  ]
  'headers' => [
    'X-Api-Key' => 'omx_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx',
  ],
]);

echo $response->getBody();
```

```csharp Ví dụ tải file ghi âm
using RestSharp;

var client = new RestClient("https://api.omicx.one/stt");
var request = new RestRequest(Method.POST);
request.AddHeader("X-Api-Key", "omx_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx");
request.AddParameter("undefined", "-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"file\"; filename=\"recording.wav\"\r\nContent-Type: application/octet-stream\r\n\r\n\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"languageCode\"\r\n\r\nvi\r\n-----011000010111000001101001--\r\n", ParameterType.RequestBody);
IRestResponse response = client.Execute(request);
```

```swift Ví dụ tải file ghi âm
import Foundation

let headers = ["X-Api-Key": "omx_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"]
let parameters = [
  [
    "name": "file",
    "fileName": "recording.wav"
  ],
  [
    "name": "languageCode",
    "value": "vi"
  ]
]

let boundary = "---011000010111000001101001"

var body = ""
var error: NSError? = nil
for param in parameters {
  let paramName = param["name"]!
  body += "--\(boundary)\r\n"
  body += "Content-Disposition:form-data; name=\"\(paramName)\""
  if let filename = param["fileName"] {
    let contentType = param["content-type"]!
    let fileContent = String(contentsOfFile: filename, encoding: String.Encoding.utf8)
    if (error != nil) {
      print(error as Any)
    }
    body += "; filename=\"\(filename)\"\r\n"
    body += "Content-Type: \(contentType)\r\n\r\n"
    body += fileContent
  } else if let paramValue = param["value"] {
    body += "\r\n\r\n\(paramValue)"
  }
}

let request = NSMutableURLRequest(url: NSURL(string: "https://api.omicx.one/stt")! as URL,
                                        cachePolicy: .useProtocolCachePolicy,
                                    timeoutInterval: 10.0)
request.httpMethod = "POST"
request.allHTTPHeaderFields = headers
request.httpBody = postData as Data

let session = URLSession.shared
let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in
  if (error != nil) {
    print(error as Any)
  } else {
    let httpResponse = response as? HTTPURLResponse
    print(httpResponse)
  }
})

dataTask.resume()
```