Loomio Server API documentation
/api/server0 is for server-level operations. Use /api/user0 for user-oriented actions performed as a Loomio user account.
Use one server key to create and manage accounts, discover groups across the installation, and manage their memberships and roles. No bot account, user API key, or membership in the groups is required. User API keys continue to follow the user's group permissions.
Authentication
The key is the SERVER_API_KEY setting in the server environment. create_env.sh generates one for new installations.
Send the key as a bearer token:
curl -H 'Authorization: Bearer YOUR_SERVER_API_KEY' https://www.loomio.com/api/server0/users
Send credentials only in the Authorization header. API keys in query strings or request bodies are rejected.
Provision accounts and memberships
| Method | Endpoint | Purpose |
|---|---|---|
POST |
/api/server0/users |
Create an account |
GET |
/api/server0/groups |
Discover groups across the installation |
GET |
/api/server0/memberships |
List memberships in a group |
POST |
/api/server0/memberships |
Invite members or synchronise a complete membership list |
PATCH |
/api/server0/memberships/:id |
Change group administrator and delegate roles |
DELETE |
/api/server0/memberships/:id |
Revoke one membership |
Existing account update, deactivation, reactivation, redaction, and deletion endpoints are described below.
Create an account
POST /api/server0/users
Send a user object with a required email. Email addresses are trimmed and converted to lowercase. Optional fields are name, username, and is_admin. Here is_admin means an instance administrator; group administrator roles are managed on memberships. Omit username to generate one automatically. Unknown fields are rejected.
curl https://loomio.example.org/api/server0/users \
-H 'Authorization: Bearer YOUR_SERVER_API_KEY' \
--json '{
"user": {
"email": "ada@example.org",
"name": "Ada Lovelace",
"username": "ada",
"is_admin": false
}
}'
The response is HTTP 201 with a user object. Invalid fields or an email or username already in use return HTTP 422. Creating an account does not send an invitation, sign the person in, or mark their email as verified. Invite them to a group using the membership endpoint to send an invitation through the normal notification workflow.
Discover groups
GET /api/server0/groups
List all groups that have not been deleted, including private groups and groups with expired subscriptions. Results are ordered by ascending ID. Use limit (default and maximum 50) and offset (default 0) to page through them.
curl 'https://loomio.example.org/api/server0/groups?limit=50&offset=0' \
-H 'Authorization: Bearer YOUR_SERVER_API_KEY'
{
"groups": [
{
"id": 123,
"key": "examplegroup",
"name": "Planning group",
"handle": "planning",
"parent_id": null,
"group_privacy": "secret"
}
],
"meta": {
"total": 1
}
}
List memberships
GET /api/server0/memberships?group_id=123
group_id is required. Results include accepted memberships and pending invitations, ordered by ascending membership ID. Revoked memberships are excluded. Use limit (default and maximum 50) and offset (default 0) for pagination.
curl 'https://loomio.example.org/api/server0/memberships?group_id=123&limit=50&offset=0' \
-H 'Authorization: Bearer YOUR_SERVER_API_KEY'
{
"memberships": [
{
"id": 456,
"group_id": 123,
"user_id": 789,
"user_email": "ada@example.org",
"user_name": "Ada Lovelace",
"admin": false,
"delegate": false,
"accepted_at": null,
"pending": true
}
],
"meta": {
"total": 1
}
}
admin and delegate are group roles. accepted_at is the acceptance timestamp, or null while an invitation is pending. This endpoint does not expose invitation tokens, login credentials, or votes.
Invite or synchronise members
POST /api/server0/memberships
Send a required group_id and emails array. By default, this adds the listed people who do not already have an active membership, leaving everyone else in place. Email addresses are trimmed, converted to lowercase, and deduplicated. New addresses create accounts and pending invitations. Existing verified members of another active group in the same organisation are accepted automatically, matching the User API invitation workflow.
curl https://loomio.example.org/api/server0/memberships \
-H 'Authorization: Bearer YOUR_SERVER_API_KEY' \
--json '{
"group_id": 123,
"emails": [
"ada@example.org",
"alex@example.org"
]
}'
To synchronise a complete membership list, explicitly set remove_absent to true. Anyone missing from the list is removed from that group and its subgroups. An empty array with remove_absent: true removes everyone, including group administrators. Omit remove_absent or set it to false when only adding people. The legacy values 1 and 0 are also accepted.
curl https://loomio.example.org/api/server0/memberships \
-H 'Authorization: Bearer YOUR_SERVER_API_KEY' \
--json '{
"group_id": 123,
"emails": [
"ada@example.org"
],
"remove_absent": true
}'
{
"added_emails": [],
"removed_emails": [
"alex@example.org"
]
}
Repeating the same request does not create duplicate memberships or repeat invitations. Reinviting a revoked member restores their group access without restoring a previous administrator role. New invitations and role changes use the normal notification workflow, with no user recorded as the actor.
Missing or malformed email lists and invalid boolean values return HTTP 400. A listed deactivated account returns HTTP 422; reactivate it explicitly before adding it. Invalid account data, subscription limits, or other failures roll back the whole synchronisation, including new accounts and membership removals. Additions require an active group and remain subject to subscription member limits.
Update membership roles
PATCH /api/server0/memberships/:id
Send a membership object containing either or both boolean fields admin and delegate. Omitted roles remain unchanged. Unknown fields are rejected; the endpoint cannot transfer a membership, accept an invitation, or change voting weights.
curl https://loomio.example.org/api/server0/memberships/456 \
-X PATCH \
-H 'Authorization: Bearer YOUR_SERVER_API_KEY' \
--json '{
"membership": {
"admin": true,
"delegate": false
}
}'
Returns a membership object with the same fields as a listed membership. Granting a role creates the usual role notification; repeating an unchanged role does not notify again. Role changes require an active group. Revoked memberships return HTTP 404.
Remove one membership
DELETE /api/server0/memberships/:id
curl https://loomio.example.org/api/server0/memberships/456 \
-X DELETE \
-H 'Authorization: Bearer YOUR_SERVER_API_KEY'
Returns {"success": true}. Removal revokes access in the group and its subgroups, including topic access and voting rights in active polls. It does not delete the account, alter unrelated groups, or remove direct-thread access. Repeating a removal succeeds. You can remove memberships from groups with expired subscriptions; deleted groups return HTTP 404.
User Object
User responses use this shape:
{
"id": 123,
"name": "Ada Lovelace",
"username": "ada",
"email": "ada@example.org",
"is_admin": false,
"active": true,
"deactivated_at": null,
"identities": [
{
"id": 456,
"identity_type": "oauth",
"uid": "external-123",
"email": "ada@example.org",
"name": "Ada Lovelace"
}
]
}
List Users
List all user accounts on the Loomio installation.
GET /api/server0/users
Example
curl -H 'Authorization: Bearer YOUR_SERVER_API_KEY' https://www.loomio.com/api/server0/users
Returns:
{
"users": []
}
Show User
Find a user by their Loomio user ID or external identity.
GET /api/server0/users/:id
GET /api/server0/users/identity/:identity_type/:uid
Examples
By Loomio user ID:
curl -H 'Authorization: Bearer YOUR_SERVER_API_KEY' https://www.loomio.com/api/server0/users/123
By external identity:
curl -H 'Authorization: Bearer YOUR_SERVER_API_KEY' https://www.loomio.com/api/server0/users/identity/oauth/external-123
Returns:
{
"user": {}
}
Update User
Update the profile fields of a user found by their Loomio user ID or external identity.
PATCH /api/server0/users/:id
PATCH /api/server0/users/identity/:identity_type/:uid
Params
| Field | Description |
|---|---|
name |
Display name |
username |
Loomio username |
email |
Email address |
Examples
By Loomio user ID:
curl -X PATCH \
-H 'Authorization: Bearer YOUR_SERVER_API_KEY' \
-H 'Content-Type: application/json' \
-d '{"user":{"name":"Ada Lovelace","username":"ada","email":"ada@example.org"}}' \
https://www.loomio.com/api/server0/users/123
By external identity:
curl -X PATCH \
-H 'Authorization: Bearer YOUR_SERVER_API_KEY' \
-H 'Content-Type: application/json' \
-d '{"user":{"name":"Ada Lovelace","username":"ada","email":"ada@example.org"}}' \
https://www.loomio.com/api/server0/users/identity/oauth/external-123
Returns the updated user:
{
"user": {}
}
Deactivate User
Deactivate a user account found by its Loomio user ID or external identity.
POST /api/server0/users/:id/deactivate
POST /api/server0/users/identity/:identity_type/:uid/deactivate
Examples
By Loomio user ID:
curl -X POST -H 'Authorization: Bearer YOUR_SERVER_API_KEY' https://www.loomio.com/api/server0/users/123/deactivate
By external identity:
curl -X POST -H 'Authorization: Bearer YOUR_SERVER_API_KEY' https://www.loomio.com/api/server0/users/identity/oauth/external-123/deactivate
Returns:
{
"success": true,
"user": {}
}
Reactivate User
Reactivate a deactivated user account found by its Loomio user ID or external identity.
POST /api/server0/users/:id/reactivate
POST /api/server0/users/identity/:identity_type/:uid/reactivate
Examples
By Loomio user ID:
curl -X POST -H 'Authorization: Bearer YOUR_SERVER_API_KEY' https://www.loomio.com/api/server0/users/123/reactivate
By external identity:
curl -X POST -H 'Authorization: Bearer YOUR_SERVER_API_KEY' https://www.loomio.com/api/server0/users/identity/oauth/external-123/reactivate
Returns:
{
"success": true,
"user": {}
}
Redact User
Redaction keeps the user's comments and other user-generated content within their groups, but removes known personally identifying information such as name, bio, profile photo, email address, login credentials, identities, and active sessions.
This is the recommended way to remove a user from Loomio.
POST /api/server0/users/:id/redact
POST /api/server0/users/identity/:identity_type/:uid/redact
Examples
By Loomio user ID:
curl -X POST -H 'Authorization: Bearer YOUR_SERVER_API_KEY' https://www.loomio.com/api/server0/users/123/redact
By external identity:
curl -X POST -H 'Authorization: Bearer YOUR_SERVER_API_KEY' https://www.loomio.com/api/server0/users/identity/oauth/external-123/redact
Returns:
{
"success": true
}
Delete User
Delete removes the user and records the user created. Comments are removed from threads, votes are removed from polls, and groups, discussions, polls, and other records created by the user may also be deleted through database associations.
This is very destructive. Redaction is highly recommended instead.
DELETE /api/server0/users/:id
DELETE /api/server0/users/identity/:identity_type/:uid
Examples
By Loomio user ID:
curl -X DELETE -H 'Authorization: Bearer YOUR_SERVER_API_KEY' https://www.loomio.com/api/server0/users/123
By external identity:
curl -X DELETE -H 'Authorization: Bearer YOUR_SERVER_API_KEY' https://www.loomio.com/api/server0/users/identity/oauth/external-123
Returns:
{
"success": true
}
SSO Profile Sync Settings
Use these settings when another system manages Loomio profile fields.
LOOMIO_DISABLE_EDIT_USER_PROFILE=1
# LOOMIO_SSO_UPDATE_USER_PROFILE_ON_LOGIN=1
LOOMIO_DISABLE_EDIT_USER_PROFILE=1 prevents users from editing these fields themselves:
| Field | Notes |
|---|---|
name |
Managed by external sync |
username |
Managed by external sync |
email |
Managed by external sync |
avatar_kind / uploaded_avatar |
Managed by external sync |
Users can still edit Loomio-local fields such as short_bio and location.
LOOMIO_SSO_UPDATE_USER_PROFILE_ON_LOGIN=1 updates name and email from SSO login data. Leave it commented out or unset when an external sync script should be the only source of those updates.
LOOMIO_SSO_FORCE_USER_ATTRS still works for existing installs. It both disables user edits and updates name and email on SSO login.