#
Organizations API Reference
All endpoints require a valid Supabase JWT in the Authorization: Bearer <token> header. Organization-scoped endpoints also require X-Organization-ID.
#
Register Organization
POST /organizations/register
Creates a new organization and adds the requesting user as admin.
Request
{
"name": "Acme Corp"
}
Response
{
"success": true,
"data": {
"organization_id": "uuid"
}
}
#
List Organizations
GET /organizations/list
Returns all organizations the authenticated user belongs to.
Response
{
"success": true,
"data": [
{
"id": "uuid",
"name": "Acme Corp",
"role": "admin"
}
]
}
#
Get Organization Settings
GET /organizations/settings
Returns settings for the current organization.
Response
{
"success": true,
"data": {
"id": "uuid",
"name": "Acme Corp",
"logo_url": "https://...",
"currency": "USD"
}
}
#
Upload Organization Logo
POST /organizations/logo
Uploads a logo image for the organization. Admin only.
Request: multipart/form-data with a file field.
Response
{
"success": true,
"data": {
"logo_url": "https://..."
}
}
#
Get User Language
GET /organizations/user/language
Returns the authenticated user's preferred UI language.
Response
{
"success": true,
"data": {
"language": "en"
}
}
#
Update User Language
PUT /organizations/user/language
Updates the authenticated user's preferred UI language.
Request
{
"language": "ru"
}
Response
{
"success": true,
"data": {
"language": "ru"
}
}
#
Send Team Invitations
POST /organizations/team/invite
Sends email invitations to join the organization. Admin only. Max 3 per request.
Invitations expire after 7 days. Re-inviting an expired address is allowed.
Request
{
"invites": [
{ "email": "user@example.com", "role": "editor" }
]
}
Allowed roles: viewer, editor, admin.
Response
{
"success": true,
"data": {
"sent": [{ "email": "user@example.com", "role": "editor" }],
"failed": []
}
}
#
List Team Members
GET /organizations/team/members
Returns all members of the current organization sorted by email.
Response
{
"success": true,
"data": [
{ "id": "uuid", "email": "user@example.com" }
]
}
Data source: organization_members_view
#
Pending Invites Count
GET /organizations/team/pending-invites/count
Returns the count of pending (sent, not accepted, not expired) invitations for the current organization. Used to show the badge in the Settings sidebar.
Response
{
"success": true,
"data": {
"count": 3
}
}
Data source: organization_invites where status = 'pending', accepted_at IS NULL, expires_at > now()
#
Accept Team Invitation
POST /organizations/team/invite/accept
Accepts a pending team invitation and adds the user to the organization. No X-Organization-ID header required — the user may not belong to any org yet.
Request
{
"invite_id": "uuid"
}
Validation:
- Invite must exist and have status
pending - Invite must not be expired (expired invites are auto-marked as
expired) - Authenticated user's email must match the invite email (case-insensitive)
Response (success)
{
"success": true,
"data": {
"organization_id": "uuid",
"organization_name": "Acme Corp"
}
}
Response (already a member) — idempotent, returns success:
{
"success": true,
"data": {
"organization_id": "uuid",
"organization_name": "Acme Corp",
"message": "You are already a member of this organization."
}
}
Race conditions are handled gracefully — concurrent accept requests return success.
#
Decline Team Invitation
POST /organizations/team/invite/decline
Declines a pending team invitation. No X-Organization-ID header required.
Request
{
"invite_id": "uuid"
}
Validation:
- Invite must exist
- Authenticated user's email must match the invite email (case-insensitive)
- Invite must have status
pending
Response
{
"success": true,
"data": {
"message": "Invitation declined successfully."
}
}
#
Remove Team Member
POST /organizations/team/members/remove
Removes a member from the organization. Owner or Admin only. Cannot remove yourself. Cannot remove the last admin.
Request
{
"user_id": "uuid"
}
Response
{
"success": true,
"data": {
"message": "Member removed successfully."
}
}