5. Start a Search

Once the WebSocket connection is open, queue the player for a match by sending a begin_search action.

{
  "action": "begin_search",
  "payload": {
    "gameMode": "ranked_1v1"
  }
}

gameMode must match the name of a game mode you created for your tenant in the Config page (see Account Setup) — if it doesn't exist, or isn't enabled, the search is rejected (see below).

What happens next

When the search is registered, the server acknowledges it with SEARCH_STARTED:

{
  "action": "SEARCH_STARTED",
  "data": { "gameMode": "ranked_1v1", "startedAt": 1767225600 }
}

startedAt is the time (epoch seconds) the player's wait is measured from. For ping-based game modes it arrives once your pong completes the search. Sending begin_search again for a search that is already running re-sends it, so a client that missed the acknowledgement can recover.

The server pushes further actions back over the same connection as things happen. Once a match is made, the client receives MATCH_FOUND — see Match Made for that payload and what your backend receives via webhook.

To stop searching before a match is found, send:

{ "action": "cancel_search" }

Possible responses

Anything that isn't a success shows up as an ERROR action:

{
  "action": "ERROR",
  "data": {
    "code": "...",
    "message": "...",
    "details": { }
  }
}

Here's every response type your integration should be prepared to handle, grouped by what it means for you:

Auth / connection issues — can appear on connect, or mid-session if something about the ticket or connection becomes invalid:

  • Bad, expired, or already-used ticket. Request a new ticket from your backend and reconnect — don't retry with the same one.

Account / billing issues — something about the tenant's plan needs attention. Surface these to you (the tenant), not the player:

  • The account has used its matched player allowance for the current billing cycle (TOKEN_QUOTA_EXCEEDED). New searches are rejected and the connection is closed; players already in the queue and matches already made are unaffected. The allowance resets each billing cycle — see how billing works.
  • Billing access has been suspended (e.g. a lapsed grace period after a failed payment).
  • The account is on the signup waitlist and doesn't have matchmaking access yet.

Request issues — something about this specific search request:

  • The requested game mode doesn't exist, or isn't enabled for the tenant's tier.
  • SEARCH_START_FAILED: the search couldn't be started on our side and nothing was queued, so it's safe to send begin_search again.
  • A required player attribute (e.g. mmr, for MMR-based game modes) is missing from the ticket — make sure it's included in attrs when generating the ticket.

Ping-based game modes

If gameMode refers to a Ping-based game mode, the search doesn't join the pool immediately. Instead, expect an unsolicited action from the server:

{
  "action": "PING",
  "data": {
    "correlationId": "..."
  }
}

Reply immediately, echoing the same correlationId and the gameMode you searched with:

{
  "action": "pong",
  "payload": {
    "correlationId": "...",
    "gameMode": "ranked_1v1"
  }
}

gameMode is required on every pong — if the server doesn't recognize the correlationId you're replying to (it can happen, harmlessly, on a very fast reply), it uses gameMode to kick off a fresh ping check on your behalf rather than leaving you stuck. You'll simply receive another PING to reply to.

The round trip time is measured entirely server-side — your client doesn't need to compute or report anything beyond replying as fast as possible. Only once a pong is matched to its correlationId does the search actually enter the matchmaking pool; as with the MMR path, there's no explicit acknowledgement that this succeeded — silence means the player is now searching, and you'll next hear from the server via MATCH_FOUND, ERROR, SEARCH_SUPERSEDED, or CLOSE_CONNECTION.

No server timeout notification

If your client never replies, the server doesn't send anything back — there's no timeout error. Give begin_search a client-side timeout (a few seconds is plenty) and re-send it if you haven't heard anything back by then.

A disconnect while a PING check is outstanding isn't resumable — reconnecting starts the whole flow over from a fresh begin_search, the same as starting a new search would.

Reconnecting from another device

If the same player starts a new search from a second connection (e.g. a second device or tab) while already searching, the new connection takes over and the old one receives:

{ "action": "SEARCH_SUPERSEDED" }

The old connection is then closed. Only one active search per player is supported at a time.

Next

See Match Made for what the client and your backend each receive once a match is formed.