The base URL, authentication, the WebSocket URLs and every endpoint of the API.
Everything Boxline does is an HTTP call. The SDKs and the MCP server use the same API. Every endpoint has its own page below, with its parameters, responses, errors, and examples in curl, Node and Python (and the CLI, where it has a command).
New here? Start here has the eight calls most things begin with, each as the smallest request that works.
These pages are generated from the API’s OpenAPI description (version 1.0.0), so they always match it. You can fetch it yourself at GET /v1/openapi.json, for example to generate a client.
Basics
Base URL
https://api.boxline.dev
Authentication
x-api-key: <key> or Authorization: Bearer <key> on every request. See below.
Request bodies
JSON with content-type: application/json, except file uploads, which send the raw bytes.
Errors
A matching HTTP status and {"error": {"code", "message", "requestId"}}. See Errors and request ids.
Request ids
Every response has X-Request-Id. Quote it when you ask for help.
Rate limits
Per route, and per project, user or organization, with RateLimit-* headers. See Rate limits.
Safe retries
Idempotency-Key on the calls that create something. See Idempotency keys.
Lists
?limit=&after= in, { data, next } out. See Pagination.
Every call needs an API key, except a few public ones (GET /v1/pricing, GET /v1/openapi.json and GET /healthz). Create keys in the console, or with POST /v1/project/api-keys. A key belongs to one project, and everything it does happens in that project, so no call takes a project id. The project belongs to an organization, which holds the people and the billing. You manage organizations in the console, where an API key gets 403 console_login_required: see Organizations, projects and team members. Your own model keys belong to the project too.
GET /v1/project/api-keys shows who made each key in createdBy: via (console, api_key or support), the user’s userId and email, and the apiKeyId when a key made it. It is null for keys made before this was recorded. A new sign-up cannot create a key until it has entered its confirmation code (403 email_unconfirmed).
The console signs in with a cookie instead (bx_session). Scripts and servers should always use a key.
Password resets and account recovery
Forgot your console password? Ask for a reset link on the console’s sign-in page. The link goes to your account’s email, works once, and expires after 30 minutes. A new request replaces the earlier link. After a reset, every console login of the account ends, and you get an email saying the password was changed.
A reset also confirms your email. If the email was not confirmed yet, the reset is treated as recovering the account, in case someone else set it up with your address. Besides the new password, it does this in every organization you own and in your personal organization, for all their projects:
revokes every API key (make new ones afterwards);
switches off its webhook endpoints, with disabledReason: "account_recovered";
switches off its task schedules and cancels queued scheduled runs (errorCode: "account_recovered");
removes the organization’s other members (their console logins end) and withdraws its pending invitations, so you are its only owner again;
stops the running sessions of its projects without saving them (stopReason: "account_recovered"), and clears what its stopped sessions had saved: a resume then answers 409 nothing_saved.
The reset email of such an account says so before you use the link. A reset of a confirmed account, and a password change while signed in, keep your API keys and members.
Confirm your email
Signing up needs a 6-digit code. We email it to your address, and the console asks for it before you can use the new account. Signing in never asks for a code. If you leave before entering it, the account takes you back to the same page the next time you sign in.
The code
6 digits, random, and it works once.
Valid for
15 minutes.
Wrong tries
5 on one code, then that code is locked (410 confirmation_code_locked): ask for a new one. Over all of the account’s codes the limit is 15 wrong tries. After that no code is accepted (403 confirmation_locked), and only a password reset confirms the email.
A new code
Ask for one in the console. It replaces the old code and gives that code 5 tries, but it does not reset the 15 in total. At most 5 codes an hour and 10 a day (see Rate limits).
Until a new sign-up has entered its code:
The account can sign in only to confirm the email. Every other console call answers 403 email_unconfirmed. Reading GET /v1/auth/me, signing out, the two confirmation calls and accepting an invitation still work.
Creating an API key answers 403 email_unconfirmed. So does a key creating one, while every owner of the organization is a sign-up that has not entered its code.
API keys the account already has keep working.
Accounts made before the code existed are never held back, and never asked for a code when they sign in. If their email is not confirmed, everything works except inviting people and transferring an organization, which need a confirmed email (and nobody can demote or remove you from an organization you created until you confirm it: 403 email_unconfirmed). You can confirm whenever you like: the same two calls work for any signed-in user whose email is not confirmed.
GET /v1/auth/me shows the state in user:
Field
Meaning
emailVerified
The email is confirmed.
emailConfirmationRequired
true only for a new sign-up that has not entered its code yet. The console shows the code page and nothing else.
emailConfirmationAvailable
This server emails confirmation codes, so asking for one can work.
A password reset confirms your email too, and so does accepting an invitation, or signing up with an invitation link: those links were mailed to that address, so no code is needed.
The console makes the two calls, POST /v1/auth/confirm-email with { "code": "123456" } and POST /v1/auth/confirm-email/send. They need a console login: with an API key the answer is 403 console_login_required.
Answer
Why
400 invalid_confirmation_code
The code is wrong, or not 6 digits. The message says how many tries are left.
410 confirmation_code_expired
No code was asked for, it was used or replaced, or it is older than 15 minutes.
410 confirmation_code_locked
5 wrong tries on that code. Ask for a new code.
403 confirmation_locked
15 wrong tries over all of the account’s codes. No code is accepted or sent any more: reset the password to confirm the email.
429 rate_limited
Too many codes asked for (5 an hour, 10 a day), or too many tries.
503 mail_unavailable
The email could not be sent. Try again later.
WebSockets and the live view
Three session URLs are not plain HTTP calls. Each carries a signed token, works without your API key, and is on the session object. Get a session returns fresh ones every time; one is null when the session has no browser (connectUrl, liveUrl) or no shell (terminalUrl):
Session field
What it is
connectUrl
Chrome DevTools Protocol WebSocket (/v1/connect?sessionId=…&token=…) for Playwright or Puppeteer. See Connect with Playwright.
liveUrl
Live view page (/live/:id?token=…). Open it to watch the browser or take control. Anyone with it can control the browser: treat it like a password. See Live view.
terminalUrl
Terminal WebSocket (/v1/sessions/:id/terminal?token=…). Send JSON text frames {"type":"input","data":"…"} or {"type":"resize","cols":N,"rows":N}; receive raw terminal output as binary frames. See Shell.
A URL of a stopped or deleted session answers 410 session_ended; a rotated or expired one, 401 unauthorized.
These docs describe a beta API. Endpoints, fields and SDK methods may change before general availability; breaking changes will be listed in the changelog.