# API de roles de organización

> Lista, crea, actualiza y elimina los roles de autorización de una organización a través de la API para integradores.

Los roles de una organización determinan qué puede hacer cada miembro. Toda organización tiene siempre
cuatro **roles de sistema** — `owner`, `admin`, `member`, `viewer` — cuyos conjuntos de permisos vienen
con el producto y no pueden modificarse. Una organización en un plan que incluye roles personalizados
también puede definir sus propios **roles personalizados**, cada uno con su propio nombre y conjunto de
permisos, y asignarlos a los miembros de la misma forma en que asigna un rol de sistema.

El campo `role` de un rol en la API siempre es una cadena de texto simple (slug): los cuatro roles de
sistema usan sus nombres fijos (`owner`, `admin`, `member`, `viewer`); el slug de un rol personalizado
se elige al crearlo y no puede cambiarse después (ver [Renombrar un rol](#renombrar-un-rol) más abajo).

Los conjuntos de sistema los fija el producto: `viewer` no tiene **ningún** permiso del catálogo —
todos los endpoints de escritura de la API se lo niegan con `403 forbidden`, mientras que su acceso de
lectura no cambia porque las lecturas se autorizan por acceso al proyecto, no por rol. `member` tiene
el conjunto de lectura/escritura para el trabajo de prueba del día a día; `admin` y `owner` tienen el
conjunto completo, incluidos los permisos que controlan la gestión a nivel de organización y de proyecto
(por ejemplo `projects.manage`, que exige `POST /api/v1/projects` — un `member` no puede crear un proyecto).

## Listar roles

`GET /api/v1/orgs/{orgUlid}/roles`

Cualquier miembro de la organización puede llamar a este endpoint — leer la lista solo requiere ser
miembro, no ningún permiso en particular ni una característica del plan, porque un cliente lo necesita
para mostrar un selector de roles. La respuesta es una única página sin paginar (una organización tiene
como máximo 54 roles: cuatro roles de sistema más un tope de 50 roles personalizados) que contiene un
arreglo `items` con todos los roles, de sistema y personalizados, cada uno con:

| Campo         | Notas                                                                                                                   |
| ------------- | ----------------------------------------------------------------------------------------------------------------------- |
| `ulid`        | Identificador asignado por el servidor                                                                                  |
| `slug`        | El identificador estable del rol — lo que referencian los campos `role` en otros lugares                                |
| `name`        | Nombre para mostrar                                                                                                     |
| `description` | Opcional, o `null`                                                                                                      |
| `isSystem`    | `true` para uno de los cuatro roles fijos                                                                               |
| `isDefault`   | `true` para el rol predeterminado de la organización (ver más abajo)                                                    |
| `permissions` | Los nombres de permiso efectivos del rol                                                                                |
| `memberCount` | Cantidad de membresías aceptadas que actualmente tienen el slug de este rol (las invitaciones pendientes no se cuentan) |

Junto a `items`, el sobre de la respuesta también incluye:

| Campo                | Notas                                                                                                                                                                                       |
| -------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `customRolesEnabled` | Si el plan de esta organización incluye roles personalizados — un dato del plan de la propia organización de quien llama, presente en esta lectura sin importar los permisos de quien llama |

## Crear un rol

`POST /api/v1/orgs/{orgUlid}/roles`

Requiere el permiso de gestión de roles de la organización (que tienen `owner`/`admin`) **y** que el
plan de la organización incluya roles personalizados. Ambas condiciones deben cumplirse; ver
[Dos condiciones, una sola respuesta de rechazo](#dos-condiciones-una-sola-respuesta-de-rechazo) más abajo.

| Campo         | Requerido | Notas                                                                                               |
| ------------- | --------- | --------------------------------------------------------------------------------------------------- |
| `slug`        | sí        | Minúsculas, dígitos y guiones, 1–63 caracteres. No puede ser `owner`, `admin`, `member` ni `viewer` |
| `name`        | sí        | 1–80 caracteres                                                                                     |
| `description` | no        | Hasta 500 caracteres                                                                                |
| `permissions` | sí        | Arreglo de nombres de permiso del catálogo — puede estar vacío                                      |
| `isDefault`   | no        | Convierte este rol en el predeterminado de la organización (ver más abajo)                          |

Un slug duplicado dentro de la organización, o uno de los cuatro slugs reservados, devuelve
`409 conflict`. Una organización que ya tiene 50 roles personalizados recibe `409 conflict` al
intentar crear el 51.º.

## Actualizar un rol

`PATCH /api/v1/orgs/{orgUlid}/roles/{roleUlid}`

Misma condición de acceso que crear. Un rol personalizado acepta `name`, `description`, `permissions`
e `isDefault`. Un rol de sistema acepta **únicamente** `isDefault` — cualquier otro campo devuelve
`403 forbidden` y no cambia nada, porque el conjunto de permisos de un rol de sistema lo fija el
producto, no los datos.

### Renombrar un rol

**El `slug` de un rol no puede cambiarse después de crearlo.** Una solicitud que incluya la clave
`slug` se rechaza con `422 validation_failed`, sin importar su valor. Para renombrar un rol, envía su
nuevo `name` — el nombre para mostrar es el campo al que en realidad se refiere un "renombrado". Todo
miembro que ya tenga asignado el rol sigue funcionando sin cambios; solo cambia la etiqueta.

### El rol predeterminado

Exactamente un rol de la organización es el predeterminado — el rol que un cliente puede
preseleccionar al invitar a alguien, y el objetivo de reasignación al eliminar un rol sin indicar un
reemplazo (ver más abajo). El predeterminado comienza siendo `viewer`. Establecer `isDefault: true`
en otro rol mueve el predeterminado allí de forma atómica; la marca del predeterminado anterior se
borra en la misma solicitud. Borrar el único predeterminado (poner `isDefault: false` en él sin que
otro rol pase a ser el predeterminado) devuelve `409 conflict` — una organización siempre tiene
exactamente un predeterminado.

## Eliminar un rol

`DELETE /api/v1/orgs/{orgUlid}/roles/{roleUlid}`

Misma condición de acceso que crear. Solo puede eliminarse un rol personalizado — un rol de sistema, o
el rol predeterminado actual de la organización, devuelven `403`/`409` respectivamente. La respuesta es
`200` con un cuerpo JSON (nunca `204`), porque los conteos exactos del cuerpo son lo que permite que
una solicitud reintentada reproduzca la respuesta en lugar de repetir la operación.

```json
{ "reassignedTo": "member", "reassignedMemberCount": 3 }
```

Todo miembro e invitación pendiente que tenga el rol eliminado pasa a `reassignTo` — indicado
explícitamente en el cuerpo de la solicitud, o el rol predeterminado de la organización si se omite.

### Omitir el destino puede rechazar una eliminación que esperabas que funcionara

**Omitir `reassignTo` no siempre se acepta.** El respaldo al rol predeterminado de la organización solo
se aplica cuando los permisos de ese predeterminado son un subconjunto de los del rol que se elimina —
mover miembros a un rol "menor" siempre es seguro; moverlos a un rol que otorga algo nuevo no es algo
que la API haga en silencio. Si el predeterminado no califica, la solicitud se rechaza con
`422 validation_failed`, señalando `reassignTo` como el campo que debe indicarse explícitamente. Esto
nunca elimina una capacidad: indicar el destino explícitamente en la solicitud siempre funciona, porque
eres tú quien toma la decisión explícita. Solo evita que los miembros de un rol ganen permisos en
silencio porque no se indicó ningún destino.

## Dos condiciones, una sola respuesta de rechazo

Crear, actualizar y eliminar un rol personalizado requieren tanto el permiso de gestión de roles **como**
que el plan de la organización tenga habilitada esa característica. **Un `403 forbidden` de cualquiera
de estos tres endpoints nunca revela cuál de las dos condiciones faltó** — el permiso o la
característica del plan. Esto es deliberado: una respuesta distinguible permitiría a cualquiera
averiguar qué organizaciones tienen un plan con roles personalizados. Si recibes un `403` aquí y crees
que tienes el rol de organización correcto, consulta con el propietario de tu organización si el plan
incluye roles personalizados antes de asumir que la API está fallando.

Leer la lista de roles (`GET`) nunca está condicionado por el plan — toda organización, en cualquier
plan, siempre puede ver sus cuatro roles de sistema y cualquier rol personalizado que ya tenga.
