Выдать и отозвать адрес
Вторая ось: к каким адресам один человек может обращаться и на каком уровне.
Выполняет любой из 2 запросов на этой странице в вашем рабочем пространстве, с вашим собственным ключом.
POST /members/{userId}/addresses
Вторая ось: к каким адресам один человек может обращаться и на каком уровне.
Доступ к адресу — это не роль
access — это более старый словарь уровней доступа к отдельному адресу, и он намеренно не пересекается с названиями разрешений: member читает адрес и отправляет от его имени, viewer только читает его. Он ничего не говорит о том, может ли человек вообще отправлять почту — это определяет его роль, и обе стороны должны разрешить отправку, прежде чем она произойдёт.
| Их роль | Их доступ к billing@ | Могут ли они отправлять от billing@ |
|---|---|---|
| есть `emails:send` | member | Да. |
| есть `emails:send` | viewer | Нет, доступ к адресу этого не позволяет. |
| нет `emails:send` | member | Нет, роль этого не позволяет. |
| есть `emails:send` | доступа нет вовсе | Нет, этот адрес вообще не попадает в список, по которому проверяется отправка. |
Выдача роли не даёт человеку ни одного адреса. Участник с ролью и без выданных адресов откроет ПУСТОЙ почтовый ящик, а не ящик всех подряд, — и это правильный вид отказа, пока вы ещё решаете, что именно он должен видеть.
Выдать доступ к адресу
Требует members:write. POST /members/{userId}/addresses с { addressId, access }; access по умолчанию member. Возвращает участника целиком в его текущем состоянии.
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 по подколлекции, а не PUT по паре, потому что это в любом случае upsert, а идентификатор строки клиент никогда не называет. Повторный POST с другим access — это способ превратить viewer в member: на пару (адрес, человек) существует ровно одна строка, поэтому второй вызов меняет уровень, а не добавляет второй доступ. Это же делает его тем редким POST, который безопасно повторять.
Ограничен members:write, а не владением адресом, — в этом и разница между ним и более старым путём выдачи доступа на роутере доменов. Владение — правильный критерий для того, кто добавил домен, и неправильный для администратора, который не владеет ничем и управляет доступом в рабочем пространстве от имени владельца. Оба пишут одну и ту же строку.
Возвращается участник целиком, а не один выданный доступ, чтобы строку на экране можно было перерисовать без второго запроса и чтобы ответ читался одинаково и при новой выдаче, и при её изменении.
Адрес, которого нет в этом рабочем пространстве, — это member_not_found, 422, с param: "addressId". Ключ может раздавать только адреса того рабочего пространства, для которого он был выпущен.
Выдача доступа ВЛАДЕЛЬЦУ рабочего пространства — это member_is_owner, 422. У него уже есть все адреса в нём, так что этот вызов ничего не смог бы добавить.
Отозвать адрес
Требует members:write. DELETE /members/{userId}/addresses/{addressId}. Возвращает участника без этого адреса.
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" }Узкий отзыв доступа, к которому стоит прибегать, когда человек переходит в другую команду: он сохраняет свою роль и остальные адреса и перестаёт видеть этот.
Адрес, которого нет в этом рабочем пространстве, отклоняется, а не игнорируется молча. Иначе опечатка в идентификаторе сообщала бы об успешном отзыве, которого не было, — а именно этот сбой данный эндпоинт и призван предотвратить.
Возвращается участник, а не надгробный камень, потому что интересен ответ на вопрос, до чего он всё ещё может дотянуться. { deleted: true } здесь заставило бы клиента вычислять это вычитанием.
Чтобы отобрать всё сразу, DELETE /members/{userId} снимает и роль, и все выданные доступы вместе и сообщает, сколько их было.