Developer docs
Integrate retroshelf into your emulator, launcher, or frontend.
Add play tracking to your app
retroshelf's API lets your emulator, launcher, or app record what a player plays to their retroshelf activity. When a play session ends, send the game, play time, and timestamp, and retroshelf logs it automatically.
Begin by applying for a client ID. Once approved, link players with the device-code flow and send completed play sessions in the background.
Device flow overview
- 1Request a device code with your approved client ID.
- 2Show the user code and verification URL to the user.
- 3Poll the token endpoint until the user approves access.
- 4Send the bearer token with completeLink and syncSessions.
/api/auth/device/codeRequest a device code
Creates a short user code and verification URL for the OAuth device flow.
Fields
| Field | Type | Required | Description |
|---|---|---|---|
| client_id | string | Yes | The client ID issued after your integration application is approved. |
client_idstring · requiredThe client ID issued after your integration application is approved.
Request
{
"client_id": "rs_your_client_id"
}Response
{
"device_code": "internal-device-code",
"user_code": "483921",
"verification_uri": "https://retroshelf.org/link",
"verification_uri_complete": "https://retroshelf.org/link?user_code=483921",
"expires_in": 86400,
"interval": 5
}/api/auth/device/tokenPoll for an access token
Poll at the returned interval until the user approves the code.
Fields
| Field | Type | Required | Description |
|---|---|---|---|
| grant_type | string | Yes | Use urn:ietf:params:oauth:grant-type:device_code. |
| device_code | string | Yes | The internal device code returned by the device-code endpoint. |
| client_id | string | Yes | The same approved client ID used to request the device code. |
grant_typestring · requiredUse urn:ietf:params:oauth:grant-type:device_code.
device_codestring · requiredThe internal device code returned by the device-code endpoint.
client_idstring · requiredThe same approved client ID used to request the device code.
Request
{
"grant_type": "urn:ietf:params:oauth:grant-type:device_code",
"device_code": "internal-device-code",
"client_id": "rs_your_client_id"
}Response
{
"access_token": "bearer_access_token",
"token_type": "Bearer"
}/api/syncClient/completeLinkRegister or reconnect an installation
Creates the sync client record used by later play-session uploads.
Authorization: Bearer access_token
Fields
| Field | Type | Required | Description |
|---|---|---|---|
| name | string | No | A user-visible name for the emulator, launcher, or device installation. |
| platform | string | Yes | Currently supported values: onionos, knulli, muos, rocknix, android, spruceos, batocera, steamos, nextui. |
| fingerprint | string | No | A stable local identifier used to reconnect the same installation. |
| deviceSlug | string | No | Known retroshelf device slug when the integration can identify hardware. |
| metadata | object | No | Version or implementation details useful for support and debugging. |
namestring · optionalA user-visible name for the emulator, launcher, or device installation.
platformstring · requiredCurrently supported values: onionos, knulli, muos, rocknix, android, spruceos, batocera, steamos, nextui.
fingerprintstring · optionalA stable local identifier used to reconnect the same installation.
deviceSlugstring · optionalKnown retroshelf device slug when the integration can identify hardware.
metadataobject · optionalVersion or implementation details useful for support and debugging.
Request
{
"name": "ExampleEmu",
"platform": "android",
"fingerprint": "stable-device-id",
"deviceSlug": "retroid-pocket-5",
"metadata": {
"version": "1.2.3"
}
}Response
{
"syncClientId": "sync_client_id",
"lastSyncCursor": null
}/api/syncClient/syncSessionsUpload play sessions
Sends completed play sessions. Offline clients should queue sessions locally and retry with the same values.
Authorization: Bearer access_token
Fields
| Field | Type | Required | Description |
|---|---|---|---|
| syncClientId | string | Yes | The sync client ID returned by completeLink. |
| sessions[].romPath | string | Yes | The path or filename available to the emulator or launcher. |
| sessions[].duration | number | Yes | Session length in seconds. |
| sessions[].devicePlayedAt | number | Yes | Unix timestamp, in seconds, for when the session started on the device. RetroShelf derives the end by adding duration. |
| sessions[].romName | string | No | Clean game name when available. Used before falling back to romPath matching. |
| sessions[].romHash | string | No | MD5 ROM hash when available. |
| sessions[].steamAppId | string | No | Numeric Steam app ID for exact native Steam game matching. |
| sessions[].platformHint | string | No | Console or platform hint, such as snes, gba, psx, or arcade. |
syncClientIdstring · requiredThe sync client ID returned by completeLink.
sessions[].romPathstring · requiredThe path or filename available to the emulator or launcher.
sessions[].durationnumber · requiredSession length in seconds.
sessions[].devicePlayedAtnumber · requiredUnix timestamp, in seconds, for when the session started on the device. RetroShelf derives the end by adding duration.
sessions[].romNamestring · optionalClean game name when available. Used before falling back to romPath matching.
sessions[].romHashstring · optionalMD5 ROM hash when available.
sessions[].steamAppIdstring · optionalNumeric Steam app ID for exact native Steam game matching.
sessions[].platformHintstring · optionalConsole or platform hint, such as snes, gba, psx, or arcade.
Request
{
"syncClientId": "sync_client_id_from_completeLink",
"sessions": [
{
"romPath": "steam/367520",
"romName": "Hollow Knight",
"steamAppId": "367520",
"duration": 5400,
"devicePlayedAt": 1781229600,
"platformHint": "steam"
}
]
}Response
{
"submitted": 1,
"created": 1,
"synced": 1,
"matched": 1,
"unmatched": 0,
"results": [
{
"romPath": "steam/367520",
"status": "matched",
"syncSessionIds": ["sync_session_id"],
"matched": true,
"gameId": "game_id",
"gameName": "Hollow Knight",
"gameSlug": "hollow-knight",
"coverImageUrl": "https://cdn.retroshelf.org/covers/hollow-knight.webp"
}
]
}created counts newly stored rows; synced counts rows accepted for matching in this request, including an idempotent retry that was already stored but still pending.