> ## 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.

# Deploy from Unity Matchmaker

> Keep your Unity Game Services (UGS) Matchmaker and deploy the match servers to PlayFlow via a custom allocator.

Follow this guide if you want to keep the **Unity Game Services (UGS) Matchmaker** and use **PlayFlow** as your server host. When the matchmaker assembles a match, a small Cloud Code **allocator** you deploy calls PlayFlow to start a server and returns the connection details to the matched players.

<Info>
  Prefer an all-in-one path? PlayFlow has native [matchmaking](/lobbies/matchmaking) with [Unity SDK support](/unity/matchmaking) — no UGS required. Use this guide when you specifically want to keep the Unity Matchmaker.
</Info>

Before getting started, we expect that:

* You have a working UGS Matchmaker setup. Refer to [Unity's Matchmaker documentation](https://docs.unity.com/ugs/manual/matchmaker/manual/matchmaker-overview) for matchmaking topics.
* You have a PlayFlow project and an uploaded **dedicated Linux server build**. See [Docker builds](/guides/docker-builds).

<Warning>
  Unlike Multiplay, there is no pre-built "PlayFlow allocator" in Unity's hosting-providers repo — you author a small Cloud Code allocator (below). It's just an HTTP call to PlayFlow, so this stays a lightweight, fully custom integration.
</Warning>

## How it works

<Steps>
  <Step title="Matchmaker assembles a match">
    UGS Matchmaker groups players and invokes your allocator Cloud Code function with the matched player list.
  </Step>

  <Step title="Allocator starts a PlayFlow server">
    Your allocator calls `POST /api/v3/servers/start`. PlayFlow reserves the ports immediately and returns the `instance_id` and `network_ports`.
  </Step>

  <Step title="Allocator publishes the assignment">
    The allocator waits for the server to reach `running`, then stores the `host:port` for the match (in UGS Cloud Save / your game data) keyed so the matched players can find it.
  </Step>

  <Step title="Clients connect">
    Each client retrieves its assignment and connects.
  </Step>
</Steps>

## Step 1 — Store your PlayFlow key as a UGS secret

In the [Unity Cloud dashboard](https://cloud.unity.com/), under **Administration → Secrets**, add:

* `PLAYFLOW_API_KEY` — a PlayFlow **server key** (`pf_...`) from your project's API Keys.

The allocator runs in Cloud Code (server-side), so the key is never exposed to clients.

## Step 2 — Region selection

Edgegap places servers from player IP addresses; **PlayFlow takes an explicit `region`** instead (one of [13](/fundamentals/regions)) — there is no IP-based placement.

Have each player attach a coarse location or preferred region as **matchmaking ticket custom data**, then pick a region in the allocator:

```csharp Attach region hint to the ticket theme={null}
var ticketOptions = new CreateTicketOptions(
    queueName: "my-queue",
    attributes: new Dictionary<string, object>
    {
        { "region_hint", "us-east" } // or a lat/long your allocator maps to a region
    }
);
```

<Info>
  If you send lat/long instead of a region name, reuse the `nearestRegion()` helper from the [PlayFab Bridge guide](/guides/playfab-bridge) to map the match's average location to one of PlayFlow's regions.
</Info>

## Step 3 — The allocator

Deploy a Cloud Code function that the matchmaker calls when a match is assembled. Its PlayFlow responsibilities are:

1. Start a server with the matched players and chosen region.
2. Wait until the server is `running`.
3. Store the connection details so clients can look them up.

The PlayFlow calls are identical to any REST client:

```javascript Allocator (Cloud Code) — PlayFlow calls theme={null}
const PLAYFLOW_API = "https://api.computeflow.cloud/api";

async function startPlayFlowServer({ apiKey, region, buildName, matchId, playerIds }) {
  // 1. Start the server
  const start = await fetch(`${PLAYFLOW_API}/v3/servers/start`, {
    method: "POST",
    headers: { "api-key": apiKey, "Content-Type": "application/json" },
    body: JSON.stringify({
      name: `match-${matchId}`,
      region,
      version_tag: buildName,          // the build NAME you uploaded
      ttl: 3600,                       // safety net
      environment_variables: {
        UGS_MATCH_ID: matchId,
        UGS_PLAYER_IDS: playerIds.join(","),
      },
    }),
  }).then((r) => r.json());

  // 2. Wait for the server to be ready
  let instance = start;
  while (instance.status !== "running") {
    await new Promise((res) => setTimeout(res, 2000));
    instance = await fetch(`${PLAYFLOW_API}/v3/servers/${start.instance_id}`, {
      headers: { "api-key": apiKey },
    }).then((r) => r.json());
    if (instance.status === "stopped") throw new Error("Server failed to start");
  }

  // 3. Return the public address of the "game" port
  const game = instance.network_ports.find((p) => p.name === "game");
  return {
    instanceId: instance.instance_id,
    host: game.host,
    port: game.external_port,
  };
}
```

<Tip>
  Because PlayFlow returns `network_ports` **synchronously** from the start call, you don't need Edgegap's deployment-id-plus-webhook dance. The only wait is for `status` to flip to `running`. For very high match rates, kick off the poll asynchronously and store the assignment when it completes rather than blocking the allocator.
</Tip>

Store `{ host, port, instanceId }` against the match (UGS Cloud Save, a database, or the matchmaker's assignment results) so the matched players — looked up by `UGS_PLAYER_IDS` — can retrieve it.

<Info>
  For the **UGS-side contract** — how your allocator is registered, invoked by the matchmaker, and how assignment results are returned to clients — follow Unity's official [hosting-providers allocator template](https://github.com/Unity-Technologies/matchmaker-hosting-providers). This guide only covers the PlayFlow calls that go inside it.
</Info>

## Step 4 — Retrieve the assignment on the client

Clients fetch their assignment (via the matchmaker's standard assignment flow or by reading the game data your allocator wrote), then connect their transport to `host:port`:

```csharp Connect once assigned theme={null}
InstanceFinder.TransportManager.Transport.SetClientAddress(host);
InstanceFinder.TransportManager.Transport.SetPort(port);
InstanceFinder.ClientManager.StartConnection();
```

## Step 5 — Clean up

When the match ends, terminate the server so you stop paying for it:

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

The server can also stop itself — PlayFlow injects `PLAYFLOW_API_KEY`, `PLAYFLOW_API_URL`, and `PLAYFLOW_INSTANCE_ID` into the container, so your headless build can call `DELETE /v3/servers/$PLAYFLOW_INSTANCE_ID` when the last player disconnects. Setting a `ttl` at start time guarantees cleanup even if nothing calls delete.
