Start here

Your first match

Create a Roblox matchmaking search with the BirdHouse Luau SDK and handle the matched group in a server script.

This guide creates a two-player match from two solo tickets. Run the integration in a server script; each searching party gets its own session.

Not on Roblox? There is no SDK for other engines. A game server or backend you control runs the same search over HTTP: see Use it from any game server.

Availability. Matchmaking is in early access. These examples are written for SDK version 0.4.0. To run them you need SDK package access and a service endpoint, both arranged with the team; this page does not confirm that a package is published or a service is hosted. Apply for early access.

Before you begin

You need the Roblox SDK installed, HTTP requests enabled in Roblox Studio, and a service base URL supplied for your deployment. The examples use https://matchmaking.example.com as a placeholder.

Every client names one environment, such as dev. Use an environment that is enabled on your service endpoint, and install the package version listed in your access instructions.

Create a client

Use one client for an environment. The default heartbeat interval is five seconds.

local ServerScriptService = game:GetService("ServerScriptService")
local MatchmakingClient = require(ServerScriptService.ServerPackages.Matchmaking)

local client = MatchmakingClient.new({
    baseUrl = "https://matchmaking.example.com",
    environment = "dev",
    heartbeatSeconds = 5,
})

client.Error:Connect(function(problem)
    warn(problem.code, problem.message)
end)

The base URL must not include /api/progress, a query, or a fragment. The SDK appends the endpoint. There is no token or custom-header option in this version.

Use the player’s actual Roblox UserId as a string. The following function creates one solo ticket; call it for each player who intentionally joins the queue.

local function searchForPlayer(player: Player)
    local session = client:CreateSession({
        players = {
            { id = tostring(player.UserId) },
        },
        meta = { mode = "duel" },
        constraints = {
            type = "and",
            constraints = {
                { type = "playerCount", min = 2, max = 2 },
                { type = "fieldEqualsDeep", field = "mode" },
            },
        },
    })

    session.Matched:Connect(function(match)
        print("Match ready", match.id)
        -- Reserve a server and hand off the matched players in your game.
        -- Retain session.Match while retrying any game-level handoff.
    end)

    session.Failed:Connect(function(problem)
        warn("Search ended", problem.code, problem.message)
    end)

    session.StateChanged:Connect(function(state)
        print("Search state", state)
        -- Remove your registry entry on matched, canceled, or failed.
    end)

    return session
end

CreateSession does not yield. It validates and copies the options immediately, then defers network work so listeners connected right away can receive the outcome. Invalid options throw before a session is registered.

Receive a match

Start compatible searches for two different players in the same environment. Each ticket supplies one player, and playerCount requires two players across the complete group.

When a match is ready, Matched receives its ID, environment, and member tickets. Both searches observe the shared match. BirdHouse groups the players; your game decides how to coordinate server allocation and teleportation.

A single two-player party will not satisfy this example by itself. The matcher combines at least two tickets, even if one party already fills the target size.

session:Cancel()

Cancellation is idempotent. An unsent session can finish locally as canceled. Once submission has started, it enters canceling while the SDK asks the service to cancel.

Keep your Matched handler connected until a terminal outcome. Cancellation cannot undo an already published match. See the complete ticket lifecycle.

Handle shutdown

game:BindToClose(function()
    client:Destroy()
end)

Destroy() immediately abandons active sessions and reports client_destroyed. It does not drain or gracefully cancel remote tickets. For ordinary queue exits, call Cancel() and wait for the outcome.

Make it your own

Add a skill window or role composition, maintain a party-to-session registry, and handle player departure in your game. Keep development traffic separate with environments, then review limits and guarantees before shipping.