Keys and security

Errors

Every refusal is JSON with a code, a sentence and a request id. The tables list every code an API caller can meet, with what to do about it.

The shape of a refusal

A call that fails answers with an HTTP status and a JSON body:

{
  "code": "api.key_refused",
  "error": "That API key is not a live key.",
  "requestId": null,
  "detail": null
}
FieldWhat it is
codeA stable name for what went wrong. Branch on this.
errorOne sentence for a person. It may be reworded at any time, so never match on it.
requestIdFilled in when the fault is ours, and null otherwise. Quote it when you report a problem, and we can find the call.
detailThe one value a sentence names, such as a size or a field, or null.

A 401 also carries the header WWW-Authenticate: Bearer realm="panlytics", and a 429 carries Retry-After. Every answer, good or bad, is sent with Cache-Control: no-store.

Every code

These are the codes any endpoint can answer with. Each endpoint's reference page lists the ones it can give, with when.

CodeStatusWhat happenedWhat to do
api.invalid400The field is missing or not valid.Read that endpoint in the API reference, correct the field and call again.
bad_request.said400Change it and try again.
upload.said400Change what you are sending and try again.
api.key_missing401This call carried no API key.Send the key in the Authorization header as Bearer, then call again.
api.key_refused401That API key is not a live key.Ask the person who gave you the key to make a new one.
api.wrong_key_kind401This key does not open these endpoints.Use the key these endpoints were made for, or ask the person who gave you the key.
agent.sign_in_off403Letting an AI sign in to the app is switched off for this company.Ask your CEO to switch it on in the company's Developer settings.
api.approve_in_app403A run an API key started waits for a person to approve it in the app.Open the conversation in Panlytics, signed in as yourself, and decide there.
api.key_ended403The API key that started this run is no longer live, so the run was stopped.Make a new key and start the run again.
auth.not_fresh403This step needs you to prove it is you.Confirm your password or passkey, then try again.
refused403You do not have permission to do that.Ask your CEO or a manager if you need it.
refused.demo403This is a public demo, and that part is switched off.Open your own shop to do this for real.
refused.said403Ask an owner of your company if you need this.
not_found.said404Go back and pick it from the list again.
api.idempotency_busy409The first request with that Idempotency-Key is still running.Wait a few seconds, then send it again.
api.idempotency_conflict409That Idempotency-Key was already used for a different request.Send a new key with a new request, or send the first request again unchanged.
api.stale409This was changed after the time you sent.Read it again, then send your change with the new updatedAt.
conflict.said409Reload the page and try again.
report.none409There is no report open for this shop.Upload a report or load an example first.
store.none409No shop is open in this session.Choose a shop from the switcher and try again.
upload.too_large413That file is too big to upload.Send a file under instead.
form.field422Correct it and save again.
upload.unreadable422That file could not be read.Check it opens on your machine, then upload it again.
agent.links_limit429This key's maker has asked for six sign-in links in the last hour.Wait a minute and ask for a new link.
api.ai_budget429This shop has used today's assistant budget.Try again after midnight UTC, or ask an owner to raise the daily limit in AI settings.
api.rate_limited429This key made too many calls in one minute.Wait the seconds in Retry-After, then call again.
net.rate_limited429That was asked for too many times.Wait a minute and try again.
server500Something went wrong on our side.Try again in a minute; if it keeps happening, report it.
agent.unreachable503The assistant is not answering right now.Everything else still works; try the assistant again in a few minutes.
developer.unavailable503API keys are not set up on this deployment yet.Report it, and we will switch them on.

api.invalid names the refused field in detail, as a dotted path such as limit or settings.periodDays. It is body for a body that is not JSON or is over 256 KB, file for a missing upload, and cursor for a cursor the API did not make. A code ending in .said carries its sentence in detail.

What to do, by status

StatusMeaningWhat a program should do
400The request was not understoodFix the request. Retrying it unchanged gets the same answer.
401No key, or a key that is not liveStop. Ask the person for a new key.
403The key may not do thisStop. Tell the person. Do not look for another way.
404Not in the key's companyCheck the id. An id of another company answers 404, the same as one that never existed. Inside the company, a shop the key may not open answers 403.
409It clashes with something: no report yet, a name in use, a stale updatedAt, an Idempotency-Key in useRead the current state, then decide again.
413, 422The uploaded file was refusedChange the file, then upload again.
429Too many calls, or the shop's AI budget for the day is used upWait the seconds in Retry-After, or until midnight UTC for the budget.
500Something failed on our sideRetry once after a short wait. If it fails again, report the requestId.
503Not switched on, or briefly too busyWait a minute and retry. If it keeps happening, report it.