Zur Dokumentation springen
API

Eine Adresse gewähren und entziehen

Die zweite Achse: Welche Adressen eine Person erreichen darf, und auf welcher Ebene.

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

Führt jeden der 2 Aufrufe auf dieser Seite gegen Ihren Workspace aus, mit Ihrem eigenen Schlüssel.

POST /members/{userId}/addresses

Die zweite Achse: Welche Adressen eine Person erreichen darf, und auf welcher Ebene.

Ein Grant ist keine Rolle

access ist das ältere Vokabular pro Adresse und überschneidet sich bewusst nicht mit den Berechtigungsnamen: member liest die Adresse und sendet als sie, viewer liest sie nur. Es sagt nichts darüber, ob die Person überhaupt senden darf – das ist ihre Rolle –, und beide müssen einen Versand erlauben, bevor einer stattfindet.

Ihre RolleIhr Grant auf billing@Dürfen sie als billing@ senden
hält `emails:send`memberJa.
hält `emails:send`viewerNein, der Grant verweigert es.
kein `emails:send`memberNein, die Rolle verweigert es.
hält `emails:send`gar kein GrantNein, die Adresse gelangt nie in die Liste, gegen die ein Versand geprüft wird.

Jemandem eine Rolle zu geben gibt ihm keine Adressen. Ein Mitglied mit einer Rolle und ohne Grants öffnet ein LEERES Postfach statt das aller anderen – das ist der richtige Fehlerfall, solange Sie noch entscheiden, was diese Person sehen soll.

Eine Adresse gewähren

Benötigt members:write. POST /members/{userId}/addresses mit { addressId, access }; access ist standardmäßig member. Gibt das gesamte Mitglied in seinem nunmehrigen Zustand zurück.

curl
curl -X POST "$OE/members/nQ8vBz1aRd4tYwKx7fQ2mN8vBz1aRd4t/addresses" -H "$AUTH" \    -H "Content-Type: application/json" \    -d '{ "addressId": "c40a95f2-1cc6-4d31-82a8-9e075d31c2a8", "access": "viewer" }'
Antwort
{    "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 auf die Unterkollektion statt ein PUT auf das Paar, weil es ohnehin ein Upsert ist und die Zeilen-ID nichts ist, was ein Aufrufer je benennt. Erneutes Posten mit einem anderen access ist der Weg, auf dem aus einem viewer ein member wird: Es gibt eine Zeile pro (Adresse, Person), sodass ein zweiter Aufruf die Ebene ändert, statt einen zweiten Grant hinzuzufügen. Das macht dies zugleich zu dem seltenen POST, den man gefahrlos wiederholen kann.

Abgesichert über members:write statt über den Besitz der Adresse – das ist der Unterschied zwischen diesem und dem älteren Grant-Pfad im Domains-Router. Besitz ist das richtige Gatter für die Person, die die Domain eingebracht hat, und das falsche für einen Administrator, der nichts besitzt und den Zugriff des Workspace im Auftrag des Inhabers verwaltet. Beide schreiben dieselbe Zeile.

Es kommt das gesamte Mitglied zurück statt nur der Grant, damit die Zeile auf dem Bildschirm ohne zweite Anfrage neu gezeichnet werden kann und damit die Antwort gleich aussieht, ob der Grant neu war oder eine Änderung.

Eine Adresse, die nicht zu diesem Workspace gehört, ergibt member_not_found, ein 422, mit param: "addressId". Ein Schlüssel kann nur Adressen vergeben, die zu dem Workspace gehören, für den er ausgestellt wurde.

Ein Grant an den Workspace-INHABER ergibt member_is_owner, ein 422. Er hat bereits jede Adresse darin, der Aufruf könnte also nichts hinzufügen.

Eine Adresse entziehen

Benötigt members:write. DELETE /members/{userId}/addresses/{addressId}. Gibt das Mitglied zurück, abzüglich dieser Adresse.

curl
curl -X DELETE \    "$OE/members/nQ8vBz1aRd4tYwKx7fQ2mN8vBz1aRd4t/addresses/c40a95f2-1cc6-4d31-82a8-9e075d31c2a8" \    -H "$AUTH"
Antwort
{    "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"  }

Der enge Entzug, und der richtige Griff, wenn jemand das Team wechselt: Rolle und übrige Adressen bleiben, nur diese eine sieht die Person nicht mehr.

Eine Adresse, die nicht zu diesem Workspace gehört, wird abgelehnt statt stillschweigend ignoriert. Ein Tippfehler in der ID würde sonst einen erfolgreichen Entzug melden, der nie stattgefunden hat – genau den Fehler, den es diesen Endpunkt zu verhindern gibt.

Es kommt das Mitglied zurück statt eines Grabsteins, weil die interessante Antwort ist, was es noch erreichen kann. Ein { deleted: true } würde einen Client hier zwingen, sich das durch Subtraktion zu erschließen.

Um alles auf einmal zurückzunehmen, entfernt DELETE /members/{userId} die Rolle und jeden Grant gemeinsam und meldet, wie viele es waren.