> Dành cho AI agent: hướng dẫn từng bước để thiết lập — [`/docs/agents.md`](https://gate.joingonka.ai/docs/agents.md), chỉ mục tài liệu — [`/llms.txt`](https://gate.joingonka.ai/llms.txt).

# Tham chiếu API

Mọi thông tin về các yêu cầu gửi đến gateway: giao thức, địa chỉ, khóa và tham số. Dưới đây — streaming, gọi công cụ, suy luận, plugin và chi phí của yêu cầu trong phản hồi.

## Giao thức và địa chỉ

Gateway chấp nhận định dạng OpenAI và Anthropic. Base URL cho SDK OpenAI là `https://gate.joingonka.ai/v1`, cho SDK Anthropic là `https://gate.joingonka.ai`. Mọi giao thức dùng chung một key và một số dư: request ở bất kỳ định dạng nào đều đi cùng một đường.

| Phương thức và đường dẫn | Định dạng | Dùng để làm gì | Đặc điểm |
| --- | --- | --- | --- |
| `POST /v1/chat/completions` | OpenAI Chat Completions | Chat, agent, gọi công cụ | Đường chính: các định dạng khác đều được gateway chuyển về đây. |
| `POST /v1/messages` | Anthropic Messages | Claude Code và SDK Anthropic | Base URL không có `/v1`; các mô hình `claude-*` được thay bằng mô hình đề xuất; trường `max_tokens` là bắt buộc. |
| `POST /v1/responses` | OpenAI Responses | Codex CLI và các SDK OpenAI mới | Không trạng thái: hãy gửi toàn bộ lịch sử trong mỗi request. |
| `POST /v1/completions` | OpenAI Completions (legacy) | Tự động hoàn thành và sửa code trong editor | `suffix` được truyền cho mô hình như gợi ý; trong phản hồi có `logprobs: null`. |
| `POST /v1/embeddings` | OpenAI Embeddings | Biểu diễn vector của văn bản | Phản hồi `501`: mạng không có mô hình embedding. |

### Địa chỉ tham chiếu

Trả lời không cần key. Bảng mô hình với context và trạng thái — trong mục [Mô hình](https://gate.joingonka.ai/vi/docs/models).

| Phương thức và đường dẫn | Mô tả |
| --- | --- |
| `GET /v1/models` | Danh sách mô hình: context, giá, tham số được hỗ trợ — các trường theo định dạng OpenRouter. |
| `GET /v1/models/{model}` | Thông tin một mô hình; dấu gạch chéo trong id — giữ nguyên hoặc `%2F`. Mô hình ẩn hoặc không xác định — `404 model_not_found`. |
| `GET /v1/capabilities` | Khả năng của gateway: tham số, giao thức, plugin, trường chi phí và giới hạn (`limits`). |
| `GET /v1/plugins` | Plugin: id và tên. |
| `GET /v1/network-status` | Trạng thái các mô hình trong mạng: khả dụng, độ trễ, uptime. |
| `GET /v1/nodes` | Tóm tắt pool node: tổng số, đang hoạt động và cách ly. |
| `GET /v1/web-search/engines` | Web search có được bật không và các engine của nó đang ở trạng thái nào. |

### Anthropic Messages

- Base URL là `https://gate.joingonka.ai`: SDK sẽ tự thêm `/v1/messages`.
- Key nằm trong header `x-api-key` (cách SDK Anthropic gửi) hoặc `Authorization: Bearer`.
- Các mô hình `claude-*` được gateway thay bằng mô hình đề xuất (`MiniMaxAI/MiniMax-M2.7`); trong trường `model` của phản hồi vẫn giữ tên mà client đã gửi.
- `max_tokens` là bắt buộc, như trong API Anthropic; vượt trần của mô hình sẽ bị cắt.
- Stream — sự kiện Anthropic; trong khoảng nghỉ gateway gửi `event: ping`, lỗi đến qua sự kiện `event: error`.
- Phần suy luận của mô hình không xuất hiện trong phản hồi: không có block `thinking`.
- Công cụ tích hợp `web_search` được thực thi bởi plugin web search của gateway — xem mục [Plugin](https://gate.joingonka.ai/vi/docs/api#plugins).
- Không có đếm token (`/v1/messages/count_tokens`) — phản hồi `404`.

Claude Code dễ cấu hình nhất bằng trình cài đặt — [kết nối công cụ](https://gate.joingonka.ai/vi/docs#connect). Thủ công thì dùng biến môi trường; `ANTHROPIC_MODEL` ghim mô hình của mạng.

#### Claude Code

```bash
export ANTHROPIC_BASE_URL=https://gate.joingonka.ai
export ANTHROPIC_AUTH_TOKEN=$JOINGONKA_API_KEY
export ANTHROPIC_MODEL=MiniMaxAI/MiniMax-M2.7
claude
```

#### cURL

```bash
curl https://gate.joingonka.ai/v1/messages \
  -H "x-api-key: $JOINGONKA_API_KEY" \
  -H "anthropic-version: 2023-06-01" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "MiniMaxAI/MiniMax-M2.7",
    "max_tokens": 1024,
    "messages": [{"role": "user", "content": "What is Gonka?"}]
  }'
```

### OpenAI Responses

- Gateway không lưu phản hồi: hãy gửi toàn bộ lịch sử trong `input`. Các trường `previous_response_id` và `conversation` — lỗi `400` kèm mã.
- `store` được chấp nhận và không thay đổi gì.
- Công cụ: `function` và `web_search` — công cụ sau được thực thi bởi plugin web search. Các công cụ tích hợp khác bị gateway bỏ qua và request vẫn chạy mà không có chúng; yêu cầu công cụ như vậy qua `tool_choice` — lỗi `400`.
- Các phần `input_image` và `input_file` — lỗi `400`: mô hình trong mạng chỉ làm việc với văn bản.
- Các địa chỉ trạng thái (`GET /v1/responses/{id}`, `DELETE /v1/responses/{id}`, `GET /v1/responses/{id}/input_items`, `POST /v1/responses/{id}/cancel`, `POST /v1/responses/compact`) trả về `404` kèm mã — gateway không lưu phản hồi.
- Codex CLI: đặt id provider của bạn trong `model_provider` (không phải openai) — khi đó Codex tự nén lịch sử, không cần `/v1/responses/compact`.

### Legacy Completions

- `prompt` — chuỗi hoặc mảng một chuỗi; phản hồi nằm trong `choices[].text`. Nhiều prompt hoặc token thay vì văn bản — lỗi `400`.
- `suffix` được truyền cho mô hình như gợi ý trong prompt: mạng không có khả năng điền giữa thực sự.
- Trong phản hồi có `logprobs: null`; `best_of` bị bỏ qua; `echo` hoạt động.

## Key và xác thực

Key được truyền trong header `Authorization: Bearer jg-…` hoặc `x-api-key: jg-…` — trên mọi địa chỉ. Key được tạo sau khi đăng ký tại trang [gate.joingonka.ai/keys](https://gate.joingonka.ai/keys).

| Tiền tố | Key | Request tới mô hình |
| --- | --- | --- |
| `jg-` | Key tài khoản thông thường | có |
| `gc-` | Key con: giới hạn riêng, chi phí trừ từ số dư của chủ sở hữu | có |
| `gm-` | Key quản lý: chỉ quản lý các key con | không — `403 forbidden` |

- Mỗi key trong trang cá nhân có thể đặt giới hạn chi tiêu theo ngày, tháng và tổng; vượt quá — `402 child_key_limit_exceeded`.
- Số request mỗi phút cho mỗi key bị giới hạn — giá trị trong mục [Giới hạn](https://gate.joingonka.ai/vi/docs/errors#limits).
- Không có key thì chỉ demo chat trên site hoạt động: request không key từ code của bạn sẽ nhận `402` với `is_demo`.
- Key chỉ được quản lý trong trang cá nhân: `/api/keys` với API key không khả dụng. Số dư và chi tiêu theo key — [API tài khoản](https://gate.joingonka.ai/vi/docs/billing#account-api).

> Khóa là bí mật: đừng lưu trong repository hay code frontend, hãy truyền qua biến môi trường.

## Ví dụ

Cùng một yêu cầu trong bốn SDK. Model là model khuyến nghị (`MiniMaxAI/MiniMax-M2.7`), khóa lấy từ biến môi trường `JOINGONKA_API_KEY`.

### Python

```python
import os
from openai import OpenAI

client = OpenAI(
    base_url="https://gate.joingonka.ai/v1",
    api_key=os.environ["JOINGONKA_API_KEY"],
)

response = client.chat.completions.create(
    model="MiniMaxAI/MiniMax-M2.7",
    messages=[{"role": "user", "content": "What is Gonka?"}],
)
print(response.choices[0].message.content)
```

### TypeScript

```typescript
import OpenAI from "openai";

const client = new OpenAI({
  baseURL: "https://gate.joingonka.ai/v1",
  apiKey: process.env.JOINGONKA_API_KEY,
});

const response = await client.chat.completions.create({
  model: "MiniMaxAI/MiniMax-M2.7",
  messages: [{ role: "user", content: "What is Gonka?" }],
});
console.log(response.choices[0].message.content);
```

### cURL

```bash
curl https://gate.joingonka.ai/v1/chat/completions \
  -H "Authorization: Bearer $JOINGONKA_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "MiniMaxAI/MiniMax-M2.7",
    "messages": [{"role": "user", "content": "What is Gonka?"}]
  }'
```

### Anthropic SDK

```python
import os
import anthropic

client = anthropic.Anthropic(
    base_url="https://gate.joingonka.ai",
    api_key=os.environ["JOINGONKA_API_KEY"],
)

message = client.messages.create(
    model="MiniMaxAI/MiniMax-M2.7",
    max_tokens=1024,
    messages=[{"role": "user", "content": "What is Gonka?"}],
)
print(message.content[0].text)
```

### Phản hồi dạng stream

#### Python

```python
import os
from openai import OpenAI

client = OpenAI(base_url="https://gate.joingonka.ai/v1", api_key=os.environ["JOINGONKA_API_KEY"])

stream = client.chat.completions.create(
    model="MiniMaxAI/MiniMax-M2.7",
    messages=[{"role": "user", "content": "What is Gonka?"}],
    stream=True,
)
for chunk in stream:
    if chunk.choices and chunk.choices[0].delta.content:
        print(chunk.choices[0].delta.content, end="", flush=True)
```

#### TypeScript

```typescript
import OpenAI from "openai";

const client = new OpenAI({ baseURL: "https://gate.joingonka.ai/v1", apiKey: process.env.JOINGONKA_API_KEY });

const stream = await client.chat.completions.create({
  model: "MiniMaxAI/MiniMax-M2.7",
  messages: [{ role: "user", content: "What is Gonka?" }],
  stream: true,
});
for await (const chunk of stream) {
  process.stdout.write(chunk.choices[0]?.delta?.content ?? "");
}
```

#### cURL

```bash
curl -N https://gate.joingonka.ai/v1/chat/completions \
  -H "Authorization: Bearer $JOINGONKA_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "MiniMaxAI/MiniMax-M2.7",
    "messages": [{"role": "user", "content": "What is Gonka?"}],
    "stream": true
  }'
```

#### Anthropic SDK

```python
import os
import anthropic

client = anthropic.Anthropic(base_url="https://gate.joingonka.ai", api_key=os.environ["JOINGONKA_API_KEY"])

with client.messages.stream(
    model="MiniMaxAI/MiniMax-M2.7",
    max_tokens=1024,
    messages=[{"role": "user", "content": "What is Gonka?"}],
) as stream:
    for text in stream.text_stream:
        print(text, end="", flush=True)
```

## Tham số yêu cầu

Các tham số `POST /v1/chat/completions` mà gateway tự đảm bảo — danh sách `supported_parameters` trong phản hồi capabilities:

| Tham số | Mô tả |
| --- | --- |
| `temperature` | Độ ngẫu nhiên của câu trả lời: càng cao càng đa dạng. |
| `top_p` | Chọn token theo tổng xác suất. |
| `top_k` | Chọn từ k token có xác suất cao nhất. |
| `min_p` | Cắt bỏ các token ít khả năng so với token khả năng nhất. |
| `frequency_penalty` | Phạt việc lặp lại thường xuyên. |
| `presence_penalty` | Phạt các token đã xuất hiện trước đó. |
| `repetition_penalty` | Hệ số chống lặp lại. |
| `stop` | Các chuỗi mà tại đó quá trình sinh dừng lại. |
| `seed` | Seed để tái tạo kết quả. |
| `max_tokens` | Giới hạn token của phản hồi; vượt trần model sẽ bị cắt về trần. |
| `max_completion_tokens` | Tên khác của `max_tokens`: gateway chuyển giá trị vào đó. |
| `tools` | Các hàm mà model có thể gọi, theo định dạng OpenAI. |
| `tool_choice` | Có gọi hàm không: để model chọn, không bao giờ, bắt buộc hoặc một hàm cụ thể. |
| `response_format` | Phản hồi có cấu trúc: `json_object` hoặc `json_schema`. |

- Không có `temperature`, gateway thay bằng `0.7`.
- Không có `max_tokens`, gateway thay bằng mặc định của model: không stream thì ngắn hơn, khi stream thì bằng trần model. Số cụ thể theo model — trong mục [Giới hạn](https://gate.joingonka.ai/vi/docs/errors#limits).

### Được truyền nguyên vẹn vào mạng

`reasoning_effort`, `reasoning`, `enable_thinking`, `chat_template_kwargs`, `thinking_token_budget`, `min_tokens`, `logit_bias`, `n`, `parallel_tool_calls`, `extra_body`. Gateway không kiểm tra chúng: giá trị ngoài danh sách của mạng sẽ gây lỗi `400` với type `api_error`.

### Không được truyền vào mạng

Các trường còn lại gateway nhận nhưng không truyền vào mạng — ví dụ `user`, `metadata`, `store`, `logprobs`, `top_logprobs`, `thinking`, `stream_options`, `web_search_options`. `usage` trong stream luôn được gửi.

## Streaming

- `stream: true` — phản hồi dưới dạng sự kiện SSE; sự kiện cuối là `data: [DONE]`.
- Trước khi kết thúc luôn có một chunk chứa `usage` — kể cả khi không có `stream_options`.
- Trong lúc tạm dừng, gateway cứ mỗi 15 giây gửi comment `: keep-alive` — các SSE client sẽ bỏ qua.
- Khi stream chưa mở, lỗi đến dưới dạng mã phản hồi thông thường; sau khi mở, lỗi đến dưới dạng chunk `joingonka-error`.
- Trong `delta.tool_calls` — một lệnh gọi mỗi chunk: các lệnh gọi bị mạng gộp lại sẽ được gateway tách ra.

```text
data: {"object":"chat.completion.chunk","model":"MiniMaxAI/MiniMax-M2.7","choices":[{"index":0,"delta":{"content":"Hi"},"finish_reason":null}]}

data: {"object":"chat.completion.chunk","model":"MiniMaxAI/MiniMax-M2.7","choices":[{"index":0,"delta":{},"finish_reason":"stop"}]}

data: {"object":"chat.completion.chunk","model":"MiniMaxAI/MiniMax-M2.7","choices":[],"usage":{"prompt_tokens":12,"completion_tokens":2,"total_tokens":14}}

data: [DONE]
```

### Các chunk dịch vụ của gateway

Có thể nhận biết chúng qua trường `id`:

| `id` | Khi nào và bên trong có gì |
| --- | --- |
| `joingonka-error` | Sự cố sau khi mở stream: trường `error`, sau đó ngắt kết nối không có `[DONE]`. |
| `joingonka-stream-stalled` | Mạng im lặng lâu hơn khoảng tạm dừng cho phép: stream đóng với `finish_reason: stop`. |
| `joingonka-stream-unfinished` | Mạng ngắt quá trình sinh: `finish_reason: length` — hãy tiếp tục bằng yêu cầu tiếp theo. |
| `joingonka-citations` | Nguồn tìm kiếm web trong `delta.annotations` — trước khi kết thúc. |
| `joingonka-meta` | Chi phí và thời gian — chỉ khi có header `x-joingonka-meta: 1`. |

### Stream trong các giao thức khác

- Anthropic Messages: sự kiện từ `message_start` đến `message_stop`, khi tạm dừng là `event: ping`, sự cố là `event: error`.
- OpenAI Responses: sự kiện `response.*`, sự cố là `response.failed`.
- Legacy Completions: khi sự cố — `data: {"error": …}`, sau đó `[DONE]`.

## Gọi công cụ

- Định dạng OpenAI: `tools` và `tool_choice`. Định dạng cũ `functions` và `function_call` cũng được chấp nhận — phản hồi sẽ ở đúng định dạng đó.
- Trong stream — một lệnh gọi mỗi chunk: các client chỉ đọc phần tử đầu tiên sẽ không mất lệnh gọi nào.
- Lịch sử mà mạng sẽ trả lỗi `400` sẽ được gateway sửa: role `developer` thành `system`, các id lệnh gọi trống hoặc trùng lặp được cấp id duy nhất, `arguments` dạng object chuyển thành chuỗi JSON, `type` bị thiếu được bổ sung, lệnh gọi không có tên bị loại bỏ cùng với kết quả.
- Lệnh gọi mà model viết dưới dạng markup trong văn bản sẽ được gateway chuyển vào `tool_calls`; các lệnh gọi giả trong phản hồi của yêu cầu không có công cụ sẽ bị loại bỏ.
- Quá trình sinh bị ngắt giữa chừng phần tham số — sẽ nhận `finish_reason: length`, không phải `tool_calls`: hãy tăng giới hạn phản hồi.

```json
{
  "model": "MiniMaxAI/MiniMax-M2.7",
  "messages": [{"role": "user", "content": "What is the weather in Paris?"}],
  "tools": [{
    "type": "function",
    "function": {
      "name": "get_weather",
      "description": "Current weather for a city",
      "parameters": {
        "type": "object",
        "properties": {"city": {"type": "string"}},
        "required": ["city"]
      }
    }
  }]
}
```

### Giới hạn của JSON-Schema

Schema của công cụ và `response_format` được mạng biên dịch thành grammar; biểu thức chính quy dùng engine RE2. Gateway đưa schema về dạng mà mạng chấp nhận:

- `$ref` được mở rộng tại chỗ, các mục `$defs` và `definitions` bị xóa; tham chiếu đệ quy trở thành schema không có ràng buộc.
- `pattern` chứa các cấu trúc không có trong RE2 (lookahead, lookbehind, backreference, atomic group, quantifier siêu tham lam) sẽ bị gỡ bỏ; các lần lặp trên 1000 bị rút xuống 1000.
- `anyOf` và `oneOf` từ các hằng số được gộp thành `enum`; nếu số nhánh không gộp được nhiều hơn 16, phần hợp nhất sẽ bị gỡ bỏ.

> Schema có thể trở nên lỏng hơn so với ban đầu — hãy tự kiểm tra tham số lệnh gọi ở phía bạn.

## Phản hồi có cấu trúc

`response_format`: `{"type": "json_object"}` — phản hồi là JSON hợp lệ, `{"type": "json_schema", "json_schema": {"name": …, "schema": …}}` — theo schema của bạn với các giới hạn ở trên. JSON bị cắt mà không stream sẽ được plugin `response-healing` sửa.

```json
{
  "model": "MiniMaxAI/MiniMax-M2.7",
  "messages": [{"role": "user", "content": "Name three planets."}],
  "response_format": {
    "type": "json_schema",
    "json_schema": {
      "name": "planets",
      "schema": {
        "type": "object",
        "properties": {"planets": {"type": "array", "items": {"type": "string"}}},
        "required": ["planets"]
      }
    }
  }
}
```

## Suy luận

- Suy luận của model đến tách biệt với phản hồi: `message.reasoning_content`, trong stream là `delta.reasoning_content`. Trường `reasoning` được gateway đổi tên thành định dạng này.
- Suy luận tiêu tốn `max_tokens`: với giới hạn nhỏ, phản hồi bị ngắt (`finish_reason: length`) ngay cả trước phần văn bản.
- Nếu không có văn bản phản hồi mà có suy luận, gateway chuyển chúng vào `content` — trừ các phản hồi có gọi công cụ.
- `reasoning_effort` và `reasoning.effort` được truyền vào mạng. Nếu model chỉ có hai chế độ, gateway đưa giá trị về chúng: `none` và `minimal` → `low`, các mức cao hơn → suy luận mặc định.
- Nếu node từ chối giá trị, gateway hạ nó xuống (`max` và `xhigh` → `high`, `minimal` → `low`, nếu không thì gỡ trường) và gửi lại yêu cầu.
- Trong `/v1/messages`, suy luận không được truyền — không có block `thinking`.

## Plugin

Plugin được bật bằng trường `plugins` — mảng chuỗi hoặc object có options. Danh sách — `GET /v1/plugins`.

| Plugin | Mô tả | Điều kiện |
| --- | --- | --- |
| `response-healing` | Sửa JSON bị cắt trong phản hồi của model. | Chỉ khi không stream và phản hồi bắt đầu bằng `{` hoặc `[`. |
| `privacy-sanitization` | Che trong tin nhắn văn bản: email, IPv4, số thẻ, JWT, khóa hex 64 ký tự và khóa dạng `sk-…`, `gw_…`, `gm-…`, `Bearer …`. | Chế độ — trường `privacy_mode`: `redact` (mặc định) hoặc `tokenize`. |
| `file-parser` | Trích xuất văn bản từ PDF. | Nếu toàn bộ nội dung tin nhắn là PDF dạng base64: `data:application/pdf;base64,…` hoặc không có tiền tố. |
| `web` | Tìm kiếm web: kết quả được trộn vào yêu cầu, phản hồi nhận liên kết nguồn. | Kết hợp với `privacy-sanitization` — lỗi `400`. |

### Tìm kiếm web

- Tùy chọn: `max_results` — từ 1 đến 10, mặc định 5; `engine` — gợi ý engine; `search_prompt` — văn bản riêng trước kết quả; `enabled: false` — tắt tìm kiếm.
- Nguồn — trong `message.annotations[].url_citation`; trong stream — chunk `joingonka-citations` trước khi kết thúc.
- `mode: "agent"` — model tự quyết định có tìm kiếm hay không và tìm gì; `max_searches` — từ 1 đến 5, mặc định 3.
- Thanh toán: ở chế độ thông thường — chỉ tính token (kết quả tìm kiếm được tính vào token đầu vào); ở chế độ agent — token của tất cả các bước cộng thêm 1000 nGNK cho mỗi lần tìm kiếm được thực hiện (`x_joingonka.web_search_surcharge_ngonka`).
- Trong Anthropic Messages và OpenAI Responses, công cụ tích hợp `web_search` thực thi chính plugin này ở chế độ agent.

#### plugins: web

```json
{
  "model": "MiniMaxAI/MiniMax-M2.7",
  "messages": [{"role": "user", "content": "What is new in the Gonka network?"}],
  "plugins": [{"id": "web", "max_results": 5}]
}
```

#### mode: agent

```json
{
  "model": "MiniMaxAI/MiniMax-M2.7",
  "messages": [{"role": "user", "content": "What is new in the Gonka network?"}],
  "plugins": [{"id": "web", "mode": "agent", "max_searches": 3}]
}
```

## Chi phí và các trường hệ thống

Phản hồi không streaming mang theo chi phí của yêu cầu trong `usage`:

| Trường | Mô tả |
| --- | --- |
| `usage.cost_gnk` | Chi phí yêu cầu tính bằng GNK |
| `usage.platform_fee_gnk` | Trong đó — phí nền tảng, GNK |
| `usage.total_cost_gnk` | Tổng số tiền trừ, tính bằng GNK |
| `usage.total_cost_usd` | Tổng số tiền tính bằng USD theo tỷ giá GNK hiện tại |

- Trong stream, `usage` chỉ chứa token; chi phí nằm trong chunk `joingonka-meta`.
- Với header `x-joingonka-meta: 1`, phản hồi `POST /v1/chat/completions` nhận được block `x_joingonka`: chi phí (`cost_ngonka`), số dư sau khi trừ (`balance_ngonka`, chỉ khi không streaming) và thời gian (`ttft_ms`). Các giao thức khác không trả về block này.
- `x-request-id` — mã định danh yêu cầu: hãy gửi kèm khi liên hệ hỗ trợ.
- `Retry-After` đi kèm với `429`: cần chờ bấy nhiêu giây trước khi thử lại.
- Các header `X-Title` và `HTTP-Referer` (giống OpenRouter) giúp gateway nhận diện ứng dụng của bạn; nội dung của chúng không được lưu lại.

## Giới hạn

- Hình ảnh: các phần `image_url` được thay bằng văn bản giữ chỗ — model không nhìn thấy hình (`vision: false` trong phần khả năng).
- Từ trình duyệt, API chỉ truy cập được từ các tên miền JoinGonka (kiểm tra `Origin`): hãy gọi nó từ server của bạn, đừng đặt key ở frontend.
- Embeddings: `POST /v1/embeddings` trả về `501` — mạng không có model embedding.
- Mã lỗi, giới hạn và timeout — trong mục [Lỗi và giới hạn](https://gate.joingonka.ai/vi/docs/errors).
