Change a member’s role
The role and nothing else. Addresses are the other axis and have their own calls.
Runs the real call against your workspace, with your own key.
PATCH /members/{userId}
The role and nothing else. Addresses are the other axis and have their own calls.
Example
Needs members:write. roleId is the only field, and it is required. This call names the state it wants rather than a change to apply, so sending it twice lands on the same person holding the same role.
curl -X PATCH "$OE/members/7fQ2mN8vBz1aRd4tYwKx7fQ2mN8vBz1a" -H "$AUTH" \ -H "Content-Type: application/json" \ -d '{ "roleId": "role_2b81de079c1f0a4b7e05d386" }'{ "object": "member", "userId": "7fQ2mN8vBz1aRd4tYwKx7fQ2mN8vBz1a", "email": "[email protected]", "name": null, "image": null, "role": { "id": "role_2b81de079c1f0a4b7e05d386", "name": "Support", "builtin": null }, "implied": false, "permissions": [ "emails:send", "emails:read", "threads:read", "threads:write", "labels:read", "labels:write", "contacts:read" ], "addresses": [ { "addressId": "c40a95f2-1cc6-4d31-82a8-9e075d31c2a8", "address": "[email protected]", "access": "viewer" } ], "createdAt": "2026-08-30T15:44:02.000Z"}Addresses are NOT patchable from here, and the omission is deliberate: they are a set with a per-element access level, and a PATCH carrying an array would have to mean replace-whole, which is a silent mass revocation every time a client sends a stale list. The two address calls add and remove one at a time, so what happened is always what was asked for.
This is also how a legacy grant holder stops being implied: they have no member row, this upserts one, and from then on their permissions are what somebody chose rather than what their address access happened to imply. Note implied flipping to false and createdAt appearing in the response above. Nothing about their addresses changes.
The owner role cannot be handed out (role_immutable, a 409) whatever the caller holds. Making somebody an owner is a workspace transfer, which has different consequences and does not exist on this API.
Their new role takes effect on their next request, and on the next request of every API key capped by it.