Skip to content

Sync Endpoint

POST /nexori/sync is Nexori's backend matchmaking heartbeat.

It lets a backend observe the current server snapshot, inspect backend-driven queue members, acknowledge assignment ACKs, and return match assignments that Nexori should attempt to launch.

This endpoint is only used for backend-driven matchmaking. Local FIFO queues do not need it.

Recommended Multi-Server Setup

In the recommended multi-server setup, this endpoint runs on lobby/queue servers. It exposes queued players and receives INITIAL_MATCH or BACKFILL assignments.

Arena/minigame servers do not need syncEnabled=true just to support backfill. They should publish backfill visibility through POST /nexori/matches/state.

When Nexori Calls It

Nexori calls this endpoint when:

  • backend config has syncEnabled=true
  • baseUrl and serverToken are configured
  • the server process reaches its next syncIntervalMs
  • no previous sync request is still in flight

One server process sends at most one sync request per configured interval. The interval is controlled by syncIntervalMs; for example, 1000 means the server attempts roughly one heartbeat every second.

This is global per server process, not per player, queue, arena, portal, or entity tick.

http
POST <baseUrl>/nexori/sync
Authorization: Bearer <serverToken>
Content-Type: application/json
X-Nexori-Server-Id: <serverId>
X-Nexori-Sync-Id: <syncId>
X-Nexori-Sequence: <sequence>
X-Nexori-Sent-At-Epoch-Ms: <sentAtEpochMs>

Required Headers

HeaderRequiredSourceDescription
AuthorizationYesNexori configMust be Bearer <serverToken>. Your backend should reject missing or invalid tokens.
Content-TypeYesNexoriAlways application/json.
X-Nexori-Server-IdYesNexori server identitySame value as body serverId. Useful for logs and auth policy.
X-Nexori-Sync-IdYesNexori sync runtimeSame value as body syncId. Unique per sync attempt.
X-Nexori-SequenceYesNexori durable assignment storeSame value as body sequence. Monotonic per server process/store.
X-Nexori-Sent-At-Epoch-MsYesNexoriSame value as body sentAtEpochMs. Unix epoch milliseconds.

Your backend should validate that the trace headers match the body so request logs, idempotency checks, and debugging all refer to the same sync attempt.

Request Payload

json
{
  "schemaVersion": 1,
  "syncId": "8c80e2d9-4f6e-4f62-bd9f-f02fdad9d43d",
  "sequence": 123,
  "sentAtEpochMs": 1760000000000,
  "serverId": "7b2fd2f5-50a5-4d0b-8e62-dc2dc82e9bb9",
  "server": {
    "fingerprint": "server-fingerprint",
    "connectionAddress": "lobby.example.com:21918",
    "role": "SERVER",
    "region": "us-east"
  },
  "queues": [
    {
      "queueId": "duel_sword",
      "displayName": "Sword Duel",
      "minPlayers": 2,
      "maxPlayers": 2,
      "countdownSeconds": 5,
      "launchTravelProfileId": "default",
      "matchmakingMode": "BACKEND_DRIVEN",
      "enabled": true,
      "arenaIds": ["duel_arena_01"],
      "runtime": {
        "phase": "WAITING",
        "countdownEndsAtEpochMs": 0,
        "readyAtEpochMs": 0,
        "lastStateChangeEpochMs": 1760000000000,
        "lastLaunchAttemptAtEpochMs": 0,
        "lastLaunchError": "",
        "waitingMembers": [
          {
            "playerUuid": "11111111-1111-1111-1111-111111111111",
            "playerNameSnapshot": "PlayerOne",
            "sourceLobbyId": "main_lobby",
            "sourcePortalId": "duel_portal",
            "joinedAtEpochMs": 1760000000000
          }
        ],
        "readyMembers": []
      }
    }
  ],
  "arenas": [
    {
      "arenaId": "duel_arena_01",
      "displayName": "Duel Arena 01",
      "destinationConnectionAddress": "arena.example.com:21918",
      "destinationTargetId": "arena-server-01",
      "instanceTemplateId": "DuelTemplate",
      "maxSupportedPlayers": 2,
      "enabled": true
    }
  ],
  "activeMatches": [
    {
      "matchId": "nexori-match-001",
      "queueId": "duel_sword",
      "arenaId": "duel_arena_01",
      "expectedPlayerCount": 2,
      "arrivedPlayerCount": 1,
      "activePlayerCount": 1,
      "createdAtEpochMs": 1760000000000,
      "updatedAtEpochMs": 1760000005000,
      "lastError": ""
    }
  ],
  "assignmentAcks": [
    {
      "ackId": "ack-001",
      "assignmentId": "assign-001",
      "externalMatchId": "backend-match-001",
      "status": "LAUNCHED",
      "localMatchId": "nexori-match-001",
      "reason": "",
      "createdAtEpochMs": 1760000000000
    }
  ]
}

