Kraty

Player tools

Live-ops tools for when something goes wrong in production.

The Player lookup screen (inside any game's nav) is the single search for everything Kraty knows about a player: profile snapshot, recent attempts, reward and crate grants, lobby memberships. It's also where you fix things when they break.

The four admin actions

Every action is permission-gated, audited (with reason captured where applicable), and idempotent where possible. Use them sparingly — they bypass the normal player flow, so reach for them only to fix broken state, not as a routine operation.

Force-complete an attempt

When a player's attempt is stuck in in_progress (client crashed between progress reports, network partition, etc.), force-complete from the attempt row.

You can optionally override the final score. The leaderboard catches up with the new score automatically. Rewards are not auto-rolled — chain with Retry roll if the attempt should produce a grant.

  • Permission: attempt.force_complete (GAME_ADMIN, GAME_DEBUGGER)
  • Audit: attempt.force_complete with the reason in metadata.

Retry the reward roll

When the original roll failed or never happened (e.g., after a force-complete), re-run the reward pipeline from the attempt row.

The route refuses to roll a second time if any grants are already linked to the attempt — there's no duplicate-rewards path.

  • Permission: grant.retry_reward_roll (GAME_ADMIN, GAME_DEBUGGER)
  • Audit: grant.retry_reward_roll with { grantCount } in metadata.

Issue a manual grant

Make-goods, promotional one-offs, and recovery from edge cases. Compose the grant's contents as JSON in the Issue a manual grant card. The grant fires a normal grant.created webhook so your studio backend picks it up the same way as an event-completion grant.

  • Permission: grant.manual_create (GAME_ADMIN)
  • Audit: grant.manual_create with the reason and the external player id in metadata.

Force-claim a grant

Use after verifying server-side that a claim succeeded but the ack never came back (network hiccup mid-deploy on the receiver, etc.). Marks the grant claimed regardless of current status.

  • Permission: grant.manual_force_claim (GAME_ADMIN, GAME_DEBUGGER)
  • Audit: grant.manual_force_claim with full before/after diff.

From your backend (without the portal)

For support workflows that need to read player state programmatically — a customer-service tool, a fraud-review job, a Slack bot — call the player profile endpoint directly with a server_integration API key:

GET /server/v1/players/{externalPlayerId}
Authorization: Bearer <your-client-sdk-key>

Returns the same shape the portal's Player lookup uses:

{
  "data": {
    "player": {
      "id": "...",
      "externalPlayerId": "alice_42",
      "firstSeenAt": "...",
      "lastSeenAt": "...",
      "lastContextSnapshot": { "country": "PT", "level": 7 }
    },
    "attempts": [/* recent N */],
    "grants":   [/* recent N */],
    "lobbies":  [/* recent N */],
    "summary": {
      "attemptCount": 12,
      "attemptsCompleted": 8,
      "attemptsExpired": 1,
      "grantsPending": 2,
      "grantsClaimed": 9,
      "lobbiesActive": 0
    }
  }
}

The endpoint is read-only — no force-complete, no manual grant, no force-claim from the server side. Those mutations need a member session through the portal so they leave an actor trail. If your support team needs to issue make-goods programmatically, use POST /server/v1/players/:p/grants (which already audits the grant and fires the same grant.created webhook).

  • Auth: server_integration API key (a client_sdk key gets 403).
  • Scope: the key's (studio, game) — the path doesn't carry them.
  • Limits: ?limit= caps each of the three lists (1–200, default 50).

When NOT to use these

These tools are for fixing broken state. They aren't for routine operations:

  • For periodic make-goods, generate them server-to-server via POST /server/v1/players/:p/grants, not by hand in the portal.
  • If an attempt regularly needs force-completing, the event config is probably wrong (e.g., target unreachable) — fix the config rather than the symptom.
  • If grants regularly need force-claiming, your webhook receiver is unreliable — fix retries on your side instead of papering over with manual claims.

Walkthrough: resolve a "lost my reward" support ticket

Your support engineer gets a ticket: "I finished the daily race but the gold never arrived." The full path from ticket to resolution, all auditable:

  1. Open the game's Players tab and search for the player by their external id (or the email they signed up with — both are indexed).
  2. The profile shows their last reported context, ban status, and totals. Open the Attempts card and find the one they're complaining about. Its status is one of:
    • completed with grants attached → look at the Grants card; if pending they're waiting for the SDK collectAll() call. Click Force-claim to deliver immediately.
    • completed with no grants attached → the reward roll failed. Click Retry reward roll — idempotent, so the fix lands exactly once even if you double-click.
    • in_progress or expired → the attempt never completed server-side. Click Force-complete with an override score and a reason; the engine rolls rewards as if the attempt finished normally.
  3. For a make-good on top of recovering the original payout, open the Wallet card and click + Credit. Enter the currency key, amount, and an audit reason. The credit appears instantly and emits a wallet.changed webhook to your CRM.
  4. Every action lands in the studio Audit log with the actor, timestamp, and before/after diff. Linking the audit row in your ticketing system gives you a permanent paper trail.

Walkthrough: merge a guest player into an authenticated account

When a guest who's been playing locally signs in with a social provider for the first time, you usually want their progress to follow them — not start over. Run this from your backend, not the portal:

// `@kraty/server-sdk` — server_integration key required.
await server.players.merge('guest_device_abc', 'auth_user_42');

What happens, in one transaction:

  • Attempts and grants are reassigned to the authenticated id.
  • Item quantities are summed (guest had 2 potions, authed had 1 → 3).
  • Wallet balances are summed.
  • Lobby seats are re-pointed.
  • The guest's externalPlayerId is anonymised so the slot can be reused on the next guest signup from the same device.

A single player.merged webhook fires with the full counts so your analytics pipeline can stitch the two journeys together.