# Quickstart

### 0. Prerequisites

This guide assumes you've followed the [setup guide](https://docs.play.fun/build-on-play.fun/play-fun-sdk/server-side-sdk/setup) from the previous page.

### 1. Register a Game

First, we will register a new game on play.fun:

```typescript
try {
  const game = await ogp.games.register({
    name: 'My Game Name',
    description: 'A description of more than 10 characters',
    gameUrl: 'https://mygame.com',
    platform: 'HTML', // 'HTML' or 'EXTERNAL_URL'
    isHTMLGame: true, // Optional: Defaults to True
    iframable: true, // Optional: Defaults to True
    twitter: 'https://x.com/mytwitter', // Optional
    discord: 'https://discord.gg/invite', // Optional
    telegram: 'https://t.me/mychannel', // Optional
    maxScorePerSession: 0, // Optional: 0 or null = Unlimited
    maxSessionsPerDay: 0, // Optional: 0 or null = Unlimited
    maxCumulativePointsPerDay: 0, // Optional: 0 or null = Unlimited
    base64Image: '...', // Required if not passing image file
    base64CoverImage: '...', // Optional
  });
  console.log('Game registered:', game.id);
} catch (e) {
  throw e;
}
```

#### Interacting with the Game API

```typescript
/** Fetch a game by ID */
const game = await ogp.games.getById({ gameId });

/** Batch fetch games by ID */
const games = await ogp.games.getManyByIds({ gameIds: ['id1', 'id2'] });

/** Update a game */
const result = await ogp.games.update({
  gameId: game.id,
  name: 'Updated Name',
  description: 'Updated description',
});

/** Get your games (paginated) */
const myGames = await ogp.games.getAuthedGames({
  limit: 50,
  cursor: undefined,
  sort: 'desc',
});

/** Find a game by token ID */
const game = await ogp.games.getByTokenId({ tokenId: 'token-mint-address' });

/** Find games by batch token IDs */
const gamesMap = await ogp.games.getByBatchTokenIds({
  tokenIds: ['token1', 'token2'],
});

/** List all games (paginated) */
const games = await ogp.games.get({
  query: '',
  limit: 50,
  sortDirection: 'desc',
  sortBy: 'totalRewardsAllocatedUsd',
});
```

#### Verify Game Ownership

Without claiming game ownership, you will only earn 5% of allocated creator rewards. By claiming ownership, you gain access to the full creator rewards allocation.

To verify game ownership, you will need to embed the following meta tag in the `head` of your game URL webpage:

```html
<meta name="x-ogp-key" content="your-creator-api-key" id="ogp-key-meta" />
```

Once this is in your webpage head, call the following via the client:

```typescript
await ogp.games.claimOwnership({ gameId });
```

{% hint style="warning" %}
The `claimOwnership` method is not yet implemented in the server SDK. Ownership verification can be done via the Play Fun dashboard.
{% endhint %}

### 2. Submit Points for Players

Use the play module to save points for players from your server. This is the **dev endpoint** — it's for server-to-server integration where your backend validates gameplay and submits scores.

#### Single Player

```typescript
const result = await ogp.play.savePoints({
  gameId: 'your-game-uuid',
  playerId: 'player-identifier',
  points: 1000,
});
console.log('Saved:', result.savedCount);
console.log('OGP ID:', result.playerIdToOgpId['player-identifier']);
```

#### Batch Save (Multiple Players)

```typescript
// Object format: { playerId: points }
const result = await ogp.play.batchSavePoints({
  gameId: 'your-game-uuid',
  pointsRecord: {
    player1: 1000,
    player2: 2000,
    player3: 500,
  },
});

// Array format
const result = await ogp.play.batchSavePoints({
  gameId: 'your-game-uuid',
  pointsRecord: [
    { playerId: 'player1', points: 1000 },
    { playerId: 'player2', points: 2000 },
  ],
});
```

#### Player Identifier Formats

The `playerId` field supports multiple formats. The system will resolve the identifier to an internal OGP user ID and create users if they don't exist:

| Format              | Example                                     | Description                               |
| ------------------- | ------------------------------------------- | ----------------------------------------- |
| **Solana wallet**   | `sol:9qdvVLY3v...` or `solana:9qdvVLY3v...` | Looks up/creates user by Solana address   |
| **Ethereum wallet** | `eth:0x123...` or `ethereum:0x123...`       | Looks up/creates user by ETH address      |
| **Email**           | `email:player@example.com`                  | Looks up/creates user by email            |
| **Twitter/X**       | `twitter:username` or `x:username`          | Looks up/creates user by Twitter/X handle |
| **Privy ID**        | `did:privy:abc123`                          | Direct Privy user ID                      |
| **OGP User ID**     | `550e8400-e29b-41d4-...`                    | Raw UUID — direct internal user ID        |

{% hint style="info" %}
**Performance tip:** The response includes a `playerIdToOgpId` mapping. Cache these mappings and use OGP User IDs (raw UUIDs) in subsequent requests to skip identifier resolution and improve throughput.
{% endhint %}

{% hint style="warning" %}
Identifiers that are not in `type:value` format and are not valid UUIDs will throw an error. Always use one of the supported formats above.
{% endhint %}

#### Response Format

```typescript
interface SavePointsResponse {
  savedCount: number;
  playerIdToOgpId: {
    [playerId: string]: string; // Your ID -> OGP system ID
  };
  message?: string;
}
```

#### Important Notes

* Points are **cumulative** — they add to existing points for the day
* Only the game **owner or creator** can save points
* Points are stored per user, per game, per day
* Maximum 1000 entries per batch request
* Rate limit: 3 requests per second

### 3. Validate Session Tokens

If you're using a [hybrid integration](https://docs.play.fun/build-on-play.fun/play-fun-sdk/hybrid-integration) (client SDK for auth + server SDK for points), you can validate session tokens sent from the client:

```typescript
const { valid, playerId, ogpId, gameId } = await ogp.play.validateSessionToken('player_xxx...');

if (valid) {
  console.log('Privy ID:', playerId); // did:privy:xxx
  console.log('OGP User ID:', ogpId); // Use this for savePoints
  console.log('Game ID:', gameId);
}
```

#### Response Format

```typescript
interface ValidateSessionTokenResponse {
  valid: boolean;
  playerId?: string; // Privy ID (did:privy:xxx)
  gameId?: string; // Game UUID the token was issued for
  ogpId?: string; // Internal OGP user UUID — use as playerId in savePoints
}
```

#### Important Notes

* Session tokens are `player_xxx` format strings issued by the client SDK
* Tokens are scoped per-game and expire after 30 minutes
* The returned `ogpId` can be used directly as the `playerId` in `savePoints` / `batchSavePoints` for best performance

### 4. Query Points & Leaderboard

```typescript
/** Fetch player points for today */
const points = await ogp.play.getPoints({
  gameId: 'your-game-uuid',
  playerId: 'player-identifier', // Same identifier formats as savePoints
});
console.log('Points today:', points.points);

/** Fetch game leaderboard */
const leaderboard = await ogp.play.getLeaderboard({
  gameId: 'your-game-uuid',
});
console.log('Top players:', leaderboard);
```
