/sandboxesAsk for a sandbox key
POST /sandboxes
Takes no credential: it is how a script, a CLI or a coding agent that holds no key yet asks for one.
A person approves. The answer is 202 with status: "authorization_required": a verifyUrl to show the person you are working for, and a deviceCode to keep. They open the link, a page of its own that says who is asking, sign in and approve or decline (no phone number and no enrolment: the sign-in is enough for a test key). Poll POST /sandboxes/token with the deviceCode every interval seconds; it answers pending until they approve and then hands over the key, once. The key is an ordinary sandbox key on their developer account: no expiry, visible and revocable in the console, and capped at 5,000 requests a day until the account verifies a phone. An authorization lives ten minutes.
Anonymous sandboxes are a server option that is off by default. Where the operator enabled them the answer is 201 with status: "ready" and an apiKey at once, for a sandbox nobody owns yet: it has small limits, expires at expiresAt, may not register webhooks or Connect apps or have documents read, and a person keeps it by opening claimUrl, which replaces its key. Read status and handle both.
Body (optional)
| Field | Type | Description | |
|---|---|---|---|
name | string | What you are building, shown to the person on the approval screen. Letters, digits, space, dot, dash; 60 characters. |
Response
201: { "ok": true, … }
| Field | Type | Description | |
|---|---|---|---|
ok | boolean | required | |
status | string | required | One of: ready. |
mode | string | required | One of: sandbox. |
apiKey | string | required | The bk_test_ key. Shown here once: send it as X-Api-Key. |
keyPrefix | string | The first characters of the key, as the console lists it. | |
expiresAt | string | required | When the key stops working unless the sandbox is claimed. |
claimUrl | string | A person opens it and signs in to keep the sandbox. Claiming replaces the key. | |
claimToken | string | The token inside claimUrl. | |
limits | object | ||
limits.ratePerMin | integer | Requests per minute while unclaimed. | |
limits.dailyCap | integer | Requests per day while unclaimed. | |
apiBase | string | The base URL for every other call. | |
mcpUrl | string | The MCP endpoint, which takes the same key. | |
docs | string | The documentation index for agents. | |
next | array of string | What to do with the key, in order. |
Errors: 429, 503. Every failure answers { "error", "reason", "hint", "param", "doc_url", "retryable", "request_id" }: branch on reason, and read hint for what to change (https://docs.billos.co.il/errors.md).