Conceder y revocar una dirección
El segundo eje: a qué direcciones puede llegar una persona y con qué nivel.
Ejecuta cualquiera de las 2 llamadas de esta página contra tu espacio de trabajo, con tu propia clave.
POST /members/{userId}/addresses
El segundo eje: a qué direcciones puede llegar una persona y con qué nivel.
Una concesión no es un rol
access es el vocabulario más antiguo, por dirección, y deliberadamente no se solapa con los nombres de los permisos: member lee la dirección y envía como ella, viewer solo la lee. No dice nada sobre si la persona puede enviar en absoluto, que es cosa de su rol, y ambos deben permitir un envío para que este ocurra.
| Su rol | Su concesión sobre billing@ | ¿Puede enviar como billing@? |
|---|---|---|
| tiene `emails:send` | member | Sí. |
| tiene `emails:send` | viewer | No, la concesión lo impide. |
| sin `emails:send` | member | No, el rol lo impide. |
| tiene `emails:send` | ninguna concesión | No, la dirección nunca entra en la lista contra la que se comprueba un envío. |
Dar un rol a alguien no le da ninguna dirección. Un miembro con un rol y sin concesiones abre un buzón VACÍO en lugar del de todos, que es el fallo correcto mientras aún decides qué debería ver.
Conceder una dirección
Requiere members:write. POST /members/{userId}/addresses con { addressId, access }; access es member de forma predeterminada. Devuelve el miembro completo tal como queda.
curl -X POST "$OE/members/nQ8vBz1aRd4tYwKx7fQ2mN8vBz1aRd4t/addresses" -H "$AUTH" \ -H "Content-Type: application/json" \ -d '{ "addressId": "c40a95f2-1cc6-4d31-82a8-9e075d31c2a8", "access": "viewer" }'{ "object": "member", "userId": "nQ8vBz1aRd4tYwKx7fQ2mN8vBz1aRd4t", "email": "[email protected]", "name": "Sam Okonjo", "image": null, "role": { "id": "role_2b81de079c1f0a4b7e05d386", "name": "Support", "builtin": null }, "implied": false, "permissions": ["emails:send", "emails:read", "threads:read", "threads:write"], "addresses": [ { "addressId": "2b81de07-9c1f-4a4b-8e05-d3862c1f0a44", "address": "[email protected]", "access": "member" }, { "addressId": "c40a95f2-1cc6-4d31-82a8-9e075d31c2a8", "address": "[email protected]", "access": "viewer" } ], "createdAt": "2026-08-12T14:20:00.000Z" }POST sobre la subcolección en lugar de un PUT sobre el par, porque de todos modos es un upsert y el id de la fila no es algo que quien llama vaya a nombrar nunca. Volver a hacer POST con un access distinto es como un viewer pasa a ser member: hay una fila por (dirección, persona), así que una segunda llamada cambia el nivel en lugar de añadir una segunda concesión. Eso también convierte a este en el raro POST que se puede repetir sin riesgo.
Está protegido por members:write y no por ser propietario de la dirección, que es la diferencia entre esto y la antigua ruta de concesión del router de dominios. La propiedad es el control adecuado para la persona que añadió el dominio y el equivocado para un administrador que no posee nada y gestiona los accesos del espacio de trabajo en nombre del propietario. Ambos escriben la misma fila.
Se devuelve el miembro completo y no solo la concesión, para que la fila en pantalla se pueda volver a representar sin una segunda solicitud y para que la respuesta se lea igual tanto si la concesión era nueva como si fue una modificación.
Una dirección que no pertenece a este espacio de trabajo es member_not_found, un 422, con param: "addressId". Una clave solo puede repartir direcciones del espacio de trabajo para el que se emitió.
Conceder al PROPIETARIO del espacio de trabajo es member_is_owner, un 422. Ya tiene todas sus direcciones, así que la llamada no podría añadir nada.
Revocar una dirección
Requiere members:write. DELETE /members/{userId}/addresses/{addressId}. Devuelve el miembro, menos esa dirección.
curl -X DELETE \ "$OE/members/nQ8vBz1aRd4tYwKx7fQ2mN8vBz1aRd4t/addresses/c40a95f2-1cc6-4d31-82a8-9e075d31c2a8" \ -H "$AUTH"{ "object": "member", "userId": "nQ8vBz1aRd4tYwKx7fQ2mN8vBz1aRd4t", "email": "[email protected]", "name": "Sam Okonjo", "image": null, "role": { "id": "role_2b81de079c1f0a4b7e05d386", "name": "Support", "builtin": null }, "implied": false, "permissions": ["emails:send", "emails:read", "threads:read", "threads:write"], "addresses": [ { "addressId": "2b81de07-9c1f-4a4b-8e05-d3862c1f0a44", "address": "[email protected]", "access": "member" } ], "createdAt": "2026-08-12T14:20:00.000Z" }La revocación acotada, y la que hay que usar cuando alguien cambia de equipo: conserva su rol y sus otras direcciones y deja de ver esta.
Una dirección que no pertenece a este espacio de trabajo se rechaza en lugar de ignorarse en silencio. De lo contrario, un error tipográfico en el id informaría de una revocación exitosa que nunca ocurrió, que es justo el fallo que este endpoint existe para evitar.
Se devuelve el miembro y no una lápida, porque la respuesta interesante es a qué sigue teniendo acceso. Un { deleted: true } aquí obligaría al cliente a deducirlo por sustracción.
Para retirarlo todo de una vez, DELETE /members/{userId} elimina el rol y todas las concesiones a la vez e informa de cuántas se fueron.