Organizar equipos y roles
Los equipos que enrutan el trabajo y los roles que deciden qué ve cada miembro.
Un equipo recibe el trabajo; un rol personalizado decide qué puede hacer quien lo recibe. Son dos recursos de configuración distintos y casi siempre se editan juntos.
Equipos
Una conversación de un canal aterriza en el equipo que ese canal tiene por defecto, salvo que una regla de asignación diga otra cosa. Un ticket lleva su propio team_id. El equipo también guarda su horario de cobertura.
Leer pide teams:read; escribir, teams:write.
curl -X POST https://api.vitrinadev.com/api/v1/teams \
-H "Authorization: Bearer $VITRINA_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "Soporte técnico",
"hours_mode": "override",
"hours": { "mon": { "on": true, "start": "09:00", "end": "19:00" } }
}'{
"data": {
"id": "cccccccc-0000-4000-8000-000000000001",
"tenant_id": "a1a1a1a1-0000-4000-8000-000000000001",
"name": "Soporte técnico",
"brand": null,
"hours_mode": "override",
"hours": { "mon": { "on": true, "start": "09:00", "end": "19:00" } },
"created_at": "2026-09-22T13:00:00.000Z",
"updated_at": "2026-09-22T13:00:00.000Z"
}
}hours_mode decide de dónde sale ese horario. Con "workspace", el valor por defecto, el equipo sigue el del workspace; con "override", sigue el suyo, y hours lo describe como mapa parcial de días. Ese horario propio alimenta el reloj de SLA, el chequeo is_business_hours de las automatizaciones y el tono «oficina cerrada» del agente de IA. El huso horario y los feriados siempre vienen del workspace y nunca se sobrescriben.
Miembros del equipo
curl -X POST https://api.vitrinadev.com/api/v1/teams/cccccccc-0000-4000-8000-000000000001/members \
-H "Authorization: Bearer $VITRINA_KEY" \
-H "Content-Type: application/json" \
-d '{ "user_id": "11111111-0000-4000-8000-000000000001", "role": "member" }'user_id tiene que ser ya un miembro activo del workspace. Un id desconocido, de otro workspace o desactivado se rechaza con un 400. El role de aquí (lead o member) es una etiqueta del equipo y no un permiso; lo que la persona puede hacer lo deciden su rol de workspace o su rol personalizado.
Trampa
Los tickets asignados se quedan asignados
DELETE /teams/{id}/members/{membershipId} quita solo la membresía del equipo.
La persona conserva su cuenta y cualquier otro equipo en el que esté, y los
tickets que ya tenía asignados siguen con ella. Su assignee_user_id no se
limpia, así que un cambio de equipo nunca deja trabajo huérfano.
Sacar a alguien del workspace funciona distinto. Ahí
DELETE /memberships/{id} sí reparte el trabajo abierto de la persona, porque
esa persona deja de poder verlo. Está en Equipo.
Roles personalizados
Un rol personalizado fija el acceso efectivo de una membresía. La membresía apuntada a uno recibe exactamente sus scopes, sin ningún piso heredado del rol base, más sus dos techos de visibilidad, que se resuelven en cada solicitud.
Leer pide roles:read; escribir, roles:write.
curl -X POST https://api.vitrinadev.com/api/v1/roles \
-H "Authorization: Bearer $VITRINA_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "Agente de soporte",
"scopes": ["conversations:read", "conversations:write", "tickets:read", "tickets:write"],
"record_visibility": "assigned",
"stock_visibility": "all"
}'{
"data": {
"id": "d2d2d2d2-0000-4000-8000-000000000001",
"tenant_id": "a1a1a1a1-0000-4000-8000-000000000001",
"name": "Agente de soporte",
"scopes": ["conversations:read", "conversations:write", "tickets:read", "tickets:write"],
"record_visibility": "assigned",
"stock_visibility": "all",
"created_at": "2026-09-22T13:00:00.000Z",
"updated_at": "2026-09-22T13:00:00.000Z"
}
}Los dos techos son independientes:
| Campo | Valores | Qué acota |
|---|---|---|
record_visibility | all, assigned_unassigned, assigned | Qué conversaciones, tickets y leads ve el miembro, según a quién estén asignados. |
stock_visibility | all, own_locations | Si las lecturas de catálogo se acotan a las sucursales del miembro. El hecho vive en la membresía; la política, en el rol. |
Los roles base (owner, admin, supervisor, agent) no son roles personalizados y siempre resuelven a all en los dos ejes.
Trampa
No puedes crear un rol más ancho que el tuyo
Crear o editar un rol se valida contra el acceso propio de quien llama. Un scope
que no tiene, un record_visibility más ancho que el suyo o un
stock_visibility más ancho que el suyo son un 403. scopes además solo
acepta permisos conocidos y que no sean exclusivos del owner. Un owner o un
admin sin restricciones no se ve afectado: el techo solo alcanza a quien ya
está restringido.
Borrar un rol con miembros activos es un 409. Vacíalo primero, o pasa ?reassign_to=<id de rol> para mover a todos a otro rol en un solo paso, validado en los tres ejes igual que un PUT.
Los contratos completos están en la referencia de Equipos de trabajo y de Roles personalizados.