Request Fields

FieldType / RequiredSourceDescription
schemaVersioninteger / YesNexoriPayload schema version. Current value is 1.
syncIdstring UUID / YesNexoriUnique id for this sync attempt. Also sent in X-Nexori-Sync-Id.
sequenceinteger / YesNexoriMonotonic sync sequence from Nexori's durable backend assignment store. Also sent in X-Nexori-Sequence.
sentAtEpochMsinteger / YesNexoriTime the request was created, in Unix epoch milliseconds.
serverIdstring UUID / YesNexori server identityStable id for the server process sending the heartbeat.
serverobject / YesNexoriSnapshot of the sending server.
queuesarray / YesNexori queue runtimeAll known queues and their runtime state when available.
arenasarray / YesNexori arena configAll known arenas/games available to launch.
activeMatchesarray / YesNexori arena runtimeActive match snapshots currently known by this server.
assignmentAcksarray / YesNexori durable assignment storePending assignment ACKs Nexori wants your backend to acknowledge.

server Fields

FieldType / RequiredSourceDescription
fingerprintstring / YesNexori server identityServer fingerprint for diagnostics and trust correlation.
connectionAddressstring / YesNexori local address serviceAddress this server advertises for travel, if known.
rolestring / YesNexori server identityCurrent role marker sent by Nexori. In the current contract this value is SERVER.
regionstring / YesNexori backend configManual routing hint configured by the operator. Can be blank.

queues[] Fields

FieldType / RequiredSourceDescription
queueIdstring / YesNexori queue configStable queue id. Use this when returning assignments.
displayNamestring / YesNexori queue configHuman-readable queue name.
minPlayersinteger / YesNexori queue configMinimum players needed for a valid match.
maxPlayersinteger / YesNexori queue configMaximum players allowed by this queue.
countdownSecondsinteger / YesNexori queue configLocal countdown value. Backend-driven queues do not rely on local countdown to decide assignment.
launchTravelProfileIdstring / YesNexori queue configTravel profile used by Nexori when launching.
matchmakingModestring / YesNexori queue configLOCAL_FIFO or BACKEND_DRIVEN. Your backend should only assign players from BACKEND_DRIVEN queues.
enabledboolean / YesNexori queue configWhether the queue is enabled.
arenaIdsarray of string / YesNexori queue configArena ids this queue is allowed to launch into. Assignments must use one of these ids.
runtimeobject or null / YesNexori queue runtimeCurrent queue state. May be null if the queue has no runtime state yet.

runtime Fields

FieldType / RequiredSourceDescription
phasestring / YesNexori queue runtimeCurrent queue phase, such as WAITING, READY, or a launch-related phase. Treat as diagnostic unless your backend needs it.
countdownEndsAtEpochMsinteger / YesNexori queue runtimeLocal countdown end time. Usually not used for backend-driven assignment decisions.
readyAtEpochMsinteger / YesNexori queue runtimeTime the queue became ready locally, if applicable.
lastStateChangeEpochMsinteger / YesNexori queue runtimeLast runtime state transition time.
lastLaunchAttemptAtEpochMsinteger / YesNexori queue runtimeLast local launch attempt time.
lastLaunchErrorstring / YesNexori queue runtimeLast launch error known to Nexori, if any.
waitingMembersarray / YesNexori queue runtimePlayers currently waiting in the queue. Backend-driven matchmakers usually read from here.
readyMembersarray / YesNexori queue runtimePlayers considered ready by runtime. Backend-driven matchmakers may include them depending on policy.

