Key to live game in an afternoon.
Everything on this page is live today. Shapes are abbreviated for reading; the full REST and OpenAPI reference is at /docs.
Create an app, mint a key.
Register your app in the console and create a secret key. Keys are tenant-scoped: yours cannot see another app's players, and nobody else's can see yours.
# psk_ keys are secrets. Server-side only, never in a client build.
export PARLOR_KEY="psk_YOUR_KEY"
Provision a player, mint a token.
Players are provisioned by your user ids. Parlor never sees emails or names; it assigns each player a generated handle and a parts-based avatar. Player tokens expire, so mint one from your server when a session starts.
curl -X POST https://activities.daeila.com/v1/players/tokens \
-H "Authorization: Bearer psk_YOUR_KEY" \
-d '{"external_id":"user_42"}'
# response, abbreviated
{
"token": "ppt_...",
"expires_at": "2026-07-14T08:15:00Z"
}
Hand the ppt_ token to your client. It authorizes that one player, briefly, and nothing else.
Read the catalog.
The catalog lists what the network can play. Today that is chess: live 3+2 blitz, and a quick mode, a three-puzzle drill bounded under three minutes.
curl https://activities.daeila.com/v1/catalog \
-H "Authorization: Bearer ppt_PLAYER_TOKEN"
Queue up, then speak socket.
The client joins the matchmaking queue with its player token. Matching is rating-banded and the band widens while you wait; if a fair human match has not filled in about 15 seconds, you know in time to offer a bot fallback. When a match fills, the client opens the match WebSocket.
| Direction | Message | What happens |
|---|---|---|
| Client sends | {"type":"move","uci":"g1f3"} |
Validated server-side against the authoritative position. Illegal moves are rejected and the position does not change. |
| Client sends | {"type":"resign"} |
Ends the game immediately. The result and rating change commit in one transaction. |
| Server pushes | state frame | The authoritative position, server clocks, and status. Pushed after every accepted move and on clock events. Your client renders it and nothing more. |
| Server pushes | final state frame | Marks the game over, whether by mate, resignation, timeout, or abandonment after disconnect grace runs out, and carries the result. |
# a state frame, the only thing your renderer needs
{
"type": "state",
"fen": "r1bqk2r/ppp2ppp/2np1n2/2b1p1N1/2B1P3/2PP4/PP3PPP/RNBQK2R b KQkq - 5 6",
"turn": "black",
"clocks": { "white_ms": 158200, "black_ms": 153000 },
"last_move": "f3g5",
"status": "active"
}
Record sessions, verify receipts.
Start a quick-mode session with a ref you choose, then verify it from your server. The receipt carries completed_received_at stamped by the server clock. That is how you know a gate was really completed. Trust receipts, not clients.
curl "https://activities.daeila.com/v1/sessions?ref=wake_2026-07-14_user_42" \
-H "Authorization: Bearer psk_YOUR_KEY"
# response, abbreviated
{
"sessions": [
{
"ref": "wake_2026-07-14_user_42",
"activity": "chess",
"mode": "quick",
"completed_received_at": "2026-07-14T07:03:11Z"
}
]
}
This is the pattern behind AlarmMCP's wake checks: the alarm is satisfied by the receipt, not by the phone's word.
REST-first, on purpose.
The API surface is small enough that most stacks need no SDK: three REST calls and a WebSocket. ParlorKit, a Swift package, exists in early access for native Apple clients. SDKs for more platforms are on the roadmap, which means they are not shipped.