# API de Units: visión general y autenticación

Un **Workload** es tu app en Zaroz. Se ejecuta como una o más **unidades** (copias). La API de Units es la forma en que tú, o el código que corre dentro de tus propios Workloads, decidís qué unidades existen: crear una por nombre, entregar una caliente a una partida, parar la que nadie usa y ver qué pasó.

Cada página de esta guía trae las mismas tres muestras por operación: `curl`, TypeScript y Python. Solo necesitan el pequeño ayudante de abajo. Cópialo una vez.

**curl**

```bash
export ZAROZ_API="https://kernel.zaroz.cloud/api/v2"
export ZAROZ_TOKEN="zaroz_..."          # see "Authentication"
export PROJECT="<project uuid>"
export SERVICE="<service instance uuid>"
UNITS="$ZAROZ_API/projects/$PROJECT/service-instances/$SERVICE/units"
AUTH="Authorization: Bearer $ZAROZ_TOKEN"
```

**TypeScript**

```ts
// zaroz.ts: the only helper every sample on these pages relies on (Node 20+, no dependencies).
const API = process.env.ZAROZ_API ?? "https://kernel.zaroz.cloud/api/v2";
const TOKEN = process.env.ZAROZ_TOKEN!;
export const PROJECT = process.env.PROJECT ?? "";
export const SERVICE = process.env.SERVICE ?? "";
export const UNITS = `/projects/${PROJECT}/service-instances/${SERVICE}/units`;

export async function call<T = any>(
  method: string,
  path: string,
  body?: unknown,
  headers: Record<string, string> = {},
): Promise<{ status: number; body: T }> {
  const res = await fetch(`${API}${path}`, {
    method,
    headers: {
      Authorization: `Bearer ${TOKEN}`,
      ...(body !== undefined ? { "Content-Type": "application/json" } : {}),
      ...headers,
    },
    body: body !== undefined ? JSON.stringify(body) : undefined,
  });
  const text = await res.text();
  return { status: res.status, body: (text ? JSON.parse(text) : null) as T };
}
```

**Python**

```python
# zaroz.py: the only helper every sample on these pages relies on (pip install requests).
import os
import requests

API = os.environ.get("ZAROZ_API", "https://kernel.zaroz.cloud/api/v2")
PROJECT = os.environ.get("PROJECT", "")
SERVICE = os.environ.get("SERVICE", "")
UNITS = f"/projects/{PROJECT}/service-instances/{SERVICE}/units"
_session = requests.Session()
_session.headers["Authorization"] = "Bearer " + os.environ["ZAROZ_TOKEN"]


def call(method, path, body=None, **kwargs):
    """Returns (status, parsed JSON or None)."""
    r = _session.request(method, API + path, json=body, timeout=40, **kwargs)
    return r.status_code, (r.json() if r.content else None)
```


:::note
Todas las rutas de esta guía están en el kernel y en la referencia de la API: listar, política, presupuesto, logs, métricas y decisiones; crear por clave, iniciar, detener, destruir, etiquetas y asignar; eventos; webhooks (crear, actualizar, borrar, reactivar, probar, rotar secreto, entregas); principales de servicio y sus credenciales; y los despliegues `replace_idle` y `next_start`. La única excepción es `GET .../units/webhooks/metrics`, que el kernel sirve pero la referencia aún no lista.
:::

## A dónde va una petición

Todas las rutas de Units cuelgan de un servicio:

```
/api/v2/projects/{project_id}/service-instances/{id}/units...
```

`{project_id}` e `{id}` son UUID: el proyecto y el Workload (una instancia de servicio). Ambos aparecen en la URL de la página del Workload en la consola.

| Familia de rutas | Página |
|---|---|
| `POST .../units`, `start`, `stop`, `DELETE .../units/{key}` | [Crear por clave y ciclo de vida](/es/guides/units-api/keys-and-lifecycle) |
| `PATCH .../units/{key}`, `user_data`, filtros del listado | [Etiquetas, user data y filtros](/es/guides/units-api/labels-and-user-data) |
| `PATCH .../units/policy`, `POST .../units/allocate` | [El buffer caliente y allocate](/es/guides/units-api/warm-buffer-and-allocate) |
| `GET .../units/events` | [Eventos](/es/guides/units-api/events) |
| `.../units/webhooks` | [Webhooks](/es/guides/units-api/webhooks) |
| `.../principal` | [Principales de servicio](/es/guides/units-api/principals) |
| `GET .../units/logs`, `.../units/metrics` | [Logs y métricas](/es/guides/units-api/logs-and-metrics) |
| cada rechazo | [Errores](/es/guides/units-api/errors) |

## Autenticación

Envía un token bearer en cada petición: `Authorization: Bearer <token>`. Tres tipos de token pueden llamar a estas rutas.

| Quién llama | Token | Cómo se obtiene |
|---|---|---|
| Tú, desde un script o CI | Un token de API personal, limitado al servicio | Las [guías de CI/CD](/es/guides/ci/overview) explican cómo crearlo con solo los permisos necesarios |
| Código **dentro** de una unidad de tu Workload | El token del principal de servicio montado en `/zaroz/credentials/token` | [Principales de servicio](/es/guides/units-api/principals) |
| Un programa **fuera** de Zaroz (un matchmaker, un planificador) | Una credencial externa del principal, `zaroz_sp_...` | [Principales de servicio](/es/guides/units-api/principals) |

Un principal solo puede usar las rutas de unidades que se le concedieron (`units.read`, `units.control`). Nunca puede cambiar la política de escalado, los webhooks ni sus propios permisos: eso queda para las personas.

## Convenciones

- **Los cuerpos son JSON** y los campos desconocidos se rechazan (`400`), así que un error tipográfico es un error y no una operación silenciosa sin efecto.
- **Las claves** tienen de 1 a 40 caracteres: minúsculas, dígitos y guiones, empezando y terminando con letra o dígito. Están reservadas las del estilo `u7`, las que empiezan por `w-` o `zaroz` y las palabras que usan las rutas (`allocate`, `events`, `policy`).
- **Etiquetas**: como máximo 16 pares, se reemplazan enteras. **`user_data`**: cualquier JSON de hasta 64 KiB y 32 niveles. **No es un almacén de secretos**: lo ve cualquiera que lea la unidad. Los secretos van en el entorno del Workload.
- **Límite de peticiones** de un principal de servicio: 50 por segundo. Un `429` trae `Retry-After`.
- **Errores**: siempre llevan un `code` estable y un `docs_url`. Mira [Errores](/es/guides/units-api/errors).
- **Facturación**: una unidad creada, caliente o asignada es una unidad normal. Se factura como cualquier unidad extra, por hora, con un mínimo de 5 minutos por arranque. Una unidad detenida no cuesta nada. Eventos, webhooks y principales son gratis.

## Las rutas de unidades solo funcionan en modo `code`

`create`, `start`, `stop`, `destroy` y `allocate` responden `409 policy-not-code` salvo que la política de escalado del Workload tenga `mode: "code"`, porque en los modos `fixed` y `autoscaled` es la plataforma quien decide qué unidades existen. Cambia el modo en la [página del buffer caliente](/es/guides/units-api/warm-buffer-and-allocate). Leer, listar y `PATCH` de etiquetas funcionan en todos los modos.

## Pruébalo de punta a punta

Los [repositorios de ejemplo](/es/guides/ci/overview#repositorios-de-ejemplo) ejecutan contra un proyecto real cada llamada de esta guía.
