> ## Documentation Index
> Fetch the complete documentation index at: https://docs.zerodash.studio/llms.txt
> Use this file to discover all available pages before exploring further.

# Game launch

> Turn a player clicking a game tile into a running session.

Zero-Dash does not hand out static game URLs. You mint one per launch, bound to the player's session token and IP address, and open it in the browser.

## The flow

```mermaid theme={null}
sequenceDiagram
    autonumber
    actor P as Player
    participant O as Your platform
    participant Z as Zero-Dash
    participant G as Game

    P->>O: Clicks a game tile
    O->>O: Mint or reuse a session token
    O->>Z: GET /api/v1/games/{slug}?token&currency&ipAddress&lang
    Z-->>O: 200 { data: { url } }
    O-->>P: window.open(url) — new tab
    P->>G: Game loads
    G->>Z: Opens session with the launch URL
    Z->>O: GET /player?gameId&token
    O-->>Z: 200 accountId, displayName, balance
    G-->>P: Game is playable
```

Note what happens at the end: the [Player authorization](/api-reference/callbacks/player) callback fires **after** the launch URL is opened. If that endpoint is not working, the launch URL still returns `200` and the game still opens — and then fails to start. When a launch appears to hang, check your `/player` handler first.

## Launching

<Steps>
  <Step title="Get the game slug" icon="tag">
    From [List games](/api-reference/customer/list-games), cached. Slugs are stable, human-readable identifiers such as `lucky-duck`.
  </Step>

  <Step title="Mint a session token" icon="key">
    Unique, non-deterministic, valid for at least 2 hours. See [Session token](/launch/session-token).
  </Step>

  <Step title="Request the launch URL" icon="link">
    Call [Obtain game launch URL](/api-reference/customer/game-launch-url) with the token, the player's currency and the player's **real** IP address.

    ```http theme={null}
    GET /api/v1/games/lucky-duck?ipAddress=1.2.3.4&freeToPlay=false&currency=USD&token=abc123&lang=en
    X-Operator: your-operator-id
    X-Zd-Signature: 0c8857b40faa035e…
    X-Zd-Timestamp: 1778920901644
    ```

    ```json theme={null}
    {
      "data": { "url": "https://games.zerodash.studio/lucky-duck?s=eyJhbGciOi..." }
    }
    ```
  </Step>

  <Step title="Open it" icon="arrow-up-right-from-square">
    A new tab or window is the supported path. An iframe works but carries [requirements and caveats](/launch/iframe).
  </Step>
</Steps>

<Warning>
  Call this endpoint **at click time**, not ahead of time. The URL is tied to the token and IP you passed in — pre-generating URLs for a whole catalogue page produces links that break as soon as anything about the session changes.
</Warning>

## Launch parameters

| Parameter         | Required        | Notes                                                                           |
| ----------------- | --------------- | ------------------------------------------------------------------------------- |
| `gameSlug` (path) | Yes             | From the games list                                                             |
| `ipAddress`       | Yes             | The **player's browser** IP, not your server's — it drives country restrictions |
| `freeToPlay`      | Yes             | `false` for real money, `true` for [demo mode](/launch/free-to-play)            |
| `currency`        | Real money only | ISO-4217. Omit when `freeToPlay=true`                                           |
| `token`           | Real money only | Your session token. Omit when `freeToPlay=true`                                 |
| `lang`            | No              | ISO 639-1. Autodetected when omitted                                            |

<Tip>
  Behind a CDN or load balancer, `ipAddress` must be the leftmost trustworthy entry of `X-Forwarded-For`, not the socket peer. Send your own egress IP and every player looks like they are in your datacentre — country restrictions will apply to the wrong jurisdiction.
</Tip>

## Opening the game

<Tabs>
  <Tab title="New tab — preferred">
    Best compatibility and the best experience on every device.

    ```javascript theme={null}
    // Open the tab synchronously in the click handler, then navigate it.
    // Doing the fetch first gets the popup blocked in Safari and Firefox.
    function launchGame(slug) {
      const tab = window.open('', '_blank');
      fetch(`/api/games/${slug}/launch`, { method: 'POST' })
        .then((r) => r.json())
        .then(({ url }) => { tab.location.href = url; })
        .catch(() => tab.close());
    }
    ```

    <Note>
      The popup blocker only allows `window.open` during a user gesture. Open the blank tab first, resolve the URL after — the pattern above is the reason this works.
    </Note>
  </Tab>

  <Tab title="Iframe — not recommended">
    Supported, but you own the sizing and the browser quirks. Read [Iframe integration](/launch/iframe) before choosing this.

    ```html theme={null}
    <iframe
      src="https://games.zerodash.studio/lucky-duck?s=..."
      allow="fullscreen"
      style="width: 100lvw; height: 95lvh; border: 0;"
    ></iframe>
    ```
  </Tab>
</Tabs>

## Failure modes

<AccordionGroup>
  <Accordion title="404 on the launch URL request" icon="circle-question">
    The slug is unknown or the game is not enabled for your account. Refresh your cached catalogue; if the slug is there and still fails, the game is not provisioned for you — contact integrations.
  </Accordion>

  <Accordion title="Launch URL returns 200 but the game will not start" icon="hourglass-half">
    Almost always your `/player` callback: unreachable, returning a non-200, failing signature verification, or returning a balance in the wrong currency. Check your callback logs for a `GET /player` around the launch time.
  </Accordion>

  <Accordion title="The game opens for some players and not others" icon="globe">
    Country restrictions, driven by `ipAddress`. Check `restrictedCountries` on the game in your cached catalogue against where the affected players actually are.
  </Accordion>

  <Accordion title="The player is bounced back to the lobby mid-session" icon="right-from-bracket">
    The session token expired or was invalidated. Tokens need at least 2 hours of validity and must survive a client reload. See [Session token](/launch/session-token).
  </Accordion>
</AccordionGroup>
