Create requires an existing account session on the original device.
Transfer Code
Create and consume a short-lived account transfer code.
Transfer codes let an existing account session bootstrap a new account session on another device without adding public lookup or full authentication.
Create Request
POST /api/v1/accounts/{accountId}/transfer-codes
The original device must send the runtime key and X-Persistly-Account-Session.
POST https://api.persistly.app/api/v1/accounts/acc_01HXYZ/transfer-codes
Authorization: Bearer ps_test_your_runtime_key
X-Persistly-Account-Session: pst_01HSESSION
Content-Type: application/json
{
"deviceLabel": "Old laptop",
"ttlSeconds": 600
}Create Response
201 Created
Show the code once. It is temporary and should not be logged.
HTTP/1.1 201 Created
Content-Type: application/json
{
"transferCode": "P7K2D-M9Q4R",
"expiresAt": "2026-06-01T12:10:00Z",
"expiresInSeconds": 600
}Consume Request
POST /api/v1/account-transfer-codes/consume
The new device sends the player-entered code and receives its own account session.
POST https://api.persistly.app/api/v1/account-transfer-codes/consume
Authorization: Bearer ps_test_your_runtime_key
Content-Type: application/json
{
"transferCode": "P7K2D-M9Q4R",
"deviceLabel": "New tablet"
}Consume Response
200 OK
The response uses the normal account envelope so SDKs can attach and load slots through the existing account model.
HTTP/1.1 200 OK
Content-Type: application/json
{
"accountId": "acc_01HXYZ",
"accountSessionToken": "pst_01HNEWSESSION",
"syncPolicy": {
"minRemoteSyncIntervalSeconds": 60,
"forceSyncCooldownSeconds": 10,
"syncOnAppBackground": true,
"syncOnAppForeground": true,
"syncOnReconnect": true,
"maxQueuedLocalSnapshots": 25
},
"account": {
"accountId": "acc_01HXYZ",
"accountData": {
"diamonds": 1200
},
"slots": [
{
"slotId": "autosave",
"slotInfo": {
"characterName": "Ayla",
"level": 6
},
"version": 2,
"status": "active",
"updatedAt": "2026-06-01T12:01:00Z"
}
],
"version": 2
}
}Rules
Transfer codes are temporary save access, not auth.
Consume requires only the runtime key and the short-lived code.
A consumed or expired code cannot be reused.
The consume response returns a new accountSessionToken for the same account.
Do not use playerRef, externalAccountRef, usernames, or provider subjects as public recovery keys.