Docs

Client libraries

Cursor

Our own chat models in Cursor, through the editor's OpenAI base URL override — the exact settings, what is known to work and what is not yet, and what each error means.

Cursor can send its chat requests to any OpenAI-compatible base URL, and ours is one. Its chat modes send tools on every request, so use one of our own chat models — glm-5-3-flash, deepseek-v4-1-flash or kimi-k3 — which take tools. The catalogue's language models (auray-serverless/model/…) do not, and a request to one that carries tools is refused.

Cursor's requests come from Cursor's servers, not from your machine, so nothing needs to be reachable on your side — and it also means Cursor holds your key and sends it for you.

#1. A key for Cursor, and only for Cursor

  1. In the desktop, open Account → API Keys and create a key used by nothing else. Untick every scope except text:write: Cursor needs nothing more, and a key that can do little else is worth less to anyone who gets hold of it. Little is not nothing: text:write also reaches the catalogue's language models, and uploads — and reads back — the account's files of the kinds they take: images, video, audio and documents. (A key minted with the older scope names holds chat:write instead, which works the same for Cursor and reaches no upload.)
  2. Set its monthly credit ceiling to what you would accept losing if the key leaked — but above what a working session costs: an agent sends a request for every step it takes, each one reserves credits before it runs, and a request that would take the key past its ceiling is answered 429.
  3. Press Copy in the sheet that shows the new key. The key starts with auray_sk_ and is 71 characters; it is shown once and never again.

Revoke the key in Account when you stop using Cursor with us, or the moment you think it has leaked; nothing else uses it, so nothing else breaks.

The line Account lists for each key afterwards — ID and twelve characters — is the key's ID, not the key. Pasted into Cursor it is refused 401 invalid_api_key, which Cursor shows as "Unauthorized User API key". If all you have is the ID, create a new key.

Completions need a plan whose keys may run them. On a plan where they may not, every request is refused 403 plan_has_no_api_generate. Completions spend the plan's interactive bucket, and GET /v1/plans shows it closed by either of these:

  • limits.api.may_generate is false
  • limits.api.interactive.burst is 0

Check the key before you open Cursor, with the key in an environment variable rather than in the command:

curl -s https://api.auray.ai/openai/v1/chat/completions \
  -H "Authorization: Bearer $AURAY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"model":"glm-5-3-flash","messages":[{"role":"user","content":"Say OK."}]}'

A 200 with an answer means the key and the plan are right. 401 is the key; 403 is the plan.

#2. Cursor's settings

In Cursor, open Settings → Models:

  1. OpenAI API Key — paste the whole key, then switch its toggle on. Saving the key and turning it on are two separate actions.
  2. Override OpenAI Base URL — https://api.auray.ai/openai/v1, exactly: no trailing slash, and not /chat/completions — Cursor adds that itself, and a doubled one answers 404.
  3. AWS Bedrock — off. With it on, Cursor sends requests without your key.
  4. Add model — type the name exactly: glm-5-3-flash, deepseek-v4-1-flash or kimi-k3, and select it in the chat.

Picking a thinking level or Fast in Cursor's model picker is fine. Cursor adds -thinking-<level> and -fast to the name it sends, and we read glm-5-3-flash-thinking-high as glm-5-3-flash at its deepest reasoning; -fast changes nothing.

Avoid naming a custom model after one of Cursor's own — a name with gpt-5, claude or gemini in it makes Cursor build its request for that model's own service instead.

#What works, and what is not known yet

  • Ask sends OpenAI chat completions with tools, and that is what our own chat models answer: its calls come back as tool_calls, Cursor runs them, and the results go back in its next request. A custom tool such as Cursor's patch tool is offered to the model as a function of one string and returned as a custom call.
  • Agent works the same way when Cursor sends it as chat completions. Cursor's Agent mode has also been seen sending OpenAI's newer Responses format to a custom base URL — input where chat completions has messages. Sent to /responses, that shape is answered as chat completions are; sent to /chat/completions, it is 400 messages_required. If Agent shows that error, use Ask.
  • Tab, Auto, background agents and the Cursor CLI never use a custom base URL; they run on Cursor's own models whatever you set here.

Reasoning comes back as reasoning_content, beside the answer, and is billed as the output tokens it is. If a client sends it back on the assistant message while it is still working on the same question, it reaches the model for that exchange; from an earlier question it is dropped.

A request has no per-message length limit, so an attached file inside your message is fine; the whole request is capped at one mebibyte. A long conversation is held to your plan's history budget, counted in bytes, not tokens — each message as the JSON it is sent as, plus 64, and 128 for the request (GET /models prints yours as history_budget_bytes). Cursor's own instructions are never counted; the current exchange — an agent task and every step of it — counts first and is always sent whole, and older parts are kept, newest first, while the rest fits, the others left out a whole exchange at a time. An answer that left earlier messages out says how many in its X-Auray-History-Trimmed header. Every request is reserved at the size of everything it sends, so a long agent session reserves more per step than a short question, and is settled down to what the model reported.

#What Cursor's errors mean

Cursor turns every 401 into its own "Unauthorized User API key", and every 429 or 5xx — and a request that got no answer — into "User API Key Rate limit exceeded", whatever we said. Other refusals carry our code; each is explained on the errors page.

We answeredIt meansDo this
401 — Cursor says "Unauthorized User API key"No key, the key's ID, or a key that is wrong or revokedPaste the whole key and turn its toggle on; turn Bedrock off.
403 plan_has_no_api_generateYour plan's keys may not run completions: in GET /v1/plans, its limits.api.may_generate is false or its limits.api.interactive.burst is 0Choose a plan with may_generate true and interactive.burst above 0, in Account → Plan & Billing.
429 — Cursor says "User API Key Rate limit exceeded"Too many requests a minute, an empty wallet, or the key's monthly credit ceilingWait a minute; if it persists, top up or raise the key's ceiling.
400 unknown_modelThe name is not one we serveUse one of the three names above, exactly.
400 tools_unsupportedThe model is a catalogue model, or a tool is of a hosted typeUse one of our own chat models.
400 messages_requiredCursor sent the Responses format (input), not chat completionsUse Ask; see above.
400 tools_rejectedThe model's server refused a tool definition as sentCheck the tool's schema; nothing was charged.

Every answer carries an X-Request-Id header, which is what to quote if you write to us.

Start typing to search every page of the documentation.

Up arrow Down arrow to moveEnter to openEscape to close