waitingMembers[] And readyMembers[] Fields

FieldType / RequiredSourceDescription
playerUuidstring UUID / YesNexori queue runtimePlayer UUID to use in assignments.
playerNameSnapshotstring / YesNexori queue runtimeLast known player name snapshot. Do not use as identity.
sourceLobbyIdstring / YesNexori queue/runtime sourceLobby id where the player joined, if known.
sourcePortalIdstring / YesNexori queue/runtime sourcePortal id used to join the queue, if known.
joinedAtEpochMsinteger / YesNexori queue runtimeTime the player joined the queue. Useful for FIFO or timeout logic.

arenas[] Fields

FieldType / RequiredSourceDescription
arenaIdstring / YesNexori arena configStable arena/game id. Assignments must reference an arena id known to Nexori.
displayNamestring / YesNexori arena configHuman-readable arena name.
destinationConnectionAddressstring / YesNexori arena configDestination server address Nexori will travel players to.
destinationTargetIdstring / YesNexori arena configDestination target id used by Nexori travel.
instanceTemplateIdstring / YesNexori arena configInstance template Nexori should create or materialize.
maxSupportedPlayersinteger / YesNexori arena configMaximum players supported by this arena. Assignments above this number are rejected.
enabledboolean / YesNexori arena configWhether this arena is enabled. Disabled arenas should not be assigned.

activeMatches[] Fields

activeMatches[] is a local snapshot for the server sending sync. Backends should not rely on lobby sync activeMatches[] as the main cross-server open-match registry. For cross-server backfill, use POST /nexori/matches/state.

FieldType / RequiredSourceDescription
matchIdstring / YesNexori arena runtimeLocal Nexori match id.
queueIdstring / YesNexori arena runtimeQueue that launched the match.
arenaIdstring / YesNexori arena runtimeArena where the match is running.
expectedPlayerCountinteger / YesNexori arena runtimeNumber of players expected by the match.
arrivedPlayerCountinteger / YesNexori arena runtimeNumber of players that have reached the arena server runtime.
activePlayerCountinteger / YesNexori arena runtimeNumber of players still considered active by Nexori.
createdAtEpochMsinteger / YesNexori arena runtimeMatch creation time.
updatedAtEpochMsinteger / YesNexori arena runtimeLast match runtime update time.
lastErrorstring / YesNexori arena runtimeLast runtime error known to Nexori, if any.

assignmentAcks[] Fields

FieldType / RequiredSourceDescription
ackIdstring / YesNexori durable assignment storeUnique ACK id. Return it in acknowledgedAssignmentAckIds once your backend has processed it.
assignmentIdstring / YesBackend assignment, persisted by NexoriOriginal backend assignment id.
externalMatchIdstring / YesBackend assignment, persisted by NexoriBackend-owned match id from the assignment.
statusstring enum / YesNexori assignment executionLAUNCHED, REJECTED, or FAILED.
localMatchIdstring / YesNexori assignment executionNexori-owned local match id when launch succeeded. Blank for rejected/failed assignments.
reasonstring / YesNexori assignment executionHuman-readable reason for rejection/failure, or blank on success.
createdAtEpochMsinteger / YesNexori durable assignment storeTime the ACK was created.

Response Payload

Your backend must respond with JSON for any successful 2xx sync response.

json
{
  "schemaVersion": 1,
  "receivedSequence": 123,
  "acknowledgedAssignmentAckIds": ["ack-001"],
  "assignments": [
    {
      "assignmentType": "INITIAL_MATCH",
      "assignmentId": "assign-002",
      "matchId": "backend-match-002",
      "externalMatchId": "backend-match-002",
      "type": "CREATE_MATCH",
      "queueId": "duel_sword",
      "playerUuids": [
        "11111111-1111-1111-1111-111111111111",
        "22222222-2222-2222-2222-222222222222"
      ],
      "expectedPlayerUuids": [
        "11111111-1111-1111-1111-111111111111",
        "22222222-2222-2222-2222-222222222222"
      ],
      "arenaId": "duel_arena_01",
      "players": [],
      "reportingServerId": "",
      "targetConnectionAddress": "",
      "modeId": "duel",
      "kitId": "sword",
      "ranked": true,
      "metadata": {
        "ratingBucket": "gold",
        "region": "us-east"
      }
    }
  ]
}

