Invite a member
By email, with a role, and optionally with addresses and whole domains that land when they accept.
Runs the real call against your workspace, with your own key.
POST /members
By email, with a role, and optionally with addresses and whole domains that land when they accept.
Example
Needs members:write. Returns 202 with the invitation. Nobody is in the workspace until they accept it. access defaults to member and applies to every id in addressIds and domainIds.
curl -X POST "$OE/members" -H "$AUTH" -H "Content-Type: application/json" \ -d '{ "email": "[email protected]", "roleId": "role_2b81de079c1f0a4b7e05d386", "domainIds": ["93542ff8-2baa-4f2f-841d-5ceaa074ab0d"], "access": "member" }'{ "object": "invitation", "id": "winv_6bb640f5b99e47deb758f1f5", "email": "[email protected]", "role": { "id": "role_2b81de079c1f0a4b7e05d386", "name": "Support", "builtin": null }, "addresses": [], "domains": [ { "domainId": "93542ff8-2baa-4f2f-841d-5ceaa074ab0d", "domain": "acme.com", "access": "member" } ], "expiresAt": "2026-10-02T14:07:31.000Z", "delivered": true, "deliveryError": null, "createdAt": "2026-09-18T14:07:31.000Z"}EVERYBODY IS INVITED. Whether or not the address already has an OpenEmail account, the answer is an invitation and a 202, never a member. Nobody is put into a workspace without accepting, and this endpoint is held to the same rule as the app. Somebody already in the workspace is refused with member_is_owner, a 422: change what they may do with PATCH /members/{userId} and the grant calls instead.
The invitation carries the role, the addresses and the whole domains you name, and grants exactly those the moment it is accepted. Nothing is granted before that. A whole domain covers every address on it, including ones made after the invitation was sent.
POST rather than PUT on a user id, because the caller does not know the user id and may be inviting somebody who has no account yet. They know an email address.
Asking twice within ten minutes is invitation_too_soon, a 409. After that it refreshes the one outstanding invitation, mints a new link and retires the old one, rather than sending a second.
delivered: false means the invitation exists but the email did not leave the mail server. deliveryError says what the server said. Nothing reached them, so send it again from the app once the cooldown has passed.
invitedBy is deliberately left null rather than attributed to the workspace owner. The column records which PERSON invited somebody, and an API key is not a person.
The workspace owner is refused with member_is_owner, a 422. They are not a member and cannot be made one.