문서로 건너뛰기
API

주소 권한 부여와 회수

두 번째 축: 한 사람이 어떤 주소에, 어느 수준으로 접근할 수 있는지.

POSTapi.openemail.uk/members/{userId}/addresses

이 페이지의 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
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"  }

쌍에 대한 PUT이 아니라 하위 컬렉션에 대한 POST인 이유는, 어느 쪽이든 upsert이고 행 id는 호출자가 지정할 일이 없기 때문입니다. 다른 access로 다시 POST하는 것이 viewer를 member로 바꾸는 방법입니다. (주소, 사람) 쌍당 행이 하나뿐이므로, 두 번째 호출은 권한을 하나 더 추가하지 않고 수준을 바꿉니다. 그래서 이것은 드물게도 반복해도 안전한 POST입니다.

주소 소유 여부가 아니라 members:write로 통제하며, 이것이 도메인 라우터에 있는 예전 권한 부여 경로와의 차이입니다. 소유권은 도메인을 등록한 사람에게는 맞는 기준이지만, 아무것도 소유하지 않은 채 소유자를 대신해 워크스페이스의 접근 권한을 운영하는 관리자에게는 틀린 기준입니다. 둘은 같은 행을 씁니다.

권한 하나가 아니라 멤버 전체가 돌아오므로, 화면의 행을 두 번째 요청 없이 다시 그릴 수 있고, 권한이 새로 생겼든 수정됐든 응답이 똑같이 읽힙니다.

이 워크스페이스에 없는 주소는 member_not_found, 422이며 param: "addressId"를 담습니다. 키는 자신이 발급된 워크스페이스에 속한 주소만 나눠줄 수 있습니다.

워크스페이스 소유자에게 부여하려 하면 member_is_owner, 422입니다. 소유자는 이미 그 워크스페이스의 모든 주소를 갖고 있으므로 이 호출이 더할 것이 없습니다.

주소 권한 회수

members:write가 필요합니다. DELETE /members/{userId}/addresses/{addressId}. 해당 주소가 빠진 멤버를 반환합니다.

curl
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"  }

좁은 범위의 회수이며, 누군가 팀을 옮겼을 때 손이 가야 할 호출입니다. 역할과 다른 주소는 그대로 두고 이 주소만 보이지 않게 됩니다.

이 워크스페이스에 없는 주소는 조용히 무시되지 않고 거부됩니다. 그렇지 않으면 id 오타가 실제로는 일어나지 않은 회수를 성공했다고 보고하게 되는데, 이 엔드포인트는 바로 그 실패를 막기 위해 존재합니다.

비석이 아니라 멤버가 돌아오는 이유는, 정작 궁금한 답이 그 사람이 여전히 접근할 수 있는 범위이기 때문입니다. 여기서 { deleted: true }를 주면 클라이언트가 뺄셈으로 알아내야 합니다.

한 번에 전부 회수하려면 DELETE /members/{userId}가 역할과 모든 권한을 함께 제거하고 몇 개가 사라졌는지 보고합니다.