Version 1.2.1 of the Session API is a substantial expansion. The original surface was deliberately minimal — start a session, stop a session, check available locations — and that’s fine for getting started. But studios building production backends need more: a way to issue short-lived tokens to clients, filters that support backfill, live log access, post-match file retrieval, and the ability to drive their build pipeline through the API rather than the admin panel. All of that is now in the spec.
This post walks through what’s new, why each addition exists, and where it fits in a real integration.
Scoped token delegation — POST /token
The API has always been bearer-token authenticated. In practice this created a tension: your matchmaker needs a long-lived token to start and stop sessions, but if any component that calls the API is compromised, that same token can do everything. The fix is scope delegation.
curl -X POST https://api.production-gameye.gameye.net/token \
-H 'Authorization: Bearer YOUR_MASTER_TOKEN' \
-H 'Content-Type: application/json' \
-d '{
"name": "matchmaker-session-start",
"scopes": ["session:start", "session:read"],
"expires_after": "24h"
}'
The token returned from this call holds only the scopes you requested — and only scopes your calling token already holds. You cannot escalate. A token with session:start and session:read cannot stop sessions, stream logs, or create further tokens.
The expires_after field accepts duration strings: "30m", "2h", "2h30m". Short-lived tokens are the right choice for anything that runs in a client-side context or flows through an intermediate service you don’t fully control.
Use case — game client allocation: Your game client calls your matchmaker, your matchmaker starts a session with a master token, then issues a 10-minute session:read-only token and returns it to the client alongside the connection details. The client can poll session state without touching anything destructive, and the token expires before the match ends.
Use case — CI/CD pipelines: Your deploy pipeline pushes a new build, then calls POST /token to get a short-lived application:write token. That token enables the new tag, then expires. The CI job never holds a master token.
Available scopes: session:start, session:read, session:stop, artifact:read, logs:read, regions:read, token:write, application:read, application:write.
Session filtering — GET /session
The list endpoint now accepts query parameters. Previously it returned every active session under your token; useful for observability, not for operational decisions.
| Parameter | What it does |
|---|---|
location | Filter by region |
image | Filter by application name |
tag | Filter by image version |
host | Filter by host IP |
playerCount[lt] | Sessions with fewer than N players |
playerCount[gt] | Sessions with more than N players |
filter[key]=value | Match on a specific label |
all=true | Include stopped sessions |
Use case — backfill: The playerCount filters exist specifically for backfill. When a player queue can’t fill a full lobby, you want to find in-progress sessions in the right region that aren’t at capacity yet.
GET /session?location=europe&image=my-game&playerCount[lt]=8
That returns every session in Europe running your image with fewer than 8 players. Your matchmaker can direct new players into existing sessions rather than starting cold allocations.
Use case — label-based routing: If you encode game mode or map rotation into session labels at start time, filter[game-mode]=ranked lets you find sessions of a specific type without pulling and iterating the full list server-side.
curl 'https://api.production-gameye.gameye.net/session?filter[game-mode]=ranked&location=us-east' \
-H 'Authorization: Bearer YOUR_TOKEN'
Describe a single session — GET /session/{id}
Fetches the full state of one session by ID. Returns everything the list endpoint returns, plus the current container status (created, running, restarting, exited, dead, draining, shuttingdown, server_unreachable) and the tracked player list from any PUT /session/player/join calls you’ve made.
The status field is useful for health checks: your watchdog process can poll GET /session/{id} and act when the status transitions to server_unreachable or dead — restart the session, alert your on-call, or drain the lobby.
Live log streaming — GET /logs
GET /logs?id=SESSION_ID&follow=true
Streams the container’s stdout and stderr. Without follow, it returns everything the container has written so far and closes the connection. With follow=true, the connection stays open and new output is streamed as it arrives — the equivalent of docker logs -f against the session.
This is primarily a debugging tool during development: start a session, open a terminal with the log stream, reproduce the crash. It’s also useful for operational monitoring if your server writes structured log lines that your infra team wants to tail.
For high-volume production log pipelines, the log stream is the raw feed. If you need structured ingestion into your logging stack, wire up the stream to a log forwarder rather than consuming it directly.
Requires the logs:read scope.
Artifact download — GET /artifacts
After a session terminates, the underlying container is still present on the host for a window before it’s cleaned up. During that window you can download any file or directory from the container filesystem.
curl 'https://api.production-gameye.gameye.net/artifacts?session=SESSION_ID&path=/home/server/logs/crash.log' \
-H 'Authorization: Bearer YOUR_TOKEN' \
--output crash.tar.gz
The path is an absolute Unix path inside the container. The response is a .tar.gz archive. 100 MB limit per request.
Use case — crash dump retrieval: Your game server writes a .dmp file to a known path on crash. Your orchestration layer listens for exited status on the session, then immediately calls GET /artifacts to pull the dump before the container is removed. You get the file without needing any shared storage mounted at runtime.
Use case — match recording: If your server records match replays or scorecard data to a local path, you can pull those files post-session the same way — no S3 integration required for the server binary itself, just a GET /artifacts call from your backend once the session ends.
Requires the artifact:read scope.
Region discovery — GET /region
Returns the full list of region names available to your account. Use this if you’re building dynamic region selection in your matchmaker rather than hardcoding strings, or if you want to validate a requested region before starting a session.
{
"regions": ["europe", "us-east", "us-west", "ap-southeast"]
}
Pair with GET /available-location/{image} (the existing endpoint) to get pingable IP addresses per location — your client pings each returned IP over UDP, measures latency, and submits the results to your matchmaker for placement.
GET /available-location/my-game-server
Returns:
{
"locations": [
{ "id": "europe", "ips": ["185.123.x.x"] },
{ "id": "us-east", "ips": ["12.34.x.x"] }
]
}
Do not cache the available-location response — it reflects live capacity and changes as infrastructure availability shifts.
Application management via API
Previously, creating and managing application registrations (the mapping of a container image to its resource limits, region availability, and port bindings) required the admin panel. That’s fine during initial setup, but it blocks automation.
The five new application endpoints close that gap.
Create — POST /application
curl -X POST https://api.production-gameye.gameye.net/application \
-H 'Authorization: Bearer YOUR_TOKEN' \
-H 'Content-Type: application/json' \
-d '{
"name": "my-game-server",
"registry": "dockerhub",
"repository": "myorg/my-game-server",
"tagKeep": 5,
"networkMode": "bridge",
"nodePool": "default",
"regions": ["europe", "us-east"],
"limits": { "cpu": 2.0, "ram": 4096 },
"reservation": { "cpu": 1.0, "ram": 2048 },
"ports": [
{ "requested": 7777, "protocol": "udp", "tls": false }
]
}'
limits sets the hard ceiling on container resources. reservation is used by the scheduler — it’s what Gameye commits to having available on a host before placing the container there. Set reservation at typical load and limits at the true maximum you’ve tested your server against.
Update — PUT /application/{name}
Partial updates. Only include fields you want to change. Useful for expanding regions as you validate new geographies, or updating tagKeep as your release cadence changes.
curl -X PUT https://api.production-gameye.gameye.net/application/my-game-server \
-H 'Authorization: Bearer YOUR_TOKEN' \
-H 'Content-Type: application/json' \
-d '{ "regions": ["europe", "us-east", "ap-southeast"] }'
Enable a tag — POST /application/{name}/tags
Pre-loading a tag distributes the image layer cache across the Gameye fleet in the regions your application is configured for. Sessions using a pre-loaded tag start faster because the pull has already happened.
curl -X POST https://api.production-gameye.gameye.net/application/my-game-server/tags \
-H 'Authorization: Bearer YOUR_TOKEN' \
-H 'Content-Type: application/json' \
-d '{ "tag": "v1.3.0" }'
Use case — CI/CD pipeline integration: Your build pipeline completes, pushes the image to your registry, then calls POST /application/{name}/tags with the new tag. Pre-loading begins immediately. By the time your QA pass finishes and you’re ready to go live, the image is already distributed and the first sessions start without a cold-pull delay.
If you call this endpoint with a tag that’s already enabled, it triggers a re-pull — useful when you push a fix to an existing tag.
List upstream tags — GET /application/{name}/tags/available
GET /application/my-game-server/tags/available?page=1&pageSize=20
Returns the tags that exist in your upstream registry. Paginated. Useful for building tag selection UIs or for programmatic validation in pipelines — confirm the tag exists in the registry before calling enable.
Player tracking — PUT /session/player/join and DELETE /session/player/leave
These endpoints have been in the spec but were documented with incorrect HTTP methods. The correct methods are PUT for join and DELETE for leave.
# Register players joining a session
curl -X PUT https://api.production-gameye.gameye.net/session/player/join \
-H 'Authorization: Bearer YOUR_TOKEN' \
-H 'Content-Type: application/json' \
-d '{
"session": "SESSION_ID",
"players": ["player-uuid-1", "player-uuid-2"]
}'
The playerCount returned in GET /session and GET /session/{id} reflects these calls. If you’re using playerCount[lt] filters for backfill, you need to be calling these endpoints as players connect and disconnect.
POST /session — field corrections
Two corrections to the session start endpoint worth knowing:
ttl is a duration string, not an integer. The format is "1h", "30m", or "2h30m". Values under a minute are rounded up to a minute. Earlier documentation described it as an integer in seconds — that was wrong.
{
"location": "europe",
"image": "my-game-server",
"ttl": "2h"
}
external_id is now documented. This optional field lets you attach your own identifier to the session — a match ID, a lobby ID, a tracking reference from your backend. It’s stored on the session and returned in describe/list responses, making it easier to correlate Gameye session IDs with your own systems without managing the mapping yourself.
Full API reference
Every endpoint in this release is documented at docs.gameye.com/api-v2/, including request/response schemas and code examples for each. The full OpenAPI spec is available at docs.gameye.com/api-v2/open-api-spec/.
Questions or issues with the API? Reach the team via gameye.com/contact-us/.