Send invitations
Sends email invitations to join an organization. Accepts a batch of 1–20 invitations, each with an email and optional role_slugs.
If no role_slugs are provided for an entry, it defaults to ["member"]. Each entry must contain exactly one system role (member, billing, admin, or owner). Custom roles (org-* prefix) are only allowed alongside "member".
Each invitation is sent independently; partial failures are reported per-entry in the response array. Every invitation record carries attribution for who triggered the send.
Permissions
| Key type | Accepted | Permission required | Notes |
|---|---|---|---|
| Org API key | yes | member:invite |
Key must be scoped to the org in the URL path. Privilege escalation guards are skipped — the admin granted member:invite at key-mint time, so the key may invite at any role level. WorkOS receives no inviter user ID; the local record stores the key ID for audit. |
| Personal API key | yes | member:invite |
Caller must have an active membership in the target org; pass X-Org-ID header matching the URL path. Privilege escalation guards apply to the caller's role level. |
Error Codes
| Code | HTTP | Description |
|---|---|---|
| authorize.unauthenticated | 401 | No valid auth credentials |
| authorize.forbidden | 403 | Missing member:invite permission or wrong org |
| mfa.step_up_required | 403 | Step-up authentication required (non-member role targets, session only) |
| invite.ip_rate_limited | 429 | Too many requests from this IP |
| invite.org_rate_limited | 429 | Organization invitation limit exceeded |
| invite.invalid_org_id | 400 | Org ID is malformed or has wrong prefix |
| invite.org_not_found | 404 | No organization with this ID |
| invite.unauthorized | 401 | Authentication required |
| invite.org_mismatch | 403 | Not authorized for this organization |
| invite.user_lookup_failed | 500 | Could not resolve calling user |
| invite.member_lookup_failed | 500 | Could not verify caller membership |
| invite.decode_failed | 400 | Malformed JSON body |
| invite.empty_batch | 400 | No invitations provided |
| invite.batch_too_large | 400 | More than 20 invitations in batch |
| invite.duplicate_email | 400 | Same email appears more than once |
| invite.invalid_email | 400 | Email is missing, invalid, or too long |
| invite.self_invite | 400 | Cannot invite yourself |
| invite.invalid_role | 400 | Unknown role slug |
| invite.no_system_role | 400 | No system role provided |
| invite.multiple_system_roles | 400 | More than one system role |
| invite.custom_roles_not_allowed | 400 | Custom roles only with member |
| invite.insufficient_role | 403 | Caller's role level too low or caller doesn't hold assigned custom role |
| invite.already_member | 409 | Invitee is already a member of this organization |
| invite.already_pending | 409 | A pending invitation already exists for this email |
| invite.role_resolve_failed | 400 | Could not resolve role slugs |
| invite.send_failed | 500 | Invitation delivery failed |
Path Parameters
- Type: stringidrequired
Organization ID (e.g. org_01h455vb4pex5vsknk084sn02q)
Body
required
application/json
Invitation entries
- Type: array object[] · InviteEntry[]
A single invitation entry with email and optional roles
Responses
- application/json
- application/json
- application/json
- application/json
- application/json
- application/json
- application/json
- application/json
Request Example for post/orgs/{id}/invitations
curl 'https://api.nametrust.com/orgs/{id}/invitations' \
--request POST \
--header 'Content-Type: application/json' \
--data '[
{
"email": "jane@acme.com",
"role_slugs": [
"member"
]
}
]'
[
{
"object": "invite_result",
"email": "jane@acme.com",
"success": true,
"invitation": {
"object": "invitation",
"id": "inv_01h455vb4pex5vsknk084sn02q",
"email": "jane@acme.com",
"state": "pending",
"role_slugs": [
"member"
],
"expires_at": "2025-07-01T00:00:00Z",
"created_at": "2025-06-01T12:00:00Z"
},
"error": ""
}
]