Guild
Membership groups with teeth: clans, teams, crews, or any grouping where who-may-do-what matters. Guilds combine CRUD, three join policies (open, application, invite-only), role-based permissions (leader, officer, member) enforced server-side on every operation, and a per-guild treasury bank denominated in gold.
When to reach for it: game clans with ranks, team rosters where officers moderate membership, communities gated by application review, groups pooling a shared currency balance.
When not to: account-level organizations (billing, invites tied to authentication) belong to Auth organizations; short-lived parties that queue for matches together belong to Matchmaking group tickets.
Concepts
- Guild roles govern membership and treasury - every member is a
leader,officer, ormember, and each operation checks the role server-side. The creator automatically becomes the leader. - Join policy decides what
joindoes:openadmits immediately,applicationfiles a pending application,invite_onlyrejects direct joins outright. Details on Join policies. - Leadership continuity is guarded - a leader cannot be kicked, and the last leader can neither leave nor be demoted. Promote a successor first.
- The bank is a real ledger - integer gold amounts, atomic overdraft protection, and idempotency keys for safe retries, backed by the same ledger engine as the Treasury service.
- The CLI acts as the token's subject -
create,join, andleavefill the acting user from the JWT. Over raw HTTP the body carries it explicitly (created_byon create,user_idon join/leave), and the server requires it to match the verified caller.
Enterprise access
A guild belongs to a tenant and optionally a named workspace (--workspace-id).
public means discoverable by that workspace's readers; at the account root it
means tenant-local discovery. It never means Internet access or access to another
workspace's private resources. private guilds are visible to their members,
owner and explicitly shared readers/editors/managers.
The shared access API supports the guilds target and reader/editor/manager grants,
including groups, service identities and expiry. Editors may edit the profile;
managers control sharing and deletion. These grants do not make someone a guild
officer or leader. Membership review, role changes and treasury operations still
require the corresponding guild role. Applications and invitations have narrower
read policies than the guild profile, including when requested through includes.
snug guild create --name "Research" --workspace-id <workspace> --visibility private
snug access grant --helpCreate and manage
snug guild create --name "Ember Vanguard" --description "Docs guild" \
--visibility public --join-policy open
snug guild get --guild-id <id>
snug guild update --guild-id <id> --description "New blurb" --visibility private
snug guild delete --guild-id <id>Over HTTP, creation is a POST /api/v1/guilds with an explicit created_by;
captured live:
curl -X POST -H "Authorization: Bearer $SNUG_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{"name":"docs4-Iron Pact","created_by":"docs-wave","join_policy":"open"}' \
http://localhost:4000/api/v1/guilds{
"status": 201,
"msg": "Created",
"data": {
"guild_id": "0199e100-0000-7000-8000-0846554b676c",
"name": "docs4-Iron Pact",
"created_at": "2026-08-28T13:27:59.020794Z"
}
}get returns the full profile (policy, visibility, member_count,
max_members, level, xp). Over HTTP, GET /api/v1/guilds/{id}?include=members
hydrates the member list into the same response (the CLI get command has
no include flag). Deleting a guild cascades: its members, applications, and
invitations go with it, and subsequent reads return 404 guild_not_found.
Finding guilds
list speaks the standard list contract,
plus --public-only (public_only=true) to hide private guilds: full-text
-q over name and description, filters on visibility,
join_policy, member_count, level, created_at, and updated_at, and
sorts on created_at (newest first by default), updated_at, name,
member_count, level, and xp:
snug guild list -q vanguard --sort -member_count --limit 5 # then --cursor <page.next_cursor>, or --all
snug guild list --public-only --filter 'join_policy[eq]=open'Membership
snug guild join --guild-id <id> # you, per the JWT subject
snug guild leave --guild-id <id>
snug guild members list --guild-id <id>
snug guild members update-role --guild-id <id> --user-id alice --role officer
snug guild members kick --guild-id <id> --user-id aliceVerified behaviors:
- Joining an open guild admits immediately with
role: "member". Joining twice is409 member_already_exists. - Kicking a leader fails with
403 insufficient_permissions(Cannot kick a leader) - demote them first. - The last leader leaving is
400 cannot_leave_guild, and demoting the last leader is400 cannot_demote_last_leader. Promote another member toleaderfirst; after that the original leader leaves cleanly.
Roles and permissions
With three identities:
| Capability | Leader | Officer | Member |
|---|---|---|---|
| View guild, members, bank balance | yes | yes | yes |
| Invite users, review applications | yes | yes | - |
| Kick members | yes | yes | - |
| Deposit to the bank | yes | yes | - |
| Withdraw from the bank | yes | - | - |
| Change roles, edit profile, delete guild | yes | - | - |
A denied operation is always 403 insufficient_permissions, with the
missing permission named in msg - e.g. an officer calling update-role
gets Missing permission: ManageRoles.
Treasury bank
Each guild has one gold-denominated account. Amounts are integers >= 1 in the smallest currency unit:
snug guild bank balance --guild-id <id>
snug guild bank deposit --guild-id <id> --amount 500 --idempotency-key dep-1
snug guild bank withdraw --guild-id <id> --amount 200{ "guild_id": "0199e100-0000-7000-8000-72b10073ed16", "currency": "gold", "balance": 300, "held": 0, "available": 300 }Withdrawing more than the balance is rejected atomically with
409 insufficient_funds. Repeating a deposit or withdrawal with the same
--idempotency-key returns the committed balance without applying it twice -
verified by replaying a deposit and confirming the balance moved once.
Joining flows
Application review and invitations - who may act, and how decided records behave - have their own page: Join policies.
Limits and configuration
Guilds cap at 100 members and 10,000 guilds per deployment by default, with
name and description bounded at 100 and 1,000 characters - all tunable via
the GUILD_* variables.
Reference
- Guild API - every endpoint, callable
- Related: Auth for account-level organizations, Matchmaking for party-based match tickets, Treasury for the general-purpose ledger the bank is built on
