Zaroz
Equipos y organizaciones

Organizaciones, proyectos, miembros e invitaciones

Una organización es quien paga y quien puede hacer qué. Contiene proyectos, y los proyectos contienen lo que compras. Esta guía muestra las llamadas del día a día para montar una y para llevar un equipo. Los ejemplos usan el ayudante de Usar la API de Zaroz.

Roles

RolPuede
ownerTodo, incluido borrar la organización y gestionar a otros propietarios
adminComprar, gestionar proyectos y miembros (no por encima de su propio rol), crear tokens de API de la organización
billingPagar facturas y leer la facturación
memberUsar los servicios de la organización; ver sus proyectos, miembros y facturas

Dos reglas te protegen de quedarte fuera: no puedes degradar ni quitar al último propietario, y no puedes cambiar a un miembro cuyo rol esté por encima del tuyo (403 member-outranks-caller). Cualquiera puede quitarse a sí mismo. Una organización personal (toda cuenta tiene una) no admite otros miembros: añadir uno responde 422 member-personal-organization.

Crear una organización

curl

TerminalCode
curl -sS -X POST "$ZAROZ_API/organizations" -H "$AUTH" -H "Content-Type: application/json" \ -d '{"name": "Acme Games", "slug": "acme-games"}' # {"id": "...", "name": "Acme Games", "slug": "acme-games", "role": "owner", ...}

TypeScript

TypeScriptCode
import { call } from "./zaroz"; const org = await call("POST", "/organizations", { name: "Acme Games", slug: "acme-games" }); const ORG = org.body.id;

Python

Code
from zaroz import call _, org = call("POST", "/organizations", {"name": "Acme Games", "slug": "acme-games"}) ORG = org["id"]

Te conviertes en su propietario. GET /organizations lista todas las organizaciones a las que perteneces y GET /organizations/by-slug/{slug} busca una por su slug. PATCH /organizations/{id} la renombra.

Proyectos

Un proyecto agrupa servicios. El slug es único dentro de la organización.

curl

TerminalCode
curl -sS -X POST "$ZAROZ_API/organizations/$ORG/projects" -H "$AUTH" -H "Content-Type: application/json" \ -d '{"name": "Production", "slug": "production", "description": "Servidores en vivo"}' curl -sS -X PATCH "$ZAROZ_API/projects/$PROJECT" -H "$AUTH" -H "Content-Type: application/json" \ -d '{"description": null}' # null lo borra; los campos omitidos no cambian curl -sS "$ZAROZ_API/projects/$PROJECT/stats" -H "$AUTH" curl -sS -X DELETE "$ZAROZ_API/projects/$PROJECT" -H "$AUTH" # 204, solo si no tiene suscripciones

TypeScript

TypeScriptCode
const project = await call("POST", `/organizations/${ORG}/projects`, { name: "Production", slug: "production", }); await call("PATCH", `/projects/${project.body.id}`, { name: "Live" }); const del = await call("DELETE", `/projects/${project.body.id}`); console.log(del.status); // 204, o 4xx mientras tenga suscripciones

Python

Code
_, project = call("POST", f"/organizations/{ORG}/projects", {"name": "Production", "slug": "production"}) call("PATCH", f"/projects/{project['id']}", {"name": "Live"}) status, _ = call("DELETE", f"/projects/{project['id']}") # 204, o 4xx mientras tenga suscripciones

En una organización personal el slug games está reservado al panel de juegos (422 project-slug-reserved) y ese proyecto no se puede cambiar aquí (403 project-gaming-managed).

Invitar a personas

Invita por correo con el rol que deben recibir. La respuesta es la misma tenga o no cuenta esa dirección, así que la API no sirve para averiguar quién está registrado.

curl

TerminalCode
curl -sS -X POST "$ZAROZ_API/organizations/$ORG/invitations" -H "$AUTH" -H "Content-Type: application/json" \ -d '{"email": "[email protected]", "role": "member"}' curl -sS "$ZAROZ_API/organizations/$ORG/invitations" -H "$AUTH" # pendientes, sin caducar curl -sS -X DELETE "$ZAROZ_API/organizations/$ORG/invitations/$INVITATION" -H "$AUTH" # retirar una

TypeScript

TypeScriptCode
const inv = await call("POST", `/organizations/${ORG}/invitations`, { email: "[email protected]", role: "member", }); console.log(inv.body.status, inv.body.expires_at); // "pending", ... const pending = await call("GET", `/organizations/${ORG}/invitations`);

Python

Code
_, inv = call("POST", f"/organizations/{ORG}/invitations", {"email": "[email protected]", "role": "member"}) print(inv["status"], inv["expires_at"]) # "pending", ... _, pending = call("GET", f"/organizations/{ORG}/invitations")

Tu propio correo debe estar verificado para poder invitar. El envío tiene límite de frecuencia, y una dirección que rechazó tiene un periodo de espera de 30 días antes de poder ser invitada de nuevo; ambos casos responden 429 con un code legible por máquina.

La persona invitada, con sesión iniciada con el mismo correo verificado, ve la invitación y la responde:

curl

TerminalCode
curl -sS "$ZAROZ_API/me/invitations?status=pending" -H "$AUTH" curl -sS -X POST "$ZAROZ_API/invitations/$INVITATION/accept" -H "$AUTH" curl -sS -X POST "$ZAROZ_API/invitations/$INVITATION/decline" -H "$AUTH" curl -sS -X POST "$ZAROZ_API/invitations/$INVITATION/allow-reinvite" -H "$AUTH" # levantar antes la espera

TypeScript

TypeScriptCode
const mine = await call("GET", "/me/invitations?status=pending"); await call("POST", `/invitations/${mine.body[0].id}/accept`);

Python

Code
_, mine = call("GET", "/me/invitations?status=pending") call("POST", f"/invitations/{mine[0]['id']}/accept")

Aceptar se puede repetir (una segunda aceptación no cambia nada) y también funciona sobre una invitación rechazada que no haya caducado. Si ya perteneces a esa organización la respuesta es 409 y la invitación se cancela: aceptar nunca cambia el rol de un miembro existente.

Añadir, cambiar y quitar miembros

Si ya conoces el id de cuenta de una persona puedes añadirla directamente, sin invitación.

curl

TerminalCode
curl -sS "$ZAROZ_API/organizations/$ORG/members" -H "$AUTH" curl -sS -X POST "$ZAROZ_API/organizations/$ORG/members" -H "$AUTH" -H "Content-Type: application/json" \ -d '{"user_id": "<uuid del usuario>", "role": "member"}' curl -sS -X PATCH "$ZAROZ_API/organizations/$ORG/members/$USER" -H "$AUTH" -H "Content-Type: application/json" \ -d '{"role": "admin"}' curl -sS -X DELETE "$ZAROZ_API/organizations/$ORG/members/$USER" -H "$AUTH" # 204

TypeScript

TypeScriptCode
const members = await call("GET", `/organizations/${ORG}/members`); for (const m of members.body) console.log(m.name, m.email, m.role); await call("PATCH", `/organizations/${ORG}/members/${USER}`, { role: "admin" }); await call("DELETE", `/organizations/${ORG}/members/${USER}`);

Python

Code
_, members = call("GET", f"/organizations/{ORG}/members") for m in members: print(m["name"], m["email"], m["role"]) call("PATCH", f"/organizations/{ORG}/members/{USER}", {"role": "admin"}) call("DELETE", f"/organizations/{ORG}/members/{USER}")

Volver a añadir a un miembro existente devuelve la membresía sin cambios. Leer la lista de miembros de una organización a la que no perteneces responde 404, igual que una que no existe.

País de facturación

