Org Invitations
Org admins can invite people to join their organization by issuing single-use, tokenized invitation links. Tokens are stored hashed on the server — the plaintext is shown to the inviter exactly once, on creation, and the inviter must capture it then (or share the bundled signup_url).
This is how a multi-person company onboards onto ButtrBase. The first member creates the org as part of their own signup (org_choice: { type: "create", ... }); subsequent members are added via invitations issued from inside the org.
---
1. Create an invitation (admin only)
Endpoint: POST /api/organizations/{org_uuid}/invitations
Caller must be authenticated and be a member of the org with role admin or owner.
curl -X POST https://api.buttrbase.com/api/organizations//invitations \
-H "Authorization: Bearer " \
-H "Content-Type: application/json" \
-d '{
"email": "alice@example.com",
"role": "member",
"expires_in_hours": 168
}'
All three body fields are optional. Defaults:
email: null (the invitation is an "open link" — anyone with the token can use it; useful for #onboarding channels)role: "member"expires_in_hours: 168 (7 days). Server clamps to[1, 720].
Response (200):
{
"data": {
"id": 42,
"org_uuid": "...",
"email": "alice@example.com",
"role": "member",
"expires_at": "2026-06-10T15:00:00Z",
"token": "Bd9...",
"signup_url": "https://buttrbase.com/signup?invite=Bd9..."
}
}
The token field is shown once. Server only stores SHA-256(token). Save the signup_url and share it with the invitee — that link drops them straight into the accept-invite path of the signup flow with the field prefilled.
---
2. Preview an invitation (public)
Before submitting, a signup page typically calls this to show "you're invited to Acme Inc as member":
Endpoint: GET /api/auth/invitations/{token}
{
"data": {
"org_uuid": "...",
"org_name": "Acme Inc",
"email": "alice@example.com",
"role": "member",
"expires_at": "2026-06-10T15:00:00Z",
"valid": true,
"invalid_reason": null
}
}
If valid: false, invalid_reason is one of expired | accepted | revoked | not_found.
---
3. Accept an invitation
There are two accept paths depending on whether the user already has an account:
3a. Brand-new user
They consume the invitation as part of registration via org_choice:
{
"org_choice": { "type": "accept_invite", "invitation_token": "Bd9..." }
}
posted to POST /api/auth/finalize-registration alongside the usual signup_token + email + password.
3b. Already-signed-in user joining an additional org
Endpoint: POST /api/auth/invitations/{token}/accept
(Authenticated.) Idempotent — accepting twice from the same user returns the same response without duplicating membership.
---
4. List + revoke
Endpoint: GET /api/organizations/{org_uuid}/invitations (admin only)
Returns all invitations for the org with their current status.
Endpoint: DELETE /api/organizations/{org_uuid}/invitations/{invitation_id} (admin only)
Marks the invitation revoked. Already-accepted invitations stay accepted; only pending invitations are revocable.