# Miembros e invitaciones

> Lista, añade y quita a las personas de tu organización, cambia sus roles y gestiona las invitaciones.

Los miembros son las personas con acceso a la organización, cada una con un [rol](https://developers.openwaf.dev/es/api/authentication/#roles). Los admins añaden miembros directamente o los invitan por correo; una invitación vale 7 días.

| Operación | Ruta |
|---|---|
| [Listar miembros](https://developers.openwaf.dev/es/api/members/#list-members) | `GET /v1/orgs/{org}/members` |
| [Añadir un miembro](https://developers.openwaf.dev/es/api/members/#add-member) | `POST /v1/orgs/{org}/members` |
| [Cambiar el rol de un miembro](https://developers.openwaf.dev/es/api/members/#update-member) | `PATCH /v1/orgs/{org}/members/{user}` |
| [Quitar un miembro](https://developers.openwaf.dev/es/api/members/#remove-member) | `DELETE /v1/orgs/{org}/members/{user}` |
| [Listar invitaciones](https://developers.openwaf.dev/es/api/members/#list-invitations) | `GET /v1/orgs/{org}/invitations` |
| [Crear una invitación](https://developers.openwaf.dev/es/api/members/#create-invitation) | `POST /v1/orgs/{org}/invitations` |
| [Retirar una invitación](https://developers.openwaf.dev/es/api/members/#delete-invitation) | `DELETE /v1/orgs/{org}/invitations/{id}` |

## Listar miembros

```http
GET /v1/orgs/{org}/members
```

Lista los miembros de la organización con su rol y, si están limitados a un cliente, su `client_id`.

**Rol necesario:** `readonly` o superior. De toda la organización.

#### Parámetros

| Nombre | En | Tipo | Obligatorio | Descripción |
|---|---|---|---|---|
| `org` | ruta | string (uuid) | Sí | Id de la organización. Cada ruta comprueba que es la del token (si no, `404`). |

#### Respuesta

- `200`: Devuelve [MemberList](https://developers.openwaf.dev/es/api/schemas/#member-list).

| Campo | Tipo | Descripción |
|---|---|---|
| `items[].id` | string (uuid) | Id. |
| `items[].email` | string | Correo electrónico. |
| `items[].name` | string | Nombre. |
| `items[].created_at` | string (date-time) | Fecha de creación. |
| `items[].role` | string | Rol. Uno de `owner`, `admin`, `tech`, `readonly`. |
| `items[].joined_at` | string (date-time) | Cuándo entró el miembro. |
| `items[].client_id` | string (uuid) o null | Cliente al que se limita el miembro (solo roles tech y readonly); null = toda la organización. |

#### Errores

| Estado | Descripción |
|---|---|
| `403` | El rol o los límites del token no lo permiten (`forbidden`). |

#### Ejemplo

**curl**


```sh
curl "https://dash.openwaf.dev/v1/orgs/$OPENWAF_ORG/members" \
  -H "Authorization: Bearer $OPENWAF_TOKEN"
```

**TypeScript**


```ts
import { OpenWAF } from "./openwaf";

const owf = new OpenWAF({ baseUrl: "https://dash.openwaf.dev" }); // token de OPENWAF_TOKEN
const result = await owf.listMembers();
```

**Python**


```py
from openwaf import OpenWAF

owf = OpenWAF(base_url="https://dash.openwaf.dev")  # token de OPENWAF_TOKEN
result = owf.list_members()
```



```json
{
  "items": [
    {
      "id": "5b8e2a1c-7d3f-4a9e-b6c2-1e4d7f0a3b9c",
      "email": "dev@acme.example",
      "name": "Dev team",
      "created_at": "2026-04-10T12:00:00Z",
      "role": "tech",
      "joined_at": "2026-04-10T12:00:00Z",
      "client_id": null
    }
  ]
}
```

## Añadir un miembro

```http
POST /v1/orgs/{org}/members
```

Añade una persona a la organización directamente por su correo, sin invitación. `client_id` limita el miembro a las zonas de un cliente (solo roles `tech` y `readonly`).

**Rol necesario:** `admin` o superior. Solo un owner puede dar el rol `owner`.

#### Parámetros

| Nombre | En | Tipo | Obligatorio | Descripción |
|---|---|---|---|---|
| `org` | ruta | string (uuid) | Sí | Id de la organización. Cada ruta comprueba que es la del token (si no, `404`). |

#### Cuerpo de la petición

Tipo de contenido `application/json`.

| Campo | Tipo | Obligatorio | Descripción |
|---|---|---|---|
| `email` | string (email) | Sí | Correo electrónico. |
| `name` | string | No | Nombre. |
| `role` | string | Sí | Rol. Uno de `owner`, `admin`, `tech`, `readonly`. |
| `client_id` | string (uuid) | No | Limita el miembro a las zonas de un cliente (solo roles tech y readonly). |

#### Respuesta

- `201`: Añadido. Devuelve [Member](https://developers.openwaf.dev/es/api/schemas/#member).

| Campo | Tipo | Descripción |
|---|---|---|
| `id` | string (uuid) | Id. |
| `email` | string | Correo electrónico. |
| `name` | string | Nombre. |
| `created_at` | string (date-time) | Fecha de creación. |
| `role` | string | Rol. Uno de `owner`, `admin`, `tech`, `readonly`. |
| `joined_at` | string (date-time) | Cuándo entró el miembro. |
| `client_id` | string (uuid) o null | Cliente al que se limita el miembro (solo roles tech y readonly); null = toda la organización. |

#### Errores

| Estado | Descripción |
|---|---|
| `403` | El rol o los límites del token no lo permiten (`forbidden`). |
| `409` | `already_member`: la persona ya es miembro. |

#### Ejemplo

**curl**


```sh
curl -X POST "https://dash.openwaf.dev/v1/orgs/$OPENWAF_ORG/members" \
  -H "Authorization: Bearer $OPENWAF_TOKEN" \
  -H "Content-Type: application/json" \
  --data '{
    "email": "dev@acme.example",
    "role": "tech"
  }'
```

**TypeScript**


```ts
import { OpenWAF } from "./openwaf";

const owf = new OpenWAF({ baseUrl: "https://dash.openwaf.dev" }); // token de OPENWAF_TOKEN
const result = await owf.addMember({
  body: {
    email: "dev@acme.example",
    role: "tech"
  }
});
```

**Python**


```py
from openwaf import OpenWAF

owf = OpenWAF(base_url="https://dash.openwaf.dev")  # token de OPENWAF_TOKEN
result = owf.add_member(
    body={
        "email": "dev@acme.example",
        "role": "tech"
    },
)
```



```json
{
  "id": "5b8e2a1c-7d3f-4a9e-b6c2-1e4d7f0a3b9c",
  "email": "dev@acme.example",
  "name": "Dev team",
  "created_at": "2026-04-10T12:00:00Z",
  "role": "tech",
  "joined_at": "2026-04-10T12:00:00Z",
  "client_id": null
}
```

## Cambiar el rol de un miembro

```http
PATCH /v1/orgs/{org}/members/{user}
```

Cambia el rol de un miembro. La organización siempre conserva al menos un owner. Los tokens del miembro siguen el nuevo rol al instante, porque un token nunca actúa por encima de su miembro.

**Rol necesario:** `admin` o superior. Cambiar a un owner o dar el rol `owner` exige el rol `owner`.

#### Parámetros

| Nombre | En | Tipo | Obligatorio | Descripción |
|---|---|---|---|---|
| `org` | ruta | string (uuid) | Sí | Id de la organización. Cada ruta comprueba que es la del token (si no, `404`). |
| `user` | ruta | string (uuid) | Sí | Id de usuario del miembro. |

#### Cuerpo de la petición

Tipo de contenido `application/json`.

| Campo | Tipo | Obligatorio | Descripción |
|---|---|---|---|
| `role` | string | Sí | Rol. Uno de `owner`, `admin`, `tech`, `readonly`. |

#### Respuesta

- `200`: Devuelve [Member](https://developers.openwaf.dev/es/api/schemas/#member).

| Campo | Tipo | Descripción |
|---|---|---|
| `id` | string (uuid) | Id. |
| `email` | string | Correo electrónico. |
| `name` | string | Nombre. |
| `created_at` | string (date-time) | Fecha de creación. |
| `role` | string | Rol. Uno de `owner`, `admin`, `tech`, `readonly`. |
| `joined_at` | string (date-time) | Cuándo entró el miembro. |
| `client_id` | string (uuid) o null | Cliente al que se limita el miembro (solo roles tech y readonly); null = toda la organización. |

#### Errores

| Estado | Descripción |
|---|---|
| `403` | El rol o los límites del token no lo permiten (`forbidden`). |
| `409` | `conflict`: el cambio dejaría la organización sin owner. |

#### Ejemplo

**curl**


```sh
curl -X PATCH "https://dash.openwaf.dev/v1/orgs/$OPENWAF_ORG/members/5b8e2a1c-7d3f-4a9e-b6c2-1e4d7f0a3b9c" \
  -H "Authorization: Bearer $OPENWAF_TOKEN" \
  -H "Content-Type: application/json" \
  --data '{
    "role": "admin"
  }'
```

**TypeScript**


```ts
import { OpenWAF } from "./openwaf";

const owf = new OpenWAF({ baseUrl: "https://dash.openwaf.dev" }); // token de OPENWAF_TOKEN
const result = await owf.updateMember({
  user: "5b8e2a1c-7d3f-4a9e-b6c2-1e4d7f0a3b9c",
  body: {
    role: "admin"
  }
});
```

**Python**


```py
from openwaf import OpenWAF

owf = OpenWAF(base_url="https://dash.openwaf.dev")  # token de OPENWAF_TOKEN
result = owf.update_member(
    user="5b8e2a1c-7d3f-4a9e-b6c2-1e4d7f0a3b9c",
    body={
        "role": "admin"
    },
)
```



```json
{
  "id": "5b8e2a1c-7d3f-4a9e-b6c2-1e4d7f0a3b9c",
  "email": "dev@acme.example",
  "name": "Dev team",
  "created_at": "2026-04-10T12:00:00Z",
  "role": "tech",
  "joined_at": "2026-04-10T12:00:00Z",
  "client_id": null
}
```

## Quitar un miembro

```http
DELETE /v1/orgs/{org}/members/{user}
```

Quita un miembro de la organización. Sus tokens de API dejan de valer al momento. No se puede quitar al último owner.

**Rol necesario:** `admin` o superior. Quitar a un owner exige el rol `owner`.

#### Parámetros

| Nombre | En | Tipo | Obligatorio | Descripción |
|---|---|---|---|---|
| `org` | ruta | string (uuid) | Sí | Id de la organización. Cada ruta comprueba que es la del token (si no, `404`). |
| `user` | ruta | string (uuid) | Sí | Id de usuario del miembro. |

#### Respuesta

- `204`: Quitado. Sin cuerpo.

#### Errores

| Estado | Descripción |
|---|---|
| `403` | El rol o los límites del token no lo permiten (`forbidden`). |
| `409` | `conflict`: el miembro es el último owner. |

#### Ejemplo

**curl**


```sh
curl -X DELETE "https://dash.openwaf.dev/v1/orgs/$OPENWAF_ORG/members/5b8e2a1c-7d3f-4a9e-b6c2-1e4d7f0a3b9c" \
  -H "Authorization: Bearer $OPENWAF_TOKEN"
```

**TypeScript**


```ts
import { OpenWAF } from "./openwaf";

const owf = new OpenWAF({ baseUrl: "https://dash.openwaf.dev" }); // token de OPENWAF_TOKEN
const result = await owf.removeMember({
  user: "5b8e2a1c-7d3f-4a9e-b6c2-1e4d7f0a3b9c"
});
```

**Python**


```py
from openwaf import OpenWAF

owf = OpenWAF(base_url="https://dash.openwaf.dev")  # token de OPENWAF_TOKEN
result = owf.remove_member(user="5b8e2a1c-7d3f-4a9e-b6c2-1e4d7f0a3b9c")
```



## Listar invitaciones

```http
GET /v1/orgs/{org}/invitations
```

Lista las invitaciones abiertas de la organización.

**Rol necesario:** `admin` o superior. De toda la organización.

#### Parámetros

| Nombre | En | Tipo | Obligatorio | Descripción |
|---|---|---|---|---|
| `org` | ruta | string (uuid) | Sí | Id de la organización. Cada ruta comprueba que es la del token (si no, `404`). |

#### Respuesta

- `200`: Devuelve `items`, un array de [Invitation](https://developers.openwaf.dev/es/api/schemas/#invitation).

| Campo | Tipo | Descripción |
|---|---|---|
| `items[].id` | string (uuid) | Id. |
| `items[].org_id` | string (uuid) | Id de la organización. |
| `items[].email` | string | Correo electrónico. |
| `items[].role` | string | Rol. Uno de `owner`, `admin`, `tech`, `readonly`. |
| `items[].invited_by` | string | Quién envió la invitación. |
| `items[].created_at` | string (date-time) | Fecha de creación. |
| `items[].expires_at` | string (date-time) | Fecha de caducidad. |
| `items[].client_id` | string (uuid) o null | Cliente al que se limitará el miembro invitado; null = toda la organización. |

#### Ejemplo

**curl**


```sh
curl "https://dash.openwaf.dev/v1/orgs/$OPENWAF_ORG/invitations" \
  -H "Authorization: Bearer $OPENWAF_TOKEN"
```

**TypeScript**


```ts
import { OpenWAF } from "./openwaf";

const owf = new OpenWAF({ baseUrl: "https://dash.openwaf.dev" }); // token de OPENWAF_TOKEN
const result = await owf.listInvitations();
```

**Python**


```py
from openwaf import OpenWAF

owf = OpenWAF(base_url="https://dash.openwaf.dev")  # token de OPENWAF_TOKEN
result = owf.list_invitations()
```



```json
{
  "items": [
    {
      "id": "a5614527-0ce6-43be-ad1d-d012b7394867",
      "org_id": "1e7c9a3b-5d2f-4b8a-9c6e-0f2a4b6c8d1e",
      "email": "ops@example.com",
      "role": "owner",
      "invited_by": "user:5b8e2a1c-7d3f-4a9e-b6c2-1e4d7f0a3b9c",
      "created_at": "2026-10-27T09:30:00Z",
      "expires_at": "2026-10-27T09:30:00Z",
      "client_id": "65904271-c7f4-4ee6-a8a6-84f55f76a92a"
    }
  ]
}
```

## Crear una invitación

```http
POST /v1/orgs/{org}/invitations
```

Invita a una persona por correo con un rol. La invitación vale 7 días. La respuesta incluye su `secret`, que solo se muestra una vez; la persona invitada la acepta desde el enlace del correo.

**Rol necesario:** `admin` o superior. Solo un owner puede invitar con el rol `owner`.

#### Parámetros

| Nombre | En | Tipo | Obligatorio | Descripción |
|---|---|---|---|---|
| `org` | ruta | string (uuid) | Sí | Id de la organización. Cada ruta comprueba que es la del token (si no, `404`). |

#### Cuerpo de la petición

Tipo de contenido `application/json`.

| Campo | Tipo | Obligatorio | Descripción |
|---|---|---|---|
| `email` | string (email) | Sí | Correo electrónico. |
| `role` | string | Sí | Rol. Uno de `owner`, `admin`, `tech`, `readonly`. |
| `client_id` | string (uuid) | No | Limita el miembro invitado a las zonas de un cliente (solo roles tech y readonly). |

#### Respuesta

- `201`: Creada. Devuelve un objeto con los campos de abajo.

| Campo | Tipo | Descripción |
|---|---|---|
| `id` | string (uuid) | Id. |
| `org_id` | string (uuid) | Id de la organización. |
| `email` | string | Correo electrónico. |
| `role` | string | Rol. Uno de `owner`, `admin`, `tech`, `readonly`. |
| `invited_by` | string | Quién envió la invitación. |
| `created_at` | string (date-time) | Fecha de creación. |
| `expires_at` | string (date-time) | Fecha de caducidad. |
| `client_id` | string (uuid) o null | Cliente al que se limitará el miembro invitado; null = toda la organización. |
| `secret` | string |  |

#### Errores

| Estado | Descripción |
|---|---|
| `409` | `already_member` o una invitación abierta para el mismo correo. |

#### Ejemplo

**curl**


```sh
curl -X POST "https://dash.openwaf.dev/v1/orgs/$OPENWAF_ORG/invitations" \
  -H "Authorization: Bearer $OPENWAF_TOKEN" \
  -H "Content-Type: application/json" \
  --data '{
    "email": "dev@acme.example",
    "role": "readonly"
  }'
```

**TypeScript**


```ts
import { OpenWAF } from "./openwaf";

const owf = new OpenWAF({ baseUrl: "https://dash.openwaf.dev" }); // token de OPENWAF_TOKEN
const result = await owf.createInvitation({
  body: {
    email: "dev@acme.example",
    role: "readonly"
  }
});
```

**Python**


```py
from openwaf import OpenWAF

owf = OpenWAF(base_url="https://dash.openwaf.dev")  # token de OPENWAF_TOKEN
result = owf.create_invitation(
    body={
        "email": "dev@acme.example",
        "role": "readonly"
    },
)
```



```json
{
  "id": "a5614527-0ce6-43be-ad1d-d012b7394867",
  "org_id": "1e7c9a3b-5d2f-4b8a-9c6e-0f2a4b6c8d1e",
  "email": "ops@example.com",
  "role": "owner",
  "invited_by": "user:5b8e2a1c-7d3f-4a9e-b6c2-1e4d7f0a3b9c",
  "created_at": "2026-10-27T09:30:00Z",
  "expires_at": "2026-10-27T09:30:00Z",
  "client_id": "65904271-c7f4-4ee6-a8a6-84f55f76a92a",
  "secret": "owf_8Jx2…"
}
```

## Retirar una invitación

```http
DELETE /v1/orgs/{org}/invitations/{id}
```

Retira una invitación abierta; su enlace deja de valer.

**Rol necesario:** `admin` o superior. De toda la organización.

#### Parámetros

| Nombre | En | Tipo | Obligatorio | Descripción |
|---|---|---|---|---|
| `org` | ruta | string (uuid) | Sí | Id de la organización. Cada ruta comprueba que es la del token (si no, `404`). |
| `id` | ruta | string (uuid) | Sí | Id del recurso. |

#### Respuesta

- `204`: Retirada. Sin cuerpo.

#### Errores

| Estado | Descripción |
|---|---|
| `404` | No existe o el token no lo ve (`not_found`). |

#### Ejemplo

**curl**


```sh
curl -X DELETE "https://dash.openwaf.dev/v1/orgs/$OPENWAF_ORG/invitations/11111111-2222-4333-8444-555555555555" \
  -H "Authorization: Bearer $OPENWAF_TOKEN"
```

**TypeScript**


```ts
import { OpenWAF } from "./openwaf";

const owf = new OpenWAF({ baseUrl: "https://dash.openwaf.dev" }); // token de OPENWAF_TOKEN
const result = await owf.deleteInvitation({
  id: "11111111-2222-4333-8444-555555555555"
});
```

**Python**


```py
from openwaf import OpenWAF

owf = OpenWAF(base_url="https://dash.openwaf.dev")  # token de OPENWAF_TOKEN
result = owf.delete_invitation(id="11111111-2222-4333-8444-555555555555")
```