Una compra en una organización compartida necesita el país en el que tributa. Un presupuesto responde billing-country-required hasta que se fija. Los propietarios y administradores lo fijan una vez; repetir el mismo valor funciona y uno distinto se rechaza con billing-country-already-set.

curl

TerminalCode
curl -sS -X PUT "$ZAROZ_API/organizations/$ORG/billing-country" -H "$AUTH" -H "Content-Type: application/json" \ -d '{"country_code": "ES"}'

TypeScript

TypeScriptCode
await call("PUT", `/organizations/${ORG}/billing-country`, { country_code: "ES" });

Python

Code
call("PUT", f"/organizations/{ORG}/billing-country", {"country_code": "ES"})

Verificar un dominio

Demostrar que la organización controla un dominio se hace con un registro DNS TXT. Inicia el desafío, publica el registro y pide una comprobación (la plataforma también vuelve a comprobar por su cuenta).

curl

TerminalCode
curl -sS -X POST "$ZAROZ_API/organizations/$ORG/domains" -H "$AUTH" -H "Content-Type: application/json" \ -d '{"domain": "example.com"}' # {"domain":"example.com","record_type":"TXT","record_name":"...","record_value":"...","status":"...", ...} # publica el registro TXT en record_name con record_value y entonces: curl -sS -X POST "$ZAROZ_API/organizations/$ORG/domains/example.com/check" -H "$AUTH" curl -sS "$ZAROZ_API/organizations/$ORG/domains" -H "$AUTH" curl -sS -X DELETE "$ZAROZ_API/organizations/$ORG/domains/example.com" -H "$AUTH"

TypeScript

TypeScriptCode
const challenge = await call("POST", `/organizations/${ORG}/domains`, { domain: "example.com" }); console.log(`TXT ${challenge.body.record_name} = ${challenge.body.record_value}`); // tras publicar el registro: const checked = await call("POST", `/organizations/${ORG}/domains/example.com/check`); console.log(checked.body.status, checked.body.last_error);

Python

Code
_, challenge = call("POST", f"/organizations/{ORG}/domains", {"domain": "example.com"}) print("TXT", challenge["record_name"], "=", challenge["record_value"]) # tras publicar el registro: _, checked = call("POST", f"/organizations/{ORG}/domains/example.com/check") print(checked["status"], checked["last_error"])

La comprobación hace una consulta DNS real y tiene un límite de frecuencia estricto, así que espera a que el registro se propague antes de volver a llamarla. last_error indica lo que encontró la consulta. (Esto demuestra la propiedad de un dominio para la organización; para poner un dominio delante de un servicio consulta Dominios personalizados.)

Registro de actividad

Todo lo hecho en la organización se lista, lo más reciente primero, con quién lo hizo (una persona, o un token de API por su nombre). Acótalo con project_id, service_instance_id o action.

curl

TerminalCode
curl -sS "$ZAROZ_API/organizations/$ORG/audit?page=1&page_size=50" -H "$AUTH" curl -sS "$ZAROZ_API/service-instances/$SERVICE/activity" -H "$AUTH" # solo un servicio

TypeScript

TypeScriptCode
const log = await call("GET", `/organizations/${ORG}/audit?page_size=20`); for (const e of log.body.data) console.log(e.created_at, e.action, e.principal_name ?? e.api_token_name);

Python

Code
_, log = call("GET", f"/organizations/{ORG}/audit?page_size=20") for e in log["data"]: print(e["created_at"], e["action"], e["principal_name"] or e["api_token_name"])

Los eventos internos de la plataforma nunca aparecen aquí: solo lo que los clientes hicieron en tu organización.

Borrar una organización

Solo un propietario puede, y solo cuando está vacía: sin proyectos (bórralos antes) y sin saldo en el monedero. Una organización personal no se puede borrar.

TerminalCode
curl -sS -X DELETE "$ZAROZ_API/organizations/$ORG" -H "$AUTH" # 204
Last modified on