アドレスの付与と取り消し
2 つ目の軸です。1 人がどのアドレスに、どのレベルで到達できるかを決めます。
このページの2件の呼び出しを、ご自身のキーで自分のワークスペースに対して実行します。
POST /members/{userId}/addresses
2 つ目の軸です。1 人がどのアドレスに、どのレベルで到達できるかを決めます。
付与はロールではない
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" }組に対する PUT ではなくサブコレクションへの POST にしてあるのは、いずれにせよ upsert であり、行の id は呼び出し側が指定するものではないからです。別の access で再度 POST するのが、viewer を member に変える方法です。(アドレス, 人) の組につき行は 1 つなので、2 回目の呼び出しは 2 つ目の付与を追加するのではなくレベルを変更します。そのおかげで、これは繰り返しても安全な珍しい POST になっています。
アドレスの所有ではなく members:write でゲートしています。そこが domains ルーター上の古い付与パスとの違いです。所有権は、ドメインを登録した本人には正しいゲートですが、自分では何も所有せずオーナーに代わってワークスペースのアクセス権を運用している管理者には誤ったゲートです。どちらも書き込む行は同じです。
付与だけでなくメンバー全体が返るので、画面上の行を 2 回目のリクエストなしに再描画でき、付与が新規でも変更でも応答は同じ形に読めます。
このワークスペースにないアドレスは 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" }狭い取り消しで、誰かがチームを移るときに使うものです。ロールと他のアドレスはそのままで、このアドレスだけが見えなくなります。
このワークスペースにないアドレスは、黙って無視されるのではなく拒否されます。そうでなければ、id の打ち間違いが、実際には起きていない取り消しの成功として報告されてしまいます。このエンドポイントはまさにその失敗を防ぐために存在します。
tombstone ではなくメンバーが返るのは、知りたいのがその人のまだ到達できる範囲だからです。ここで { deleted: true } を返すと、クライアントは引き算でそれを求めることになります。
一度にすべてを取り上げるには DELETE /members/{userId} を使います。ロールとすべての付与をまとめて削除し、いくつ取り消したかを報告します。