Source: https://gameye.com/blog/session-api-v1-2-1/

---
title: "Session API v1.2.1: Token Scopes, Log Streaming, Backfill"
description: "Session API v1.2.1 adds scoped token creation, GET /session filters for backfill, log streaming, artifact downloads, and full application management via API."
---

# Session API v1.2.1: Token Delegation, Filtering, Logs, Artifacts, and Application Management

Seventeen endpoints documented. New additions include scoped token delegation, session filtering with player-count ranges, live log streaming, post-session artifact downloads, and full application management via the API.

[Roberto Sasso](https://gameye.com/blog/author/roberto-sasso/)
_CTO at Gameye_

Sep 20, 2026

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&#x26;image=my-game&#x26;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&#x26;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&#x26;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&#x26;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&#x26;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/](https://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/](https://docs.gameye.com/api-v2/open-api-spec/).

Questions or issues with the API? Reach the team via [gameye.com/contact-us/](https://gameye.com/contact-us/).
