Zaroz
Guía de la API de Units

Eventos

Cada transición de una unidad y cada cambio de escalado se escribe en un registro de solo añadir, en la misma transacción de base de datos que el cambio. Léelo con un cursor.

curl

TerminalCode
# long-poll: returns as soon as an event after cursor 0 exists, or after 30 s curl -sS -G "$UNITS/events" -H "$AUTH" \ --data-urlencode "after=0" --data-urlencode "wait=30" --data-urlencode "limit=100" \ --data-urlencode "types=unit.ready,unit.exited,unit.destroyed"

TypeScript

TypeScriptCode
let after = 0; for (;;) { const { status, body } = await call( "GET", `${UNITS}/events?after=${after}&wait=30&types=unit.ready,unit.exited,unit.destroyed`, ); if (status === 410) { after = body.resume_after; continue; } // cursor_expired for (const e of body.events) { console.log(e.seq, e.type, e.unit?.key, e.data); // persist e.seq next to your own state: it is the dedupe key } after = body.next_after; }

Python

Code
after = 0 while True: status, body = call( "GET", f"{UNITS}/events", params={"after": after, "wait": 30, "types": "unit.ready,unit.exited,unit.destroyed"}, ) if status == 410: # cursor-expired after = body["resume_after"] continue for e in body["events"]: print(e["seq"], e["type"], (e["unit"] or {}).get("key"), e["data"]) # persist e["seq"] next to your own state: it is the dedupe key after = body["next_after"]

El sobre (contrato v1)

JSONCode
{ "seq": 1042, "id": "evt_0e8b5c6a-6f43-4b6b-8d36-6c9c1f2ad711", "type": "unit.exited", "contract_version": 1, "occurred_at": "2026-10-14T10:31:07Z", "service_instance_id": "6c9d...", "unit": {"id": "3f2a...", "key": "job-81", "labels": {"queue": "render"}}, "data": {"code": 0, "restarted": false} }

seq es continuo y estrictamente creciente por Workload, de modo que un cursor nunca se salta un evento que se confirma tarde. Quien consume debe ignorar los campos y los tipos de evento desconocidos: las adiciones nunca son una versión nueva.

Tipodata
unit.created, unit.starting, unit.ready, unit.stopped, unit.idle, unit.busy, unit.draining
unit.accepting, unit.not_accepting
unit.allocated, unit.allocation_delivered, unit.allocation_undelivered
unit.exited{code, restarted}
unit.failed{reason}
unit.destroyed{reason} (expired cuando se destruye tras estar detenida demasiado tiempo)
units.scaled{from, to, reason}; unit es null
rollout.started, rollout.completed, rollout.pausedunit es null
webhook.ping (solo en entregas de webhooks)
events.gap (solo en entregas de webhooks){from_seq, to_seq}: eventos que un webhook reactivado se perdió porque ya no se conservaban

unit.ready y unit.allocated llevan el nombre privado y la dirección pública de la unidad, así que un matchmaker no necesita una segunda lectura. idle y busy se emiten solo en las transiciones.

Parámetros

ParámetroSignificado
afterel último seq que viste; 0 empieza por el evento conservado más antiguo
typeslista separada por comas que se conserva
limithasta 500
waitde 0 a 30 segundos: si no hay nada nuevo, retiene la petición y responde en cuanto llega un evento

La respuesta es {events, next_after, oldest_retained_seq}. Envía next_after como el siguiente after. Los eventos se conservan 30 días. Un cursor más antiguo responde 410 cursor_expired con resume_after.

La entrega es al-menos-una-vez

Guarda el último seq procesado en la misma transacción que tu propio efecto y trata un seq repetido como ya hecho. Es la misma regla que hace seguros los webhooks.

Last modified on