Developer portal

Ship your game on X1.

Plug any game into the X1 network in under an hour. Your game can live on any stack — embedded, external web, Telegram, mobile, native. Players keep one identity, one XP record, one trust badge.

Submit a Game

Send your game's metadata, launch endpoint, and SDK config for admin review.

Open →

Apply as Studio

Get your studio recognised on X1 before submitting your first game.

Open →

Test Mode

Run synthetic matches and verify XP, achievements, and webhooks.

Open →

Integration Guide

TypeScript-first SDK with copy-paste examples. Source on GitHub.

Open on GitHub ↗

Onboarding in 6 steps

  1. 1

    Connect wallet

    Sign in with your X1 wallet so submissions are tied to your identity.

  2. 2

    Read the SDK

    Clone github.com/x1-labs/x1-games and wire launch params + the match-result webhook.

  3. 3

    Submit your game

    One form covers metadata, launch URL, launch mode, runtime, and trust model.

  4. 4

    Get approved

    An admin reviews and promotes your submission to the catalog at the DEMO tier.

  5. 5

    Run test mode

    Use /developers/test to run synthetic matches and verify your webhook end-to-end.

  6. 6

    Promote to LIVE

    Once your checklist is green, the operator promotes you from DEMO → LIVE.

Drop-in SDK · 3-line integration

<!-- 1. Add this once, in your game's HTML head or before </body> -->
<script src="https://games.x1.xyz/sdk.js"></script>

<!-- 2. When your match ends, call X1.reportResult(...) -->
<script>
  // result: "WIN" | "LOSS" | "DRAW"
  // xpEarned: integer
  X1.reportResult({ result: "WIN", xpEarned: 100 });
</script>

// That's the whole thing. The SDK auto-reads the session token from
// the launch URL, POSTs to /api/match-result, and redirects the player
// back to the hub. Safe to leave in even when your game runs standalone
// — it's a no-op when there's no X1 session on the URL.

// Want a custom end screen first?
X1.reportResultStay({ result: "WIN", xpEarned: 100 });
// ... show your end screen, then:
X1.goBack();

// Want the player's wallet for in-game username?
const wallet = X1.session?.wallet; // null if launched standalone

Managed-mode attestor

Use the platform attestor — don't run your own.

Every game in the catalog registers against a single X1-team- controlled attestor pubkey. The platform signs and submits post_outcome on your behalf when a match settles via the webhook above. You don't need to operate a signer or run an attestor daemon.

Platform attestor pubkey

89nss4jDBfu5gRSiNEYkASjukY5soKf3DqjvjhPWAJ1K

Network

testnet

RPC URL

https://rpc.testnet.x1.xyz

Self-hosted attestation is still supported via the SDK if you have a reason to run your own signer (sovereign infra, experimental modes). Most games should not.

Manual integration · onMatchEnd.ts

Advanced — use the drop-in SDK above instead
// Same effect as X1.reportResult(...), done by hand. Use only when you
// can't include the <script> tag (strict CSP, native shell, etc.).

const params = new URLSearchParams(window.location.search);
const sessionToken = params.get("session");
const returnUrl    = params.get("returnUrl");

async function reportResult(outcome: {
  result: "WIN" | "LOSS" | "DRAW";
  xpEarned: number;
  rankDelta?: number;
  durationSec?: number;
}) {
  if (!sessionToken) return; // game was opened directly, not from the hub
  await fetch("https://games.x1.xyz/api/match-result", {
    method: "POST",
    headers: { "content-type": "application/json" },
    body: JSON.stringify({ sessionToken, ...outcome }),
  });
  if (returnUrl) window.location.href = returnUrl;
}

// Endpoint contract:
//   POST /api/match-result
//   body: { sessionToken, result, xpEarned, rankDelta?, durationSec?,
//           opponent?, prizeLabel? }
//   200 → { ok: true, matchId, player, reward? }
//   401 → invalid or expired session token
//   409 → duplicate session (already settled)