# Roblox Headless Client API > A REST service that launches headless Roblox clients. You give it a place, it leases > `.ROBLOSECURITY` cookies from a pool, connects that many real clients to real servers, and > lets you run Luau inside them and read their output. Each user has their own isolated pool. Base URL: http://headless.secretservicepanel.com Full reference: http://headless.secretservicepanel.com/llms-full.txt ## Authentication Send `X-API-Key: ` on every request. Mint a key in the web console under API keys. A key belongs to one user and scopes every call to that user's cookie pool and jobs. `GET /health` needs no key. ## Conventions - All JSON keys are kebab-case: `place-id`, `job-id`, `batch-delay`. - Errors nest under `detail`: `{"detail":{"error":"insufficient_accounts","available":0}}`. Match on `detail.error`. Schema problems instead return `{"detail":[...]}` — a list. - Launches return `202` immediately; poll the returned `job-id`. ## Endpoints ### Cookie pool - `GET /health` — Liveness plus a pool summary **aggregated across every user**, since there is no single pool. - `GET /pool` — Every account. - `GET /pool/select` — Resolve ids you can hand straight to a launch call. - `POST /pool/import` — Add `.ROBLOSECURITY` cookies. - `POST /pool/validate` — Check every cookie against Roblox `users/authenticated`. - `DELETE /pool/{account-id}` — Drop one account. ### Launch - `POST /join` — Put N clients onto one specific server. - `POST /mass-join` — Spread clients across a place's servers. ### Jobs - `GET /jobs` — Every launch job with a rolled-up count, oldest first. - `GET /jobs/{job-id}` — One row per account. - `GET /jobs/{job-id}/logs` — Everything the client's Luau host logged, per account — `print`, `warn`, and errors raised by the game's own scripts. - `POST /jobs/{job-id}/exec` — Run Luau inside the job's live clients and collect each one's output. - `POST /jobs/{job-id}/stop` — Cancel one job. - `POST /stop-loop` — Cancel every job still running and drain their leases, newest first. ### Users - `GET /admin/users` — Every user with their pool and job counts. - `POST /admin/users` — Create a user. - `POST /admin/users/{id}/role` — Promote or demote. - `POST /admin/users/{id}/disabled` — Disable or re-enable a user. - `POST /admin/users/{id}/password` — Reset another user's password without knowing the current one. - `GET /admin/users/{id}/keys` — That user's key metadata. - `DELETE /admin/users/{id}` — Remove the user, their keys, and their pool file. ### Console - `GET /admin/state` — Whether an admin exists, whether this caller is signed in, and whether a setup token is required. - `POST /admin/setup` — One-time creation of the **first** user, who is always an `admin`. - `POST /admin/login` — Exchange credentials for a session cookie. - `POST /admin/logout` — Revoke this session and clear the cookie. - `POST /admin/password` — Change the password. - `GET /admin/keys` — List the calling user's API keys. - `POST /admin/keys` — Mint a key owned by the calling user. - `POST /admin/keys/{id}/revoke` — Refuse the key from now on, keeping the audit row. - `DELETE /admin/keys/{id}` — Remove the row entirely. - `GET /admin/settings` — Current launch settings. - `PUT /admin/settings` — Replace the launch settings. - `GET /admin/overview` — The calling user's pool counts, job counts and live client count, plus the loaded native library. ## Minimal example ```sh # put 2 clients on a specific server and print from each curl -X POST http://headless.secretservicepanel.com/join \ -H "X-API-Key: $KEY" -H "Content-Type: application/json" \ -d '{"place-id":606849621,"job-id":"","count":2,"lua":"print(1)"}' # then poll, read Luau output, and run more code in the live clients curl -H "X-API-Key: $KEY" http://headless.secretservicepanel.com/jobs/lj_xxxxxx curl -H "X-API-Key: $KEY" http://headless.secretservicepanel.com/jobs/lj_xxxxxx/logs curl -X POST -H "X-API-Key: $KEY" -H "Content-Type: application/json" \ -d '{"lua":"return game.PlaceId"}' http://headless.secretservicepanel.com/jobs/lj_xxxxxx/exec ``` ## Gotcha worth knowing up front The `lua` field on a launch runs the instant the client reports joined, which is BEFORE replication delivers `Players.LocalPlayer`, the character, or `game.PlaceId`. For anything that touches the DataModel, use `POST /jobs/{job-id}/exec` a few seconds later.