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. Every example on this page was executed against a live server.
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
- Roles are the permission system - 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).
Create 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": "gpBfMsmcATaFGFejtHxp",
"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 shared search grammar:
full-text -q over name and description, repeatable
--filter field:operator:value, --sort-by (created_at, member_count,
level, name), plus --public-only:
snug guild list -q vanguard --sort-by member_count --sort-order desc --page-size 5
snug guild list --public-only --filter join_policy:eq:openMembership
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
Verified against a live server 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": "GCELutDuFrtYWagyxaTn", "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 in the
Guild CONFIG reference.
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