> Para agentes de IA: guía paso a paso de configuración — [`/docs/agents.md`](https://gate.joingonka.ai/docs/agents.md), índice de documentación — [`/llms.txt`](https://gate.joingonka.ai/llms.txt).

# Referencia de la API

Todo sobre las solicitudes a la pasarela: protocolos, direcciones, claves y parámetros. A continuación: streaming, llamada a herramientas, razonamiento, plugins y el coste de la solicitud en la respuesta.

## Protocolos y direcciones

El gateway acepta los formatos de OpenAI y Anthropic. La URL base para el SDK de OpenAI es `https://gate.joingonka.ai/v1` y para el SDK de Anthropic, `https://gate.joingonka.ai`. Todos los protocolos funcionan con una misma clave y un mismo saldo: una solicitud en cualquier formato recorre el mismo camino.

| Método y ruta | Formato | Para qué sirve | Particularidades |
| --- | --- | --- | --- |
| `POST /v1/chat/completions` | OpenAI Chat Completions | Chat, agentes, llamada a herramientas | Es la ruta principal: el gateway convierte los demás formatos a este. |
| `POST /v1/messages` | Anthropic Messages | Claude Code y el SDK de Anthropic | URL base sin `/v1`; los modelos `claude-*` se sustituyen por el recomendado; el campo `max_tokens` es obligatorio. |
| `POST /v1/responses` | OpenAI Responses | Codex CLI y los nuevos SDK de OpenAI | Sin estado: envía el historial completo en cada solicitud. |
| `POST /v1/completions` | OpenAI Completions (legacy) | Autocompletado y edición de código en editores | `suffix` se pasa al modelo como sugerencia; en la respuesta, `logprobs: null`. |
| `POST /v1/embeddings` | OpenAI Embeddings | Representaciones vectoriales de texto | Respuesta `501`: la red no tiene modelos de embeddings. |

### Direcciones de referencia

Responden sin clave. La tabla de modelos con contexto y estado está en la sección [Modelos](https://gate.joingonka.ai/es/docs/models).

| Método y ruta | Descripción |
| --- | --- |
| `GET /v1/models` | Lista de modelos: contexto, precios, parámetros admitidos; campos en formato OpenRouter. |
| `GET /v1/models/{model}` | Ficha de un modelo; la barra en el id, tal cual o como `%2F`. Modelo oculto o desconocido: `404 model_not_found`. |
| `GET /v1/capabilities` | Capacidades del gateway: parámetros, protocolos, plugins, campos de coste y límites (`limits`). |
| `GET /v1/plugins` | Plugins: id y nombre. |
| `GET /v1/network-status` | Estado de los modelos de la red: disponibilidad, latencias, uptime. |
| `GET /v1/nodes` | Resumen del pool de nodos: cuántos hay en total, activos y en cuarentena. |
| `GET /v1/web-search/engines` | Si la búsqueda web está activada y en qué estado están sus motores. |

### Anthropic Messages

- URL base: `https://gate.joingonka.ai`. El SDK añadirá `/v1/messages` por su cuenta.
- La clave va en el encabezado `x-api-key` (así la envía el SDK de Anthropic) o `Authorization: Bearer`.
- El gateway sustituye los modelos `claude-*` por el recomendado (`MiniMaxAI/MiniMax-M2.7`); en el campo `model` de la respuesta se mantiene el nombre que envió el cliente.
- `max_tokens` es obligatorio, como en la API de Anthropic; si supera el techo del modelo, se recorta.
- El stream son eventos de Anthropic; en las pausas el gateway envía `event: ping` y un fallo llega como evento `event: error`.
- El razonamiento del modelo no aparece en la respuesta: no hay bloques `thinking`.
- La herramienta integrada `web_search` la ejecuta el plugin de búsqueda web del gateway; consulta la sección [Plugins](https://gate.joingonka.ai/es/docs/api#plugins).
- No hay conteo de tokens (`/v1/messages/count_tokens`): respuesta `404`.

Claude Code se configura más fácilmente con el instalador: [conexión de herramientas](https://gate.joingonka.ai/es/docs#connect). De forma manual, con variables de entorno; `ANTHROPIC_MODEL` fija el modelo de la red.

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

- El gateway no almacena respuestas: envía todo el historial en `input`. Los campos `previous_response_id` y `conversation` dan un error `400` con código.
- `store` se acepta y no cambia nada.
- Herramientas: `function` y `web_search`, que ejecuta el plugin de búsqueda web. El gateway omite las demás herramientas integradas y la solicitud se ejecuta sin ellas; exigir una de esas herramientas mediante `tool_choice` da un error `400`.
- Las partes `input_image` y `input_file` dan un error `400`: los modelos de la red trabajan con texto.
- Las direcciones de estado (`GET /v1/responses/{id}`, `DELETE /v1/responses/{id}`, `GET /v1/responses/{id}/input_items`, `POST /v1/responses/{id}/cancel`, `POST /v1/responses/compact`) responden `404` con código: el gateway no almacena respuestas.
- Codex CLI: define tu propio id de proveedor en `model_provider` (que no sea openai); así Codex comprime el historial por su cuenta, sin `/v1/responses/compact`.

### Legacy Completions

- `prompt` es una cadena o un array de una sola cadena; la respuesta va en `choices[].text`. Varios prompts o tokens en lugar de texto dan un error `400`.
- `suffix` se pasa al modelo como sugerencia en el prompt: la red no realiza un verdadero relleno de la parte central.
- En la respuesta, `logprobs: null`; `best_of` se ignora; `echo` funciona.

## Claves y autorización

La clave se envía en el encabezado `Authorization: Bearer jg-…` o `x-api-key: jg-…`, en todas las direcciones. La clave se crea tras registrarse en la página [gate.joingonka.ai/keys](https://gate.joingonka.ai/keys).

| Prefijo | Clave | Solicitudes a los modelos |
| --- | --- | --- |
| `jg-` | Clave normal de la cuenta | sí |
| `gc-` | Clave hija: límites propios, el gasto se descuenta del saldo del propietario | sí |
| `gm-` | Clave de gestión: solo gestiona las claves hijas | no: `403 forbidden` |

- En el panel puedes fijar a cada clave un límite de gasto diario, mensual y total; si se supera, `402 child_key_limit_exceeded`.
- El número de solicitudes por minuto por clave está limitado; los valores están en la sección [Límites](https://gate.joingonka.ai/es/docs/errors#limits).
- Sin clave solo funciona el chat de demostración del sitio: una solicitud sin clave desde tu propio código recibirá `402` con `is_demo`.
- Las claves solo se gestionan en el panel: `/api/keys` no está disponible con una clave API. El saldo y el gasto por clave están en [API de la cuenta](https://gate.joingonka.ai/es/docs/billing#account-api).

> La clave es un secreto: no la guardes en el repositorio ni en el código del frontend, pásala mediante variables de entorno.

## Ejemplos

La misma petición en cuatro SDK. El modelo es el recomendado (`MiniMaxAI/MiniMax-M2.7`), la clave va en la variable de entorno `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)
```

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

## Parámetros de la petición

Parámetros de `POST /v1/chat/completions` que el gateway garantiza por su cuenta: la lista `supported_parameters` en la respuesta de capacidades:

| Parámetro | Descripción |
| --- | --- |
| `temperature` | Aleatoriedad de la respuesta: cuanto más alto, más variada. |
| `top_p` | Selección de tokens por probabilidad acumulada. |
| `top_k` | Selección entre los k tokens más probables. |
| `min_p` | Descarte de tokens poco probables respecto al más probable. |
| `frequency_penalty` | Penalización por repeticiones frecuentes. |
| `presence_penalty` | Penalización por tokens ya aparecidos. |
| `repetition_penalty` | Multiplicador contra las repeticiones. |
| `stop` | Cadenas en las que se detiene la generación. |
| `seed` | Semilla para la reproducibilidad. |
| `max_tokens` | Límite de tokens de la respuesta; por encima del techo del modelo se recorta hasta el techo. |
| `max_completion_tokens` | Otro nombre de `max_tokens`: el gateway traslada el valor a él. |
| `tools` | Funciones que el modelo puede invocar, en formato OpenAI. |
| `tool_choice` | Si invocar una función: a elección del modelo, nunca, obligatorio o una concreta. |
| `response_format` | Respuesta estructurada: `json_object` o `json_schema`. |

- Sin `temperature`, el gateway aplica `0.7`.
- Sin `max_tokens`, el gateway aplica el valor por defecto del modelo: sin streaming, más corto; en streaming, el techo del modelo. Los números por modelo están en la sección [Límites](https://gate.joingonka.ai/es/docs/errors#limits).

### Se transmiten a la red tal cual

`reasoning_effort`, `reasoning`, `enable_thinking`, `chat_template_kwargs`, `thinking_token_budget`, `min_tokens`, `logit_bias`, `n`, `parallel_tool_calls`, `extra_body`. El gateway no los valida: un valor fuera de la lista de la red produce un error `400` con tipo `api_error`.

### No se transmiten a la red

El resto de campos el gateway los acepta pero no los transmite a la red, por ejemplo `user`, `metadata`, `store`, `logprobs`, `top_logprobs`, `thinking`, `stream_options`, `web_search_options`. `usage` en streaming llega siempre.

## Streaming

- `stream: true`: respuesta por eventos SSE; el último evento es `data: [DONE]`.
- Antes del cierre llega un chunk con `usage`, siempre, incluso sin `stream_options`.
- En las pausas el gateway envía cada 15 s un comentario `: keep-alive`; los clientes SSE lo ignoran.
- Mientras el stream no está abierto, el rechazo llega con un código de respuesta normal; una vez abierto, llega como chunk `joingonka-error`.
- En `delta.tool_calls` hay una sola llamada por chunk: las llamadas que la red une las separa el 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]
```

### Chunks internos del gateway

Se reconocen por el campo `id`:

| `id` | Cuándo y qué incluye |
| --- | --- |
| `joingonka-error` | Fallo tras abrir el stream: campo `error` y luego corte sin `[DONE]`. |
| `joingonka-stream-stalled` | La red estuvo en silencio más de la pausa permitida: el stream se cierra con `finish_reason: stop`. |
| `joingonka-stream-unfinished` | La red interrumpió la generación: `finish_reason: length`; continúa con la siguiente petición. |
| `joingonka-citations` | Fuentes de la búsqueda web en `delta.annotations`, antes del cierre. |
| `joingonka-meta` | Coste y tiempos solo con la cabecera `x-joingonka-meta: 1`. |

### Streaming en otros protocolos

- Anthropic Messages: eventos desde `message_start` hasta `message_stop`, con `event: ping` en las pausas y `event: error` en caso de fallo.
- OpenAI Responses: eventos `response.*`, fallo `response.failed`.
- Legacy Completions: en caso de fallo, `data: {"error": …}` y luego `[DONE]`.

## Llamada a herramientas

- Formato OpenAI: `tools` y `tool_choice`. El formato antiguo `functions` y `function_call` también se acepta, y la respuesta llega en ese mismo formato.
- En streaming, una llamada por chunk: los clientes que solo leen el primer elemento no pierden llamadas.
- El historial con el que la red devolvería un error `400` el gateway lo corrige: el rol `developer` pasa a `system`, los id de llamada vacíos y repetidos reciben valores únicos, `arguments` como objeto se convierte en cadena JSON, el `type` ausente se completa, y una llamada sin nombre se elimina junto con su resultado.
- La llamada que el modelo escribió como marcado en el texto el gateway la traslada a `tool_calls`; las llamadas falsas en una respuesta a una petición sin herramientas las elimina.
- La generación se interrumpió en medio de los argumentos: llegará `finish_reason: length` y no `tool_calls`; aumenta el límite de respuesta.

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

### Restricciones de JSON-Schema

Los esquemas de las herramientas y `response_format` la red los compila a una gramática; las expresiones regulares usan el motor RE2. El gateway adapta el esquema a la forma que la red acepta:

- Los `$ref` se expanden en su lugar, las secciones `$defs` y `definitions` se eliminan; una referencia recursiva se convierte en un esquema sin restricciones.
- El `pattern` con construcciones que no existen en RE2 (lookahead y lookbehind, retrorreferencias, grupos atómicos, cuantificadores posesivos) se retira; las repeticiones mayores que 1000 se reducen a 1000.
- `anyOf` y `oneOf` de constantes se colapsan en `enum`; si hay más de 16 ramas no colapsables, la unión se retira.

> El esquema puede volverse más permisivo que el original: valida los argumentos de la llamada en tu lado.

## Respuesta estructurada

`response_format`: `{"type": "json_object"}` para una respuesta JSON válida, `{"type": "json_schema", "json_schema": {"name": …, "schema": …}}` según tu esquema con las restricciones anteriores. El JSON truncado sin streaming lo corrige el 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"]
      }
    }
  }
}
```

## Razonamiento

- El razonamiento del modelo llega aparte de la respuesta: `message.reasoning_content`, y en streaming `delta.reasoning_content`. El campo `reasoning` el gateway lo renombra a este formato.
- El razonamiento consume `max_tokens`: con un límite pequeño la respuesta se corta (`finish_reason: length`) antes incluso del texto.
- Si no hay texto de respuesta pero sí razonamiento, el gateway lo traslada a `content`, salvo en respuestas con llamada a herramienta.
- `reasoning_effort` y `reasoning.effort` se transmiten a la red. Si el modelo solo tiene dos modos, el gateway adapta el valor a ellos: `none` y `minimal` a `low`, los más altos al razonamiento por defecto.
- Si el nodo rechaza el valor, el gateway lo rebaja (`max` y `xhigh` → `high`, `minimal` → `low`, o de lo contrario elimina el campo) y repite la petición.
- En `/v1/messages` no se transmite el razonamiento: no hay bloques `thinking`.

## Plugins

Los plugins se activan con el campo `plugins`: un array de cadenas o de objetos con opciones. La lista está en `GET /v1/plugins`.

| Plugin | Descripción | Condiciones |
| --- | --- | --- |
| `response-healing` | Corrige el JSON truncado en la respuesta del modelo. | Solo sin streaming y si la respuesta empieza por `{` o `[`. |
| `privacy-sanitization` | Enmascara en los mensajes de texto emails, IPv4, números de tarjeta, JWT, claves hex de 64 caracteres y claves con forma `sk-…`, `gw_…`, `gm-…`, `Bearer …`. | El modo es el campo `privacy_mode`: `redact` (por defecto) o `tokenize`. |
| `file-parser` | Extrae el texto de un PDF. | Si el texto del mensaje es íntegramente un PDF en base64: `data:application/pdf;base64,…` o sin prefijo. |
| `web` | Búsqueda web: los resultados se incorporan a la petición y la respuesta recibe enlaces a las fuentes. | Junto con `privacy-sanitization`, error `400`. |

### Búsqueda web

- Opciones: `max_results`, de 1 a 10, por defecto 5; `engine`, sugerencia de motor; `search_prompt`, texto propio antes de los resultados; `enabled: false`, desactivar la búsqueda.
- Las fuentes están en `message.annotations[].url_citation`; en streaming, en el chunk `joingonka-citations` antes del cierre.
- `mode: "agent"`: el modelo decide por sí mismo si buscar y qué buscar; `max_searches`, de 1 a 5, por defecto 3.
- Pago: en modo normal, solo tokens (los resultados de búsqueda se incluyen en los tokens de entrada); en modo agente, los tokens de todos los pasos más 1000 nGNK por cada búsqueda realizada (`x_joingonka.web_search_surcharge_ngonka`).
- En Anthropic Messages y OpenAI Responses, la herramienta integrada `web_search` ejecuta este mismo plugin en modo agente.

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

## Costo y campos de servicio

La respuesta sin streaming incluye el costo de la solicitud en `usage`:

| Campo | Descripción |
| --- | --- |
| `usage.cost_gnk` | Costo de la solicitud en GNK |
| `usage.platform_fee_gnk` | De lo cual, margen de la plataforma, GNK |
| `usage.total_cost_gnk` | Total a debitar en GNK |
| `usage.total_cost_usd` | Total en dólares según el tipo de cambio actual de GNK |

- En streaming, `usage` contiene solo tokens; el costo está en el chunk `joingonka-meta`.
- Con la cabecera `x-joingonka-meta: 1`, la respuesta `POST /v1/chat/completions` recibe el bloque `x_joingonka`: costo (`cost_ngonka`), saldo tras el débito (`balance_ngonka`, solo sin streaming) y tiempos (`ttft_ms`). Otros protocolos no devuelven este bloque.
- `x-request-id`: identificador de la solicitud; inclúyelo en tu contacto con soporte.
- `Retry-After` llega con `429`: los segundos que hay que esperar antes de reintentar.
- Las cabeceras `X-Title` y `HTTP-Referer` (como en OpenRouter) ayudan a la pasarela a identificar tu aplicación; su texto no se almacena.

## Limitaciones

- Imágenes: las partes `image_url` se sustituyen por un marcador de texto; el modelo no ve la imagen (`vision: false` en las capacidades).
- Desde el navegador, la API solo es accesible desde dominios de JoinGonka (verificación `Origin`): llámala desde tu propio servidor y no pongas la clave en el frontend.
- Embeddings: `POST /v1/embeddings` responde `501`; no hay modelos de embeddings en la red.
- Códigos de error, límites y timeouts en la sección [Errores y límites](https://gate.joingonka.ai/es/docs/errors).
