Notificaciones, flujos en vivo y tickets de soporte
Las tres formas en que Zaroz te cuenta lo que pasa, y la única por la que respondes. Los ejemplos usan el ayudante de Usar la API de Zaroz. Para los eventos de las unidades de un Workload (y sus webhooks) consulta Eventos y Webhooks.
Notificaciones
Mensajes de la plataforma para tu cuenta: una factura, un servicio que falló, una invitación. Van de más reciente a más antigua y paginadas. unread_count es el total de todas las páginas, que es lo que muestra una insignia.
curl
Code
curl -sS "$ZAROZ_API/me/notifications?page=1&page_size=20" -H "$AUTH"curl -sS -X POST "$ZAROZ_API/me/notifications/$NOTIFICATION/read" -H "$AUTH" # idempotentecurl -sS -X POST "$ZAROZ_API/me/notifications/read-all" -H "$AUTH"
TypeScript
Code
import { call } from "./zaroz";const feed = await call("GET", "/me/notifications?page_size=20");console.log(`${feed.body.unread_count} sin leer`);for (const n of feed.body.data) console.log(n.kind, n.title, n.href, n.read);await call("POST", "/me/notifications/read-all");
Python
Code
from zaroz import call_, feed = call("GET", "/me/notifications?page_size=20")print(feed["unread_count"], "sin leer")for n in feed["data"]: print(n["kind"], n["title"], n["href"], n["read"])call("POST", "/me/notifications/read-all")
Marcar una notificación como leída es idempotente. Una notificación que no es tuya responde 404, igual que una que no existe.
Flujos en vivo (eventos enviados por el servidor)
Tres rutas mantienen abierto un text/event-stream para que no tengas que consultar periódicamente.
El servidor cierra cada conexión a los quince minutos y la primera trama fija el intervalo de reconexión, así que reconecta cuando termine (un EventSource del navegador lo hace solo). La trama status del flujo del servicio trae el cuadro completo (estado agregado, las acciones del ciclo de vida que tienen sentido ahora, la operación en curso, cada recurso visible con su uso en vivo) al conectar, cuando cambia un recurso y cada cinco segundos. Responde 404 en un servicio cuya suscripción está suspendida, terminada, cancelada o fallida.
// Node 20+: un fetch normal puede leer el flujo; EventSource no puede enviar la cabecera Authorization.const res = await fetch(`${process.env.ZAROZ_API}/service-instances/${SERVICE}/status/stream`, { headers: { Authorization: `Bearer ${process.env.ZAROZ_TOKEN}`, Accept: "text/event-stream" },});const decoder = new TextDecoder();let buffer = "";for await (const chunk of res.body as any) { buffer += decoder.decode(chunk, { stream: true }); let i; while ((i = buffer.indexOf("\n\n")) >= 0) { const frame = buffer.slice(0, i); buffer = buffer.slice(i + 2); const event = /^event: (.*)$/m.exec(frame)?.[1]; const data = /^data: (.*)$/m.exec(frame)?.[1]; if (event === "status" && data) console.log(JSON.parse(data)); }}
Python
Code
import json, os, requestsurl = f"{os.environ['ZAROZ_API']}/service-instances/{SERVICE}/status/stream"headers = {"Authorization": "Bearer " + os.environ["ZAROZ_TOKEN"], "Accept": "text/event-stream"}with requests.get(url, headers=headers, stream=True, timeout=(10, 60)) as r: event = None for line in r.iter_lines(decode_unicode=True): if line.startswith("event: "): event = line[7:] elif line.startswith("data: ") and event == "status": print(json.loads(line[6:]))
Usa un cliente HTTP normal como arriba para scripts. Un EventSource del navegador no puede poner la cabecera Authorization, así que las apps web hacen el flujo a través de su propio backend.
Tickets de soporte
Los tickets pertenecen a una organización; todos los miembros ven los de la organización y pueden responder a los de un compañero. Las identidades del personal nunca se exponen: las respuestas aparecen como "Support Team".
curl
Code
# abrir un ticket en una organizacióncurl -sS -X POST "$ZAROZ_API/organizations/$ORG/tickets" -H "$AUTH" -H "Content-Type: application/json" \ -d '{"subject": "El servidor no arranca", "body": "Se reinicia en bucle desde el último despliegue. Id del servicio: …"}'# o en tu organización personalcurl -sS -X POST "$ZAROZ_API/me/tickets" -H "$AUTH" -H "Content-Type: application/json" -d '{"subject": "…", "body": "…"}'curl -sS "$ZAROZ_API/support/tickets?page_size=20" -H "$AUTH"curl -sS "$ZAROZ_API/support/tickets/$TICKET" -H "$AUTH" # con sus mensajescurl -sS -X POST "$ZAROZ_API/support/tickets/$TICKET/messages" -H "$AUTH" -H "Content-Type: application/json" \ -d '{"body": "Aquí van los logs que pedisteis."}'
TypeScript
Code
const opened = await call("POST", `/organizations/${ORG}/tickets`, { subject: "El servidor no arranca", body: "Se reinicia en bucle desde el último despliegue.",});const TICKET = opened.body.ticket.id;await call("POST", `/support/tickets/${TICKET}/messages`, { body: "Aquí van los logs que pedisteis." });const detail = await call("GET", `/support/tickets/${TICKET}`);for (const m of detail.body.messages) console.log(m.author_name, m.body);
Python
Code
_, opened = call("POST", f"/organizations/{ORG}/tickets", { "subject": "El servidor no arranca", "body": "Se reinicia en bucle desde el último despliegue.",})TICKET = opened["ticket"]["id"]call("POST", f"/support/tickets/{TICKET}/messages", {"body": "Aquí van los logs que pedisteis."})_, detail = call("GET", f"/support/tickets/{TICKET}")for m in detail["messages"]: print(m["author_name"], m["body"])
Abrir un ticket en /me/tickets usa siempre tu organización personal; no hay organización que equivocar. La lista muestra una vista previa del último mensaje, el recuento de no leídos y el estado de la valoración. Cuando un ticket se cierra puedes enviar una valoración de satisfacción una vez: POST /support/tickets/{id}/review con problem_solved (booleano), resolution_speed y response_quality (enteros pequeños) y un comment opcional; solo puede hacerlo el propietario del ticket.
Dar acceso de soporte a un servicio
Soporte puede pedir, dentro de un ticket, acceso a uno de tus servicios. Tú decides:
curl
Code
curl -sS "$ZAROZ_API/support/tickets/$TICKET/access-requests" -H "$AUTH"curl -sS -X POST "$ZAROZ_API/support/access-requests/$REQUEST/approve" -H "$AUTH"curl -sS -X POST "$ZAROZ_API/support/access-requests/$REQUEST/deny" -H "$AUTH"curl -sS "$ZAROZ_API/support/access-grants" -H "$AUTH" # quién está dentro ahora mismocurl -sS -X DELETE "$ZAROZ_API/support/access-requests/$REQUEST" -H "$AUTH" # retirar una aprobación
TypeScript
Code
const grants = await call("GET", "/support/access-grants");for (const g of grants.body) console.log(g.service_instance_id, g.expires_at, g.state);await call("DELETE", `/support/access-requests/${grants.body[0].id}`); // retirar en cualquier momento
Python
Code
_, grants = call("GET", "/support/access-grants")for g in grants: print(g["service_instance_id"], g["expires_at"], g["state"])call("DELETE", f"/support/access-requests/{grants[0]['id']}") # retirar en cualquier momento
Una concesión aprobada es para el único servicio que nombra la petición y nada más de lo que tienes. Empieza ahora y termina sola: tras max_duration_hours, en el momento en que se cierra el ticket o cuando la retiras. GET /support/access-grants responde solo las concesiones vivas, así que es la comprobación de "¿hay alguien dentro de mi infraestructura ahora mismo?".