POST /v1/orgs/:org_id/invitations
Invite a user to join an organization.
::: tip Auth
Required: cookie or Bearer. Required permission: members.invite. Caller's
org must match the path's org_id.
:::
Request
POST /v1/orgs/:org_id/invitations
| Path param | Type | Notes |
|---|---|---|
org_id | UUID | Must match auth.organization_id. |
Body
{
"email": "carol@example.com",
"role": "member"
}
| Field | Type | Required | Notes |
|---|---|---|---|
email | string | yes | Valid email. |
role | string | yes | One of admin, billing, member, viewer. owner is not grantable through invitations. |
Response
200 OK
{
"invitation": {
"id": "d4f2a9c1-...",
"email": "carol@example.com",
"role": "member",
"status": "pending",
"expires_at": "2026-06-04T12:00:00+00:00",
"invited_by": "1c8b2a5e-...",
"created_at": "2026-05-28T12:00:00+00:00"
}
}
An invitation email is sent best-effort; transport failures don't fail the request.
Error responses
| Status | Code | When |
|---|---|---|
| 400 | invalid_input | Email malformed or role invalid. |
| 401 | authentication_required | No valid auth credential. |
| 403 | forbidden | Path org doesn't match caller's org, OR caller lacks members.invite. |
| 500 | internal_error | DB failure. |
Notes
- TTL: 7 days.
- Tokens are hex-encoded 32-byte random values; only the SHA-256 hash is persisted.
- An audit entry (
members.invited) is recorded.