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
Connect wallet
Sign in with your X1 wallet so submissions are tied to your identity.
- 2
Read the SDK
Clone github.com/x1-labs/x1-games and wire launch params + the match-result webhook.
- 3
Submit your game
One form covers metadata, launch URL, launch mode, runtime, and trust model.
- 4
Get approved
An admin reviews and promotes your submission to the catalog at the DEMO tier.
- 5
Run test mode
Use /developers/test to run synthetic matches and verify your webhook end-to-end.
- 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 standaloneManaged-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)