> Untuk agen AI: panduan pengaturan langkah demi langkah — [`/docs/agents.md`](https://gate.joingonka.ai/docs/agents.md), indeks dokumentasi — [`/llms.txt`](https://gate.joingonka.ai/llms.txt).

# Referensi API

Segala hal tentang permintaan ke gateway: protokol, alamat, kunci, dan parameter. Di bawah ini — streaming, pemanggilan tool, penalaran, plugin, dan biaya permintaan di dalam respons.

## Protokol dan alamat

Gateway menerima format OpenAI dan Anthropic. Alamat dasar untuk SDK OpenAI adalah `https://gate.joingonka.ai/v1`, untuk SDK Anthropic — `https://gate.joingonka.ai`. Semua protokol berjalan dengan satu kunci dan satu saldo: permintaan dalam format apa pun menempuh jalur yang sama.

| Metode dan jalur | Format | Untuk apa | Keistimewaan |
| --- | --- | --- | --- |
| `POST /v1/chat/completions` | OpenAI Chat Completions | Obrolan, agen, pemanggilan alat | Jalur utama: gateway mengonversi format lain ke jalur ini. |
| `POST /v1/messages` | Anthropic Messages | Claude Code dan SDK Anthropic | Alamat dasar tanpa `/v1`; model `claude-*` digantikan oleh model yang direkomendasikan; field `max_tokens` wajib diisi. |
| `POST /v1/responses` | OpenAI Responses | Codex CLI dan SDK OpenAI terbaru | Tanpa state: kirim seluruh riwayat di setiap permintaan. |
| `POST /v1/completions` | OpenAI Completions (legacy) | Pelengkapan otomatis dan koreksi kode di editor | `suffix` dikirim ke model sebagai petunjuk; respons berisi `logprobs: null`. |
| `POST /v1/embeddings` | OpenAI Embeddings | Representasi vektor teks | Respons `501`: tidak ada model embedding di jaringan. |

### Alamat referensi

Merespons tanpa kunci. Tabel model dengan konteks dan status ada di bagian [Model](https://gate.joingonka.ai/id/docs/models).

| Metode dan jalur | Deskripsi |
| --- | --- |
| `GET /v1/models` | Daftar model: konteks, harga, parameter yang didukung — field dalam format OpenRouter. |
| `GET /v1/models/{model}` | Kartu satu model; garis miring dalam id bisa ditulis apa adanya atau `%2F`. Model tersembunyi atau tidak dikenal — `404 model_not_found`. |
| `GET /v1/capabilities` | Kemampuan gateway: parameter, protokol, plugin, field biaya, dan batas (`limits`). |
| `GET /v1/plugins` | Plugin: id dan nama. |
| `GET /v1/network-status` | Status model jaringan: ketersediaan, latensi, uptime. |
| `GET /v1/nodes` | Ringkasan pool node: total, aktif, dan karantina. |
| `GET /v1/web-search/engines` | Apakah pencarian web aktif dan bagaimana status mesinnya. |

### Anthropic Messages

- Alamat dasar — `https://gate.joingonka.ai`: SDK akan menambahkan `/v1/messages` sendiri.
- Kunci — di header `x-api-key` (seperti yang dikirim SDK Anthropic) atau `Authorization: Bearer`.
- Model `claude-*` digantikan gateway dengan model yang direkomendasikan (`MiniMaxAI/MiniMax-M2.7`); di field `model` respons tetap tersimpan nama yang dikirim klien.
- `max_tokens` wajib diisi, seperti di API Anthropic; melebihi plafon model — akan dipotong.
- Stream — event Anthropic; saat jeda gateway mengirim `event: ping`, kegagalan datang sebagai event `event: error`.
- Penalaran model tidak muncul di respons: tidak ada blok `thinking`.
- Alat bawaan `web_search` dijalankan oleh plugin pencarian web gateway — lihat bagian [Plugin](https://gate.joingonka.ai/id/docs/api#plugins).
- Tidak ada penghitungan token (`/v1/messages/count_tokens`) — respons `404`.

Claude Code lebih mudah dikonfigurasi dengan installer — [koneksi alat](https://gate.joingonka.ai/id/docs#connect). Secara manual — lewat variabel lingkungan; `ANTHROPIC_MODEL` mengunci model jaringan.

#### 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 tidak menyimpan respons: kirim seluruh riwayat di `input`. Field `previous_response_id` dan `conversation` — error `400` dengan kode.
- `store` diterima dan tidak mengubah apa pun.
- Alat: `function` dan `web_search` — yang terakhir dijalankan oleh plugin pencarian web. Alat bawaan lain dilewati gateway dan permintaan dijalankan tanpanya; meminta alat seperti itu lewat `tool_choice` — error `400`.
- Bagian `input_image` dan `input_file` — error `400`: model jaringan bekerja dengan teks.
- Alamat state (`GET /v1/responses/{id}`, `DELETE /v1/responses/{id}`, `GET /v1/responses/{id}/input_items`, `POST /v1/responses/{id}/cancel`, `POST /v1/responses/compact`) merespons `404` dengan kode — gateway tidak menyimpan respons.
- Codex CLI: tentukan id provider Anda sendiri di `model_provider` (bukan openai) — maka Codex memadatkan riwayat sendiri, tanpa `/v1/responses/compact`.

### Legacy Completions

- `prompt` — string atau array berisi satu string; respons ada di `choices[].text`. Beberapa prompt atau token alih-alih teks — error `400`.
- `suffix` dikirim ke model sebagai petunjuk dalam prompt: jaringan tidak melakukan pengisian tengah (fill-in-the-middle) yang sebenarnya.
- Respons berisi `logprobs: null`; `best_of` diabaikan; `echo` berfungsi.

## Kunci dan otorisasi

Kunci dikirim melalui header `Authorization: Bearer jg-…` atau `x-api-key: jg-…` — di semua alamat. Kunci dibuat setelah mendaftar di halaman [gate.joingonka.ai/keys](https://gate.joingonka.ai/keys).

| Prefiks | Kunci | Permintaan ke model |
| --- | --- | --- |
| `jg-` | Kunci akun biasa | ya |
| `gc-` | Kunci anak: batas sendiri, pengeluaran diambil dari saldo pemilik | ya |
| `gm-` | Kunci pengelola: hanya untuk mengelola kunci anak | tidak — `403 forbidden` |

- Setiap kunci di dashboard dapat diberi batas pengeluaran harian, bulanan, dan total; jika terlampaui — `402 child_key_limit_exceeded`.
- Jumlah permintaan per menit per kunci dibatasi — nilainya ada di bagian [Batas](https://gate.joingonka.ai/id/docs/errors#limits).
- Tanpa kunci, hanya chat demo di situs yang berfungsi: permintaan tanpa kunci dari kode Anda akan mendapat `402` dengan `is_demo`.
- Kunci hanya dikelola di dashboard: `/api/keys` dengan kunci API tidak dapat diakses. Saldo dan pengeluaran per kunci — [API akun](https://gate.joingonka.ai/id/docs/billing#account-api).

> Kunci bersifat rahasia: jangan simpan di repositori atau kode front-end, kirimkan melalui variabel lingkungan.

## Contoh

Satu permintaan yang sama di empat SDK. Model — yang direkomendasikan (`MiniMaxAI/MiniMax-M2.7`), kunci — dari variabel lingkungan `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)
```

### Respons streaming

#### 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)
```

## Parameter permintaan

Parameter `POST /v1/chat/completions` yang dijamin sendiri oleh gateway — daftar `supported_parameters` pada respons kapabilitas:

| Parameter | Deskripsi |
| --- | --- |
| `temperature` | Keacakan respons: makin tinggi, makin beragam. |
| `top_p` | Pemilihan token berdasarkan probabilitas kumulatif. |
| `top_k` | Pemilihan dari k token paling mungkin. |
| `min_p` | Pemotongan token berprobabilitas rendah relatif terhadap yang paling mungkin. |
| `frequency_penalty` | Penalti untuk pengulangan yang sering. |
| `presence_penalty` | Penalti untuk token yang sudah muncul. |
| `repetition_penalty` | Pengali anti-pengulangan. |
| `stop` | String tempat generasi berhenti. |
| `seed` | Seed untuk reproduksibilitas. |
| `max_tokens` | Batas token respons; melebihi plafon model — dipangkas ke plafon. |
| `max_completion_tokens` | Nama lain untuk `max_tokens`: gateway memindahkan nilainya ke sana. |
| `tools` | Fungsi yang dapat dipanggil model, dalam format OpenAI. |
| `tool_choice` | Apakah memanggil fungsi: terserah model, tidak pernah, wajib, atau fungsi tertentu. |
| `response_format` | Respons terstruktur: `json_object` atau `json_schema`. |

- Tanpa `temperature`, gateway menyisipkan `0.7`.
- Tanpa `max_tokens`, gateway menyisipkan default model: tanpa stream — lebih pendek, dalam stream — plafon model. Angka per model — di bagian [Batas](https://gate.joingonka.ai/id/docs/errors#limits).

### Diteruskan ke jaringan apa adanya

`reasoning_effort`, `reasoning`, `enable_thinking`, `chat_template_kwargs`, `thinking_token_budget`, `min_tokens`, `logit_bias`, `n`, `parallel_tool_calls`, `extra_body`. Gateway tidak memeriksanya: nilai di luar daftar jaringan — error `400` dengan tipe `api_error`.

### Tidak diteruskan ke jaringan

Kolom lainnya diterima gateway tetapi tidak diteruskan ke jaringan — misalnya `user`, `metadata`, `store`, `logprobs`, `top_logprobs`, `thinking`, `stream_options`, `web_search_options`. `usage` dalam stream selalu datang.

## Streaming

- `stream: true` — respons berupa event SSE; event terakhir — `data: [DONE]`.
- Sebelum selesai, sebuah chunk dengan `usage` datang — selalu, bahkan tanpa `stream_options`.
- Saat jeda, gateway setiap 15 dtk mengirim komentar `: keep-alive` — klien SSE melewatinya.
- Selama stream belum dibuka, penolakan datang lewat kode respons biasa; setelah dibuka — lewat chunk `joingonka-error`.
- Dalam `delta.tool_calls` — satu panggilan per chunk: panggilan yang digabung jaringan dipotong oleh gateway.

```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]
```

### Chunk layanan gateway

Bisa dikenali dari kolom `id`:

| `id` | Kapan dan apa isinya |
| --- | --- |
| `joingonka-error` | Gagal setelah stream dibuka: kolom `error`, lalu terputus tanpa `[DONE]`. |
| `joingonka-stream-stalled` | Jaringan diam lebih lama dari jeda yang diizinkan: stream ditutup dengan `finish_reason: stop`. |
| `joingonka-stream-unfinished` | Jaringan menghentikan generasi: `finish_reason: length` — lanjutkan dengan permintaan berikutnya. |
| `joingonka-citations` | Sumber pencarian web di `delta.annotations` — sebelum selesai. |
| `joingonka-meta` | Biaya dan timing — hanya dengan header `x-joingonka-meta: 1`. |

### Stream di protokol lain

- Anthropic Messages: event dari `message_start` hingga `message_stop`, saat jeda `event: ping`, gagal — `event: error`.
- OpenAI Responses: event `response.*`, gagal — `response.failed`.
- Legacy Completions: saat gagal — `data: {"error": …}`, lalu `[DONE]`.

## Pemanggilan alat

- Format OpenAI: `tools` dan `tool_choice`. Format lama `functions` dan `function_call` juga diterima — respons akan datang dalam format yang sama.
- Dalam stream — satu panggilan per chunk: klien yang hanya membaca elemen pertama tidak kehilangan panggilan.
- Riwayat yang akan membuat jaringan mengembalikan error `400` diperbaiki gateway: peran `developer` menjadi `system`, id panggilan yang kosong dan duplikat mendapat id unik, `arguments` berupa objek diubah menjadi string JSON, `type` yang hilang dilengkapi, panggilan tanpa nama dihapus bersama hasilnya.
- Panggilan yang ditulis model sebagai markup dalam teks dipindahkan gateway ke `tool_calls`; panggilan palsu dalam respons terhadap permintaan tanpa alat akan dihapus.
- Generasi terputus di tengah argumen — yang datang adalah `finish_reason: length`, bukan `tool_calls`: tingkatkan batas respons.

```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"]
      }
    }
  }]
}
```

### Batasan JSON-Schema

Skema alat dan `response_format` dikompilasi jaringan menjadi tata bahasa; ekspresi reguler — oleh mesin RE2. Gateway menyesuaikan skema ke bentuk yang diterima jaringan:

- `$ref` dijabarkan di tempat, bagian `$defs` dan `definitions` dihapus; referensi rekursif menjadi skema tanpa batasan.
- `pattern` dengan konstruksi yang tidak ada di RE2 (lookahead dan lookbehind, backreference, grup atomik, kuantifier posesif) dihapus; pengulangan lebih dari 1000 dipangkas menjadi 1000.
- `anyOf` dan `oneOf` dari konstanta dilipat menjadi `enum`; jika cabang yang tidak bisa dilipat lebih dari 16, gabungan dihapus.

> Skema bisa menjadi lebih longgar dari aslinya — validasi argumen panggilan di sisi Anda.

## Respons terstruktur

`response_format`: `{"type": "json_object"}` — respons JSON valid, `{"type": "json_schema", "json_schema": {"name": …, "schema": …}}` — sesuai skema Anda dengan batasan di atas. JSON terpotong tanpa stream diperbaiki oleh plugin `response-healing`.

```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"]
      }
    }
  }
}
```

## Penalaran

- Penalaran model datang terpisah dari respons: `message.reasoning_content`, dalam stream — `delta.reasoning_content`. Kolom `reasoning` diganti nama gateway ke format ini.
- Penalaran menghabiskan `max_tokens`: dengan batas kecil, respons terputus (`finish_reason: length`) bahkan sebelum teks.
- Jika tidak ada teks respons tetapi ada penalaran, gateway memindahkannya ke `content` — kecuali respons dengan pemanggilan alat.
- `reasoning_effort` dan `reasoning.effort` diteruskan ke jaringan. Jika model hanya punya dua mode, gateway menyesuaikan nilainya: `none` dan `minimal` → `low`, yang lebih tinggi → penalaran default.
- Jika node menolak nilai, gateway menurunkannya (`max` dan `xhigh` → `high`, `minimal` → `low`, jika tidak kolomnya dihapus) dan mengulang permintaan.
- Dalam `/v1/messages`, penalaran tidak diteruskan — tidak ada blok `thinking`.

## Plugin

Plugin diaktifkan lewat kolom `plugins` — array string atau objek dengan opsi. Daftarnya — `GET /v1/plugins`.

| Plugin | Deskripsi | Kondisi |
| --- | --- | --- |
| `response-healing` | Memperbaiki JSON terpotong dalam respons model. | Hanya tanpa stream dan jika respons dimulai dengan `{` atau `[`. |
| `privacy-sanitization` | Menyamarkan dalam pesan teks email, IPv4, nomor kartu, JWT, kunci hex 64 karakter, dan kunci berformat `sk-…`, `gw_…`, `gm-…`, `Bearer …`. | Mode — kolom `privacy_mode`: `redact` (default) atau `tokenize`. |
| `file-parser` | Mengekstrak teks dari PDF. | Jika teks pesan seluruhnya berupa PDF dalam base64: `data:application/pdf;base64,…` atau tanpa prefiks. |
| `web` | Pencarian web: hasilnya dicampur ke permintaan, respons mendapat tautan ke sumber. | Bersama `privacy-sanitization` — error `400`. |

### Pencarian web

- Opsi: `max_results` — dari 1 hingga 10, default 5; `engine` — petunjuk mesin; `search_prompt` — teks sendiri sebelum hasil; `enabled: false` — matikan pencarian.
- Sumber — di `message.annotations[].url_citation`; dalam stream — lewat chunk `joingonka-citations` sebelum selesai.
- `mode: "agent"` — model sendiri yang memutuskan apakah perlu mencari dan apa; `max_searches` — dari 1 hingga 5, default 3.
- Penagihan: pada mode biasa — hanya token (hasil pencarian termasuk dalam token input); pada mode agen — token dari semua langkah ditambah 1000 nGNK untuk setiap pencarian yang dilakukan (`x_joingonka.web_search_surcharge_ngonka`).
- Di Anthropic Messages dan OpenAI Responses, alat bawaan `web_search` menjalankan plugin yang sama dalam mode agen.

#### 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}]
}
```

## Biaya dan field layanan

Respons non-streaming menyertakan biaya permintaan di `usage`:

| Field | Deskripsi |
| --- | --- |
| `usage.cost_gnk` | Biaya permintaan dalam GNK |
| `usage.platform_fee_gnk` | Termasuk margin platform, GNK |
| `usage.total_cost_gnk` | Total yang akan didebit dalam GNK |
| `usage.total_cost_usd` | Total dalam dolar menurut kurs GNK saat ini |

- Dalam streaming, `usage` hanya berisi token; biaya ada di chunk `joingonka-meta`.
- Dengan header `x-joingonka-meta: 1`, respons `POST /v1/chat/completions` mendapatkan blok `x_joingonka`: biaya (`cost_ngonka`), saldo setelah debit (`balance_ngonka`, hanya non-streaming), dan timing (`ttft_ms`). Protokol lain tidak mengembalikan blok ini.
- `x-request-id` — ID permintaan: sertakan ini saat menghubungi dukungan.
- `Retry-After` disertakan dengan `429`: sekian detik untuk menunggu sebelum mencoba lagi.
- Header `X-Title` dan `HTTP-Referer` (seperti di OpenRouter) membantu gateway mengenali aplikasi Anda; isinya tidak disimpan.

## Batasan

- Gambar: bagian `image_url` diganti dengan placeholder teks — model tidak bisa melihat gambarnya (`vision: false` di kemampuan).
- Dari browser, API hanya dapat diakses dari domain JoinGonka (pemeriksaan `Origin`): panggil dari server Anda sendiri, jangan taruh kunci di frontend.
- Embedding: `POST /v1/embeddings` mengembalikan `501` — tidak ada model embedding di jaringan.
- Kode error, batas, dan timeout — di bagian [Error dan batas](https://gate.joingonka.ai/id/docs/errors).
