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

# EOS Lobby Integration

> Use Epic Online Services (EOS) lobbies for grouping and identity, and launch the match server on PlayFlow when the lobby owner starts the game.

This guide shows you how to pair **Epic Online Services (EOS) lobbies** with PlayFlow game-server hosting. Players group up in an EOS lobby; when the lobby owner starts the match, a dedicated server is launched on PlayFlow and its address is shared back through **EOS Lobby Properties** so everyone can connect.

The PlayFlow side is a handful of REST calls, so this works with any engine — Unreal, Unity, or custom — that can call the EOS lobby APIs and make HTTP requests.

<Warning>
  **Never ship a PlayFlow server key (`pf_...`) inside a game client.** A client holding it could start unlimited servers on your account. Route the "start the server" call through a **trusted service you control** (your backend, or a serverless function). See [Keeping the API key safe](#keeping-the-api-key-safe) below for the exact options PlayFlow gives you.
</Warning>

Before getting started, we expect that:

* You have a working EOS lobby flow (players can create/join a lobby and read [lobby & member properties](https://dev.epicgames.com/docs/game-services/lobbies)).
* You have a PlayFlow project and an uploaded **dedicated Linux server build**. See [Docker builds](/guides/docker-builds).

## How it works

```mermaid theme={null}
sequenceDiagram
    participant O as Lobby Owner (client)
    participant SV as Your trusted service
    participant PL as PlayFlow API
    participant M as Lobby Members (EOS)

    O->>SV: "Start match" (lobby id, region)
    SV->>PL: POST /v3/servers/start
    PL-->>SV: instance_id + network_ports (status: launching)
    SV->>PL: Poll GET /v3/servers/{id} until status = running
    SV->>M: Set host:port as EOS Lobby Properties
    M->>PL: Connect to server
    Note over O,SV: Match ends
    SV->>M: Unset EOS Lobby Properties
    SV->>PL: DELETE /v3/servers/{id}
```

## Step 1 — Choose a region

Edgegap places servers from a list of player IPs. **PlayFlow instead takes an explicit `region`** (one of [13 regions](/fundamentals/regions)) — there is no IP-based auto-placement. Pick one of these approaches:

* **Simple:** use a fixed region per lobby (for example, a region the players chose in a menu).
* **Geo-aware:** have each member store a coarse location (from their platform or a client-side lookup) as an EOS **member property**, then have your service average them and pick the nearest PlayFlow region. The nearest-region helper from the [PlayFab Bridge guide](/guides/playfab-bridge) works unchanged here.

<Info>
  `region` is **required** on `POST /v3/servers/start`. If you can't determine a good region, default to the one closest to most of your player base.
</Info>

## Step 2 — Start the server when the owner starts the match

When the lobby owner decides the match is ready, your trusted service starts a PlayFlow server. PlayFlow allocates the network ports up front, so the response already contains the address — you just have to wait for the server to finish booting before handing it out.

```bash Start the server theme={null}
curl -X POST "https://api.computeflow.cloud/api/v3/servers/start" \
  -H "api-key: pf_your_server_key" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "lobby-<EOS_LOBBY_ID>",
    "region": "us-east",
    "version_tag": "your-build-name",
    "ttl": 3600,
    "environment_variables": {
      "EOS_LOBBY_ID": "<EOS_LOBBY_ID>"
    }
  }'
```

```json Response (abridged) theme={null}
{
  "instance_id": "a1b2c3d4-...",
  "status": "launching",
  "region": "us-east",
  "network_ports": [
    {
      "name": "game",
      "internal_port": 7777,
      "external_port": 30412,
      "protocol": "udp",
      "host": "203.0.113.10",
      "tls_enabled": false
    }
  ]
}
```

<Tip>
  Pass a `ttl` (seconds) as a safety net so an abandoned lobby can't leave a server running forever. `version_tag` is the **name** of the build you uploaded to PlayFlow.
</Tip>

## Step 3 — Wait for `running`, then publish the address

The server starts as `launching`. Poll its status until it reports `running` — that's your "ready" signal — then read the address for your named port from `network_ports` and store it as EOS Lobby Properties.

```bash Poll until ready theme={null}
curl "https://api.computeflow.cloud/api/v3/servers/a1b2c3d4-..." \
  -H "api-key: pf_your_server_key"
# repeat every ~2s until "status": "running"
```

From the port named `game`, take `host` and `external_port` and set them as EOS Lobby Properties (for example `SERVER_HOST` and `SERVER_PORT`). EOS notifies every member of the property change.

<Info>
  For **UDP** ports (most game netcode), `host` is a proxy IP and `external_port` is the allocated port. For **TCP** ports, `host` is a PlayFlow relay hostname. Always read both from the response — never hardcode a port; it is allocated per server.
</Info>

## Step 4 — Members connect

When members receive the EOS property-changed notification for `SERVER_HOST` / `SERVER_PORT`, they connect their netcode transport to that address and join the match. Because you only publish the address once the server is `running`, members never receive a dead endpoint.

## Step 5 — End the match (and rematch)

When the match is over, your service clears the EOS Lobby Properties (signalling everyone to disconnect gracefully) and terminates the PlayFlow server:

```bash Terminate the server theme={null}
curl -X DELETE "https://api.computeflow.cloud/api/v3/servers/a1b2c3d4-...?shutdown_reason=MATCH_ENDED" \
  -H "api-key: pf_your_server_key"
```

If the players stay in the lobby, repeat from **Step 2** to run a rematch with the same group. You can also let the server terminate itself — every PlayFlow server is handed `PLAYFLOW_API_KEY`, `PLAYFLOW_API_URL`, and `PLAYFLOW_INSTANCE_ID`, so it can call `DELETE /v3/servers/$PLAYFLOW_INSTANCE_ID` when the last player leaves.

## Keeping the API key safe

The security concern is the same as any host: a client that can start servers can be abused. PlayFlow gives you two key types:

| Key            | Prefix      | Use                                                                                                               |
| -------------- | ----------- | ----------------------------------------------------------------------------------------------------------------- |
| **Server key** | `pf_`       | Full access. Keep it **only** in a trusted server-side context (your service).                                    |
| **Client key** | `pfclient_` | Safe to ship in clients. Can use player-facing lobby/read endpoints; **cannot** touch project settings or builds. |

By default a client key *can* start/stop servers. If you want a trusted service to be the **only** thing that can launch servers, set `client_key_server_control` to `false` in your project settings — server lifecycle calls then require a server key, closing the door on client-triggered starts entirely.

**Recommended:** the lobby owner's client asks your trusted service to start the match; the service holds the server key and makes the PlayFlow calls. The client never sees `pf_...`.

<Info>
  Prefer not to run your own service? PlayFlow's [native lobbies and matchmaking](/lobbies/overview) launch the server for you and deliver connection details over a real-time channel — no external key handling required.
</Info>
