Skip to content

Guild

Try the interactive example ↓

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, or member, and each operation checks the role server-side. The creator automatically becomes the leader.
  • Join policy decides what join does: open admits immediately, application files a pending application, invite_only rejects 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, and leave fill the acting user from the JWT. Over raw HTTP the body carries it explicitly (created_by on create, user_id on 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 --help

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": "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 alice

Verified behaviors:

  • Joining an open guild admits immediately with role: "member". Joining twice is 409 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 is 400 cannot_demote_last_leader. Promote another member to leader first; after that the original leader leaves cleanly.

Roles and permissions

With three identities:

CapabilityLeaderOfficerMember
View guild, members, bank balanceyesyesyes
Invite users, review applicationsyesyes-
Kick membersyesyes-
Deposit to the bankyesyes-
Withdraw from the bankyes--
Change roles, edit profile, delete guildyes--

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

Interactive example

Manage groups with membership, applications, and roles. Give leaders a way to admit members and hand responsibility to a successor.

An illustrative example that runs in your browser. No account or API calls required.