Your own model keys
Save your own Claude, OpenAI, Grok or Gemini key, choose whose key a provider uses, set the project's default model, and see how it is billed. The Free plan runs AI on your own keys.
AI runs call a model from a provider. You can use Boxline’s keys, or save your own key for a provider and pay the provider directly. Every plan can save keys. On the Free plan, your own keys are the only way to run models.
Providers
| Provider id | Name | Notes |
|---|---|---|
anthropic | Claude | Tools and computer mode. |
openai | OpenAI | Tools and computer mode. |
xai | Grok | New. Tools mode only. |
google | Gemini | New. Tools mode only. |
Grok and Gemini are new here. Try them on your own tasks before you rely on them. They do not support mode: "computer": that is a 400. Calls go only to each provider’s own API host.
Add a key in the console
- Open the console and go to Settings, then Models. Only an owner or an admin of the organization can save or remove a key.
- Paste the provider’s key and save. Boxline asks the provider to check it first.
- Choose “Use my key” or “Use Boxline’s” for that provider.
Add a key with the API
PUT /v1/project/model-keys/:provider takes key and/or use. Saving a key without use sets use to "project". The answer never has the key, only its last 4 characters in preview.
curl -X PUT https://api.boxline.dev/v1/project/model-keys/openai \ -H "x-api-key: $BOXLINE_API_KEY" \ -H "content-type: application/json" \ -d "{\"key\": \"$OPENAI_API_KEY\"}"import { Boxline } from "@boxline/sdk";const bx = new Boxline(); // reads BOXLINE_API_KEY and BOXLINE_API_URLconst item = await bx.project.setModelKey("openai", { key: process.env.OPENAI_API_KEY! });console.log(item.use, item.preview, item.verified);from boxline import Boxlinebx = Boxline() # reads BOXLINE_API_KEY and BOXLINE_API_URLimport ositem = bx.project.set_model_key("openai", key=os.environ["OPENAI_API_KEY"])print(item["use"], item["preview"], item["verified"])curl https://api.boxline.dev/v1/project/model-keys \ -H "x-api-key: $BOXLINE_API_KEY"import { Boxline } from "@boxline/sdk";const bx = new Boxline(); // reads BOXLINE_API_KEY and BOXLINE_API_URLconst { keys } = await bx.project.modelKeys();for (const k of keys) console.log(k.provider, k.hasKey, k.preview, k.useEffective);from boxline import Boxlinebx = Boxline() # reads BOXLINE_API_KEY and BOXLINE_API_URLfor k in bx.project.model_keys()["keys"]: print(k["provider"], k["hasKey"], k["preview"], k["useEffective"])| Field | Meaning |
|---|---|
provider | anthropic, openai, xai or google. The list always has all four. |
use | Your choice: "project" (your key) or "platform" (Boxline’s). It is "platform" until you choose. |
useEffective | What calls use now. Your plan can override your choice: on Free it is always "project". |
hasKey, preview | Whether a key is saved, and its last 4 characters. |
verified | true when the provider accepted the key. false when the provider could not be reached; the key is saved anyway. |
platformAvailable | Boxline can make calls for this provider for you: its key exists and your plan includes it. |
lastUsedAt, updatedAt, updatedVia | When it was last used and changed, and how: "console", "api_key" or "support". |
key is 20 to 400 characters with no spaces. Boxline asks the provider one read-only question (list models, 6 second timeout). If the provider refuses the key you get 400 invalid_model_key and nothing is saved. You can save 10 times a minute; past that, 429 rate_limited.
Switch between your key and Boxline’s
The choice is per provider. Send only use:
curl -X PUT https://api.boxline.dev/v1/project/model-keys/openai \ -H "x-api-key: $BOXLINE_API_KEY" \ -H "content-type: application/json" \ -d '{"use": "platform"}'import { Boxline } from "@boxline/sdk";const bx = new Boxline(); // reads BOXLINE_API_KEY and BOXLINE_API_URLawait bx.project.setModelKey("openai", { use: "platform" });from boxline import Boxlinebx = Boxline() # reads BOXLINE_API_KEY and BOXLINE_API_URLbx.project.set_model_key("openai", use="platform")use: "project"needs a saved key (400 without one).use: "platform"needs a plan that includes Boxline’s keys (402feature_not_in_planon Free). The saved key stays, unused.- A run keeps the source it started with. A switch does not move a run that is already working.
DELETE /v1/project/model-keys/:providerremoves the key (204, also if there was none) and setsuseback to"platform". The next call of a run on that key fails. Node:bx.project.deleteModelKey(provider). Python:bx.project.delete_model_key(provider).
The project default model
When a request names no model, Boxline picks one. The order is: the request’s own provider and model, then your project’s default model, then Boxline’s default. This applies to agent runs, plain-English steps, extract and the AI helpers in scripts. Tasks keep their own model.
curl -X PUT https://api.boxline.dev/v1/project/settings \ -H "x-api-key: $BOXLINE_API_KEY" \ -H "content-type: application/json" \ -d '{"defaultModel": {"provider": "openai", "model": "gpt-6-luna"}}'import { Boxline } from "@boxline/sdk";const bx = new Boxline(); // reads BOXLINE_API_KEY and BOXLINE_API_URLawait bx.project.setSettings({ defaultModel: { provider: "openai", model: "gpt-6-luna" } });await bx.project.setSettings({ defaultModel: null }); // clear itfrom boxline import Boxlinebx = Boxline() # reads BOXLINE_API_KEY and BOXLINE_API_URLbx.project.set_settings(default_model={"provider": "openai", "model": "gpt-6-luna"})bx.project.set_settings(default_model=None) # clear itA request that names only the default’s provider gets the default’s model. A request that names another provider gets that provider’s own default. Steps and extract use the provider’s fast model unless you name one. An unknown provider or model is a 400. The default does not need a key when you save it. See Project settings.
Billing
- A run on your own key has no model charge from Boxline. The provider bills you.
- It does not count toward your plan’s monthly model spend cap, and that cap does not stop it.
- Tokens and a list-price estimate are still shown:
usage.costUsdon the run. maxCostUsdstill applies, using that estimate.- Runs and extract answers show
keySource:"project"or"platform". GET /v1/project/usageshowsmodelCostUsd(on Boxline’s keys) apart fromownKeyModelCostUsd(on yours), for the period and for each day.- Browser time, proxies and every other feature are billed as before.
What Free gets
The Free plan runs AI on your own model keys only. Paid plans include Boxline’s model keys, or you can use your own. Without a key, a Free project gets 402 feature_not_in_plan on agent runs, steps and extract. Add a key for the provider you want and try again. See Plans and features.
Errors
| Status and code | Meaning |
|---|---|
400 invalid_model_key | The provider did not accept the key. Nothing was saved. |
402 feature_not_in_plan | Free plan, and no own key for that provider. Or use: "platform" on Free. |
503 provider_unavailable | A paid plan, but no key anywhere for that provider (Grok and Gemini may have none on Boxline’s side). Add your own key. |
errorCode: "model_key_rejected" on a run | The provider refused your key, or you removed it while the run worked. The error names the provider, never the key. It does not fall back. |
502 model_error | Another failure of a call on your key. The provider’s text is shown with the key removed. |
Keep your keys safe
- A key is stored encrypted and is write-only. No route, step, log, webhook or error shows it again; only the last 4 characters.
- The key is never sent to the model and never written to logs. A provider’s error text has it removed.
- Save keys from a secret store or an environment variable, never from code you commit.
- Create a separate key at the provider just for Boxline, with a spending limit there. Rotate it by saving a new one: it is used at once.
- Delete the key here if it may have leaked, and revoke it at the provider.
These docs describe a beta API. Endpoints, fields and SDK methods may change before general availability; breaking changes will be listed in the changelog.