Response Fields

FieldType / RequiredSourceDescription
schemaVersioninteger / YesBackendResponse schema version. Use 1.
receivedSequenceinteger / YesBackendEcho the request sequence your backend processed.
acknowledgedAssignmentAckIdsarray of string / YesBackendACK ids from request assignmentAcks that your backend has processed and durably accepted. Nexori removes these from its pending ACK store.
assignmentsarray / YesBackendAssignments Nexori should attempt to launch. Use an empty array when no match should launch.

assignments[] Fields

FieldType / RequiredSourceDescription
assignmentTypestring enum / YesBackendINITIAL_MATCH or BACKFILL. Legacy CREATE_MATCH responses can omit this only for initial-match behavior.
assignmentIdstring / YesBackendUnique id for this assignment. Never reuse with different content.
matchIdstring / YesBackendBackend-owned shared match id. Required for modern backend-driven flow.
externalMatchIdstring / YesBackendBackend-owned match id preserved by Nexori for reporting and reconciliation. In most modern backends this should match or align with matchId.
typestring enum / YesBackendCREATE_MATCH for INITIAL_MATCH. JOIN_MATCH or BACKFILL is accepted for BACKFILL.
queueIdstring / YesBackend, from request queue snapshotQueue to launch from. Must exist and be BACKEND_DRIVEN.
playerUuidsarray of UUID string / YesBackend, from queue membersPlayers assigned to this instruction. For BACKFILL, this should align with players[].playerUuid.
expectedPlayerUuidsarray of UUID string / YesBackendInitial roster model for INITIAL_MATCH. Use an empty array for BACKFILL.
arenaIdstring / YesBackend, from request arena snapshotArena Nexori should launch. Must exist and belong to the queue's arenaIds.
playersarray / YesBackendPer-player backfill tickets. Required for BACKFILL; use an empty array for INITIAL_MATCH.
reportingServerIdstring / YesBackendArena/reporting server that owns the existing match for BACKFILL. Blank for INITIAL_MATCH if unused.
targetConnectionAddressstring / YesBackendExplicit arena-server travel address for BACKFILL. Blank for INITIAL_MATCH if unused.
modeIdstring / YesBackendOptional backend/gameplay hint. Nexori transports it as assignment metadata but does not own your matchmaking algorithm. Use blank string if unused.
kitIdstring / YesBackendOptional backend/gameplay hint. Use blank string if unused.
rankedboolean / YesBackendWhether your backend considers this assignment ranked.
metadataobject / YesBackendBackend-owned JSON object. Nexori does not interpret mode-specific matchmaking metadata.

players[] Fields

FieldType / RequiredSourceDescription
playerUuidstring UUID / YesBackendPlayer being admitted through backfill.
admissionReservationIdstring / YesBackend reservation systemOne reservation id per player and per slot.
admissionExpiresAtEpochMsinteger / YesBackend reservation systemExpiration deadline for that player's admission ticket.

Assignment Validation In Nexori

Nexori validates every assignment before launching:

  • assignmentId must not have been processed before with different content
  • assignmentType must be INITIAL_MATCH or BACKFILL
  • INITIAL_MATCH must use CREATE_MATCH
  • BACKFILL must use JOIN_MATCH, BACKFILL, or leave type compatible with backfill handling
  • matchId must be a valid backend-owned match id
  • queueId must exist
  • queue must be BACKEND_DRIVEN
  • every player must be online
  • every player must still be in that queue
  • arenaId must exist
  • arena must belong to the queue's arenaIds
  • arena must be enabled
  • playerUuids.length must be less than or equal to arena.maxSupportedPlayers
  • players must not already be in another active match
  • destination travel data must be parseable by Nexori
  • BACKFILL must include targetConnectionAddress
  • BACKFILL must include one admissionReservationId and one positive admissionExpiresAtEpochMs per player
  • INITIAL_MATCH player set must be a subset of expectedPlayerUuids when expectedPlayerUuids is provided

If validation or launch fails, Nexori creates an assignment ACK with REJECTED or FAILED.

Idempotency Rules

Request Idempotency

Each sync has a unique syncId and a monotonic sequence. The backend should treat (serverId, sequence) or syncId as request trace identity for logs and duplicate protection.

Sync is a heartbeat, so the backend should be tolerant of repeated, delayed, or skipped heartbeats.

Assignment Idempotency

assignmentId is the idempotency key for assignments.

  • Same assignmentId with the same payload: Nexori does not launch it again.
  • Same assignmentId with different payload: Nexori rejects it as suspicious and ACKs rejection.
  • New assignment attempt: use a new assignmentId.

ACK Idempotency

ackId is the idempotency key for ACK processing.

The backend should only include an id in acknowledgedAssignmentAckIds after it has safely processed that ACK. Nexori will keep sending pending ACKs until they are acknowledged.

Status Code Behavior

Backend statusResponse bodyWhat it meansWhat Nexori doesRetry?
200 to 299Required, parseable sync responseSync accepted.Parses response, acknowledges returned ACK ids, processes assignments.Next heartbeat at syncIntervalMs.
400Ignored by NexoriBad request or malformed payload.Marks sync unhealthy. No assignments processed.Yes, later heartbeat.
401Ignored by NexoriMissing bearer token.Marks auth failed.Yes, after auth backoff.
403Ignored by NexoriInvalid/forbidden bearer token.Marks auth forbidden.Yes, after auth backoff.
422Ignored by NexoriPayload is valid JSON but semantically invalid.Marks sync unhealthy. No assignments processed.Yes, later heartbeat.
429Ignored by NexoriBackend rate limited the heartbeat.Marks sync unhealthy.Yes, later heartbeat.
500 to 599Ignored by NexoriBackend unavailable or failed.Marks sync unhealthy.Yes, later heartbeat.
Timeout/no responseNoneBackend did not answer in requestTimeoutMs.Clears in-flight state with a stale guard.Yes, later heartbeat.
2xx with empty/invalid bodyInvalidBackend returned success but not the required schema.Marks sync unhealthy. No assignments processed.Yes, later heartbeat.

For sync, non-success responses do not permanently stop future heartbeats. Nexori will keep trying while syncEnabled=true and the config remains usable.

Backend Implementation Rules

  • Authenticate every request with the bearer token.
  • Validate trace headers against the body.
  • Only assign players from BACKEND_DRIVEN queues.
  • Only assign players currently present in waitingMembers or readyMembers.
  • Only assign arenas listed in that queue's arenaIds.
  • Keep assignmentId stable and unique.
  • Do not reuse an assignmentId with changed payload.
  • Persist processed ackId values before returning them in acknowledgedAssignmentAckIds.
  • Return an empty assignments array when no match should be launched.

Example Flow

  1. Player A enters a backend-driven queue.
  2. Nexori sends /nexori/sync with Player A in waitingMembers.
  3. Backend sees not enough players and returns no assignments.
  4. Player B enters the same queue.
  5. Nexori sends the next /nexori/sync with both players.
  6. Backend chooses an arena and returns one INITIAL_MATCH assignment.
  7. Nexori validates the assignment.
  8. Nexori launches the match and records an ACK:
    • status="LAUNCHED"
    • localMatchId="<nexori-match-id>"
  9. Nexori includes that ACK in later assignmentAcks.
  10. Backend stores the ACK and returns its ackId in acknowledgedAssignmentAckIds.
  11. Nexori removes the ACK from its pending ACK store.

Backfill Flow

For real backfill:

  1. A backend-driven match is already running on an arena server.
  2. Nexori reports its admission visibility through /nexori/matches/state.
  3. Another player joins the same synced queue from the same or another lobby server.
  4. The backend chooses the existing match instead of creating a new one.
  5. The backend returns a BACKFILL assignment with:
    • assignmentType="BACKFILL"
    • matchId
    • externalMatchId
    • reportingServerId
    • targetConnectionAddress
    • one players[] ticket per player
  6. Nexori validates the ticket, launches travel to the owning arena server, and attempts to admit the player into the existing match runtime.