Live play API

Who it's for. Web and Roblox hosts that already have an aig_ game license.

What you'll do. Infer actions with /play/act or /play/act/batch; report scores and ranks with /play/rounds. Never expect weights in the response.

Base URL: https://aia-hub.metamindinglab.com (Cloud Run Hub; do not call the old Vercel URL). Auth: Authorization: Bearer aig_… (or campus secret for first-party). Bind Roblox place_id and/or web Origin to the license. SDK zip: /developers/sdk.

Endpoint matrix

MethodPathPurposeWeights?
POST/api/v1/play/actSingle AI infer → { m, j, f, seq } (+ Surge d/g/jh)Never
POST/api/v1/play/act/batch1–20 AIs, one HTTP tick → { acts[] }Never
POST/api/v1/play/roundsOpen heat → { round_id }Never
POST/api/v1/play/rounds/:id/completeScores, ranks, outcomes, eventsNever
POST/api/v1/play/rounds/:id/eventsMid-round event flush (≤500 total)Never
GET/api/v1/play/rounds?game_id=List recent rounds (owner / aig_)Never
GET/api/v1/play/rounds/:idRound detail + agents + eventsNever
POST/api/v1/training/licenses/gameMint aig_ (creator cookie)—
POST/api/v1/training/licenses/brainGrant play | train | director—

Inference

POST /api/v1/play/act

One brain per request. Prefer batch when N > 1.

{
  "game_id": "uuid",
  "brain_id": "uuid",
  "room_slug": "surge_gauntlet",
  "obs": [ /* 32 or 64 floats */ ],
  "seq": 1,
  "place_id": "123456789"
}
→ { "m": 0-8, "j": true|false, "f": true|false, "d": true|false, "g": true|false, "jh": 0-1, "seq": 1, "infer_ms": 2 }

POST /api/v1/play/act/batch

Cap agents.length ≤ 20. Think interval 0.15–0.2 s so Roblox stays under HttpService ~500 req/min. room_slug=surge_gauntlet → Hub expects 64-dim obs.

d / g / jh are Surge-only. Other rooms omit them or send zeros. Never map f (fire) to dash. Mint keys in Studio.

{
  "game_id": "uuid",
  "room_slug": "surge_gauntlet",
  "place_id": "123456789",
  "agents": [
    { "brain_id": "uuid-a", "obs": [/*64*/], "seq": 1 },
    { "brain_id": "uuid-b", "obs": [/*64*/], "seq": 1 }
  ]
}
→ { "acts": [{ "brain_id", "m", "j", "f", "d", "g", "jh", "seq", "infer_ms" }, ...], "infer_ms": 5 }

Round feedback (you score → Hub stores)

The physics host computes score / rank / win. Hub persists for owner review (Round history). Does not write policy_weights or replace campus training sessions.

POST /api/v1/play/rounds

{
  "game_id": "uuid",
  "place_id": "123456789",
  "room_slug": "surge_gauntlet",
  "platform": "roblox",
  "roster": [
    { "brain_id": "uuid-a", "slot": 0, "display_name": "Bolt" },
    { "brain_id": "uuid-b", "slot": 1, "display_name": "Spark" }
  ],
  "meta": { "map_id": "map1" }
}
→ { "round_id": "uuid" }

POST /api/v1/play/rounds/:id/complete

{
  "duration_sec": 42.5,
  "board": { "winner_brain_id": "uuid-a", "mode": "finish_first" },
  "agents": [
    {
      "brain_id": "uuid-a",
      "slot": 0,
      "outcome": "win",
      "rank": 1,
      "score": 100,
      "reward": { "coins": 50 },
      "finish_time_sec": 38.2,
      "alive": true,
      "stats": { "jumps": 12 }
    },
    {
      "brain_id": "uuid-b",
      "slot": 1,
      "outcome": "eliminated",
      "rank": 2,
      "score": 40,
      "death_reason": "surge_caught",
      "alive": false
    }
  ],
  "events": [
    { "t": 0, "type": "round_start" },
    { "t": 12.1, "type": "checkpoint", "brain_id": "uuid-a", "payload": { "cp": 3 } },
    { "t": 38.2, "type": "finish", "brain_id": "uuid-a" }
  ]
}
→ { "ok": true, "round_id": "uuid" }

Common errors

StatusMeaningFix
401Missing/invalid aig_ or campus secretMint key; ServerStorage / env only
403Wrong game_id, place, origin, or no play grantBind place; grant scopes play
400Bad obs dim / agents empty / events overflowMatch room_slug dim; caps 20 / 500
409Round already completedNew heat → new openRound
404Brain or round not foundValid brain_id / round_id

Not for live partners