Kraty

Guilds

Persistent groups of up to 50 players: a leader, co-leaders and members, open or request-to-join, and help requests. Kraty runs the rules; your client builds the screens from the SDK.

Guilds (also called clans or teams) are persistent groups of players inside one game. Kraty stores the guilds, enforces every rule (size, roles, unlock gate, cooldowns); your game draws the screens and calls the SDK. Guilds compete in team events rather than on a standing guild score.

The model

  • One guild per player per game, up to 50 members. The 50 is a fixed Kraty limit.
  • Three roles: leader, co-leader, member. The leader can do everything; what co-leaders and members can do is set per game (by default co-leaders accept requests, kick, promote, demote and edit the profile; members can't). Kick, promote and demote only reach a lower rank: co-leaders act on members, never on each other or on the leader.
  • Joining: each guild is open (eligible players join instantly) or by request (a co-leader or the leader accepts). The game sets which one a new guild starts as.
  • Unlock gate: players need a minimum of a progression item (level, trophies, any progression resource; 20 by default) to found or join a guild. Until you pick the item there is no gate, and the dashboard says so.
  • Create cost (optional): founding a guild can cost catalog currencies or items. With platform-managed inventory Kraty charges it in the same step as the guild is created; if your game keeps balances itself, Kraty charges nothing and your game charges the founder (see Who charges costs).
  • Leaders: a leader who leaves hands the guild to the longest-serving co-leader, else the longest-serving member. A leader not seen for 30 days (configurable) is replaced the same way by someone active. The last member to leave disbands the guild.
  • Kicks and leaving: a kicked player can't rejoin that guild for 7 days (configurable). Leaving has no cooldown by default.
  • Test and live are separate: test-key players only ever see test guilds.
  • Environment gate: like events, guilds are disabled, staging (test keys only) or live. A new setup starts in staging.

Setting it up

In the dashboard open Live ops → Guilds and choose Set up guilds. The settings page holds every rule: the state, the unlock item and threshold, the create cost, the role permissions, the timings, a list of words guild names may not contain, help requests and the guild week (when the weekly donation cap resets). Save, try it with a test key, then switch the state to live.

Your client reads the rules instead of hardcoding them:

const config = await kraty.guilds.config();
// config.unlock: { active, itemKey, threshold, balance, eligible }
// config.roles.coLeader: ['accept_requests', 'kick', ...]
// config.createCost, config.costsChargedBy, config.help, config.week, config.maxMembers (50)
// config.joinRequests: { declinedRetryHours, maxPendingPerPlayer, ttlHours }

Who charges costs

The create cost and help donations that cost the donor are generic lists of { type: 'item' | 'currency', key, amount }. config.costsChargedBy says who takes them from the player:

  • platform: Kraty debits the player when the guild is founded or the donation is made. Show the price; Kraty refuses the action if they can't pay.
  • studio: your game keeps balances itself, so Kraty charges nothing. Charge the player yourself, from the client before the call or (safer) from your backend on the guild.created and guild.help_donated webhooks, which carry the cost and costsChargedBy.
const { createCost, costsChargedBy } = await kraty.guilds.config();
if (costsChargedBy === 'studio') await myStore.charge(createCost); // your own economy
const mine = await kraty.guilds.create({ name: 'Night Owls' });

Founding and joining

// Search by name, or show recommendations (guilds with room, friends' guilds first).
const found = await kraty.guilds.search('night owls', { country: 'US' });
const picks = await kraty.guilds.recommended({ limit: 10 });

// Join: open guilds take you now; request guilds get a request.
const result = await kraty.guilds.join(picks[0].guildId, { message: 'Daily player, level 40' });
if (result.status === 'joined') showGuild(result.guild);
else showPending(result.request);

// Or found your own (you lead it).
const mine = await kraty.guilds.create({
  name: 'Night Owls',
  joinType: 'request',
  badge: 'owl_blue', // your own art key; Kraty stores it and hands it back
  country: 'US', // ISO-3166 alpha-2, or leave it out for "International"
});

badge is an opaque key (up to 64 characters) that your game defines: Kraty stores it and returns it on every guild, and your client draws the matching art. country is the country the guild says it is from (an ISO-3166 alpha-2 code such as PT or US), chosen by the founder like the clan location in Clash of Clans; search and recommendations can filter by it with country.

Errors you will want to handle (all on KratyApiError.code):

CodeMeaning
guild_unlock_requiredThe player is below the unlock threshold (details has itemKey, threshold, balance).
already_in_guildOne guild per player.
guild_full50 members.
guild_name_taken / guild_name_rejectedNames are unique per game, ignoring case, accents and look-alike letters (Cyrillic о counts as o, 0 as o), and pass your blocked-words list, compared the same way. Invisible and text-direction characters are refused (validation_failed), except the zero-width joiners emoji and some scripts need between two letters or emoji. A one-word blocked term also catches the name spelled across spaces (bad word), on whole words only.
player_bannedThe player to add, accept, promote or make leader is banned.
guild_insufficient_balanceThe player can't pay the create cost. Nothing was charged.
guild_rejoin_blockedKicked from this guild recently (details.retryAt).
guild_join_cooldownLeft a guild recently, when you set a leave cooldown.
guild_request_retry / guild_request_limitA declined player can ask the same guild again after joinRequests.declinedRetryHours (24 h by default); at most joinRequests.maxPendingPerPlayer (10) pending requests. A player can cancel a request at any time (cancelRequest), and one nobody answers expires after joinRequests.ttlHours (7 days).

Running a guild

const my = await kraty.guilds.mine();          // null when guildless
// my.guild.members: role, joinedAt, lastSeenAt, online, weeklyHelps
// my.membership.permissions: what this player may do

const inbox = await kraty.guilds.requests();   // needs accept_requests
await kraty.guilds.acceptRequest(inbox[0].requestId);
await kraty.guilds.declineRequest(inbox[1].requestId);

await kraty.guilds.promote('player_88');       // member → co-leader
await kraty.guilds.demote('player_88');        // co-leader → member
await kraty.guilds.kick('player_99');
await kraty.guilds.transferLeadership('player_88'); // leader only
await kraty.guilds.updateProfile({ description: 'We raid at 8 pm UTC', joinType: 'open' });

const left = await kraty.guilds.leave();       // { disbanded, newLeader }

guild_permission_denied means the role can't do that here (or the target is not lower-ranked).

Help requests

A member asks guildmates for a resource you allow (lives, cards, energy…); guildmates donate; each donation arrives as an ordinary grant (sourceKind: "guild_help"), and the donor can get a reward (sourceKind: "guild_help_reward"). You set the list of resources, the most per request, the wait between requests, how long a request stays open, the most per donation and the weekly helps counter (donations per player per guild week, 20 by default; leaving a guild doesn't reset it).

A resource can cost the donor (a transfer from their balance; the default, so help never mints by accident) or be minted like a free gift. Progression items (XP, trophies) can't be requested.

const help = await kraty.guilds.help();         // open requests + your counter
await kraty.guilds.requestHelp({ type: 'item', resourceKey: 'life', amount: 5 });
const donation = await kraty.guilds.donate(help.requests[0].requestId);
await kraty.guilds.cancelHelp(myRequestId);

Debug accounts

In the live environment, guilds made only of debug accounts (see Player tools) are hidden from search and recommendations.

From your backend

The server API reads and moderates guilds:

const { guilds } = await kratyServer.guilds.list({ q: 'owls' });
const g = await kratyServer.guilds.get(guildId);
await kratyServer.guilds.update(guildId, { name: 'Clean Name' });
await kratyServer.guilds.removeMember(guildId, 'player_99');
await kratyServer.guilds.setLeader(guildId, 'player_88');
await kratyServer.guilds.disband(guildId);

Webhooks keep your own systems (chat, push, analytics) in step: guild.created, guild.updated, guild.disbanded, guild.member_joined, guild.member_left, guild.role_changed, guild.join_requested, guild.help_requested, guild.help_donated. See Webhooks.

Dashboard and test tools

Live ops → Guilds lists guilds per environment; search by name or by a player id. A guild page shows the roster, pending requests and membership history, and lets you edit the profile, remove a member, make someone leader or disband the guild. The player page shows the player's guild.

For testing, the dashboard API has test-environment tools: POST …/dev/guilds/:guildId/fill { count } adds generated test players, POST …/dev/guilds/:guildId/members/:externalPlayerId/idle { days } makes a member look inactive, and POST …/dev/guilds/sweep-leaders runs the leader hand-over now.