Every REST endpoint, the MCP tools, the receipt fields and the error codes.
https://www.quorum.dog/v1Send the key as Authorization: Bearer <key>. Keys start with qk_live_ or qk_test_. The API is server to server: a request carrying a browser Origin header returns 403 browser_origin_not_allowed.
API keys always bill your organization's API account. Each API-key call has a ceiling on its bill: the higher of $2.00 and the price of the Mode being called, unless the API key carries its own ceiling. Only Quorum sets a key's own ceiling. A request can lower the ceiling for one call with quorum.max_cost_usd. Calls from a connected app on a plan pay real cost and have no per-call ceiling.
| Method and path | Purpose | Cost |
|---|---|---|
POST /v1/chat/completions | Run a Mode and return one answer. | Billed. |
POST /v1/estimate | Classify a prompt and quote the Mode's surcharge for it without running it. | Free. |
GET /v1/models | List the Modes this key can call, with pricing. | Free. |
GET /v1/receipts/{request_id} | Read the accounting for a past call. | Free. |
POST /v1/tests, GET /v1/tests?batch_id= | Launch or poll a Lab run on known-answer questions. | Billed per question. |
POST /v1/certify, GET /v1/certify?batch_id= | Launch or poll the certification run. | Billed per question. |
POST /mcp | The MCP server, 13 tools. | Per tool. |
Every model field takes a Mode ticker in the form COMPANY:MODE, for example QRUM:STAN for Standard Mode. model is required on every call.
curl -sS https://www.quorum.dog/v1/chat/completions \
-H "Authorization: Bearer $QUORUM_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
--max-time 300 \
-d '{
"model": "QRUM:STAN",
"messages": [{"role": "user", "content": "Should we use RLS or app-layer authz?"}],
"quorum": { "max_cost_usd": 1.00 }
}'Request fields:
| Field | Type | Meaning |
|---|---|---|
model | string, required | Mode ticker. |
messages | array, required | { role, content } objects. role is user, system or assistant. |
tier | string | experience (Express), plus (Foundation) or pro (Frontier). Which engine tier fills the seats for this call. An API key accepts any of the three, up to pro. The default is plus. The Mode decides whether a panel convenes. |
quorum.max_cost_usd | number | Ceiling on this call's bill, for API-key calls. It can lower the key's ceiling and cannot raise it. |
stream | boolean | true returns 400 stream_not_supported. |
Headers: Idempotency-Key makes a retry safe. Send a new key for each request and the same key only when you retry that request. If you omit it, one is derived from your organization, key, model and messages, so an identical repeat question is a retry.
A replay matches on the key alone. A key reused with a different request returns the first request's answer, when that request succeeded, and runs nothing new. While the first call is still running, a retry with the same key returns 409 request_in_progress; wait and retry.
A repeat of a call that succeeded replays the stored answer with quorum.replayed: true and bills nothing. The replayed quorum block holds request_id, replayed, mode, a note, and seats_answered and answer_truncated when they were recorded. It does not hold converged, remaining_friction, contested_passage, deliberation or billed_usd. Check replayed first and read the other fields with a default. A call that ended capped is not replayed: a retry with the same key runs again and bills again.
Response:
{
"id": "…",
"object": "chat.completion",
"created": 1790000000,
"model": "QRUM:STAN",
"choices": [
{ "index": 0, "message": { "role": "assistant", "content": "…" }, "finish_reason": "stop" }
],
"usage": {
"prompt_tokens": 41,
"completion_tokens": 612,
"total_tokens": 653,
"completion_tokens_details": { "reasoning_tokens": null }
},
"quorum": {
"request_id": "…",
"mode": "QRUM:STAN",
"rounds": 1,
"seats": 3,
"seats_answered": 3,
"converged": true,
"remaining_friction": null,
"contested_passage": null,
"best_seat_rationale": "…",
"best_seat_judged_by": "llm",
"deliberation": [],
"capped": false,
"surcharge_usd": 0.05,
"platform_fee_usd": 0.004,
"cap_credit_usd": 0,
"billed_usd": 0.10,
"latency_ms": 26418,
"engines": ["<seat model id>", "<seat model id>", "<seat model id>"],
"substitutions": [],
"persisted": true
}
}finish_reason is stop or length. length means the answer was cut off. The values in the sample are illustrative.
quorum fields:
| Field | Meaning |
|---|---|
request_id | Id for the receipt endpoint. |
mode | The Mode ticker that ran. |
rounds | Rounds of the seats answering. One pass of the seats is one round. |
seats, seats_answered | Seats convened and seats that answered. seats_answered < seats means the panel was smaller than the one that convened. |
converged | false when the seats did not settle on one position. null when no judge read the round, as on the express lane. Absent on a replay. |
remaining_friction | factual_conflict when the judge flagged a factual conflict between seats, otherwise null. Absent on a replay. |
contested_passage | An object { seat, quote, why }: the seat, the passage it quoted and why it is contested. null when no single passage carried the split. Absent on a replay. |
best_seat_rationale, best_seat_judged_by | Which seat won and why. judged_by is express_lane, heuristic or llm. |
deliberation | One entry per round. |
divergence_first_round, divergence_final_round | The judge's divergence score at the first and the last round. null when no judgment was recorded. |
capped | true when the call went over its ceiling and cap_credit_usd is above 0. The deliberation ran in full and the answer is whole. A capped call is not replayed, so a retry runs again and bills again. |
surcharge_usd, platform_fee_usd, billed_usd | The Mode's surcharge, the platform fee and the total billed. billed_usd is absent on a replay. |
cap_credit_usd | The amount over the call ceiling, credited back. A call that goes over its ceiling is charged in full: tokens, seat fees and the Quorum surcharge. The amount over the ceiling is then credited back as a cap credit, so you pay the ceiling. billed_usd is the token charge plus platform_fee_usd plus surcharge_usd, minus cap_credit_usd. 0 on a call under the ceiling. Absent on a replay. |
latency_ms | End to end. |
engines | The model ids of the seats that ran. |
substitutions | Seats that ran a backup engine, and what ran instead. [] when the panel ran as assigned. |
seats_unavailable, staff_unavailable | Present when a seat or a staff engine could not run, with the reason. |
replayed | true on an idempotent replay. Present only then. |
A deliberation runs several models. Set the client timeout to 300 seconds.
Same request body as chat completions. Free. The response:
{
"request_id": "…",
"model": "QRUM:STAN",
"mode": "QRUM:STAN",
"depth": "medium",
"difficulty_score": 0.61,
"task_type": "analysis",
"estimated_price_usd": 0.10,
"billed_usd": 0,
"deliberation_value": {
"verdict": "likely_helps",
"reason": "multi_step_conclusion",
"basis": "hypothesis",
"evidence": "…"
},
"timing_ms": { "total": 912, "classify": 874 }
}The values in the sample, including timing_ms, are illustrative. estimated_price_usd is the Mode's surcharge for the classified depth. The bill for the real call adds a token charge and the platform fee.
deliberation_value.verdict is likely_helps, likely_hurts or unknown. See the escalation router.
Returns { "object": "list", "data": [...] }. Each entry has id (the ticker), quorum.name, quorum.description, quorum.use_when, quorum.pricing (q_surcharge_usd, q_surcharge_by_depth, q_surcharge_express_usd), quorum.provider_families and quorum.includes_deliberation.
Returns { "ok": true, "receipt": {...} }. Add ?include_transcript=true to include each seat's text. A request id from another organization returns 404 receipt_not_found.
A call that never convened a panel, such as an estimate or a call that ended before a seat fired, returns 200 with { "ok": true, "request_id": "...", "status": "...", "receipt_available": false, "reason": "..." } and no receipt key. Check receipt_available before reading receipt.
Top level of receipt:
| Field | Meaning |
|---|---|
request_id, mode, status | Identity and outcome. |
billed_usd, token_charge_usd, q_surcharge_usd, cap_credit_usd | What the call billed and its parts: billed_usd is the token charge, the seat fees and the surcharge, minus cap_credit_usd. cap_credit_usd is null when no credit was recorded, which is not the same as $0.00. |
prompt_tokens, completion_tokens | Token counts. |
rounds, seat_count | Rounds fired and seats convened. |
payer | Who paid. |
lane | express when one engine answered on its own, panel otherwise. null when no seat row was recorded. |
difficulty_score, task_type, hallucination_risk, minority_insight_likely | The classifier's read of the prompt. |
best_seat | model_used and judge_score of the winning seat. |
seats | One entry per seat, below. |
staff | The judge, synthesis and grounding engines. |
seats_unavailable, staff_unavailable | Each entry is { role, reason }. |
latency_ms, created_at, completed_at | Timing. |
Each entry in seats:
| Field | Meaning |
|---|---|
role | The seat. |
model_used, original_model | The engine that answered and, on a substitution, the one it replaced. |
fallback_used | true when the seat ran its backup. |
judge_score, is_best_seat | The judge's score and whether this seat won. |
round_number | The round. |
tokens_in, tokens_out, latency_ms, started_at, ended_at | Usage and timing. |
source | Which source served the call. |
real_cost_usd | What the call cost at the source. |
fee_rate | The platform fee rate. The default is 5% on Quorum's keys and 0.5% on your own key. |
fee_usd | real_cost_usd times fee_rate. |
A staff engine that could not run shows as unavailable: reason. The fee applies to API calls only, on the source's real billed cost; never on plan usage.
POST /v1/tests runs known-answer questions against a Mode you can call and returns a batch_id and a status_url. Send dry_run: true for a quote. POST /v1/certify runs the certification for a Mode your organization owns. The Mode must be listed in the Marketplace to launch, and a dry_run on an unlisted Mode returns its quote with requires_listing: true. Both are billed per question to the organization, accept Idempotency-Key, and are polled with GET and batch_id. The same operations are the run_test and certify_mode MCP tools.
The server is at https://www.quorum.dog/mcp. It has 13 tools. Setup is in Connect Quorum to your tools. The loop is estimate, deliberate, get_receipt, act.
| Tool | What it does |
|---|---|
deliberate | Sends your question to a panel of models and returns where they agree and where they split. |
list_modes | Lists the Modes you can call, with their prices and the providers behind them. |
estimate | Quotes the Mode's surcharge for the question and says whether a panel is likely to help, free of charge. The bill adds the token charge and the platform fee. |
get_receipt | Shows which model sat in each seat of a past deliberation, with judge scores, cost and latency. |
run_test | Runs a Lab test of a Mode on questions with known answers, graded the way the Mode Builder grades. |
certify_mode | Starts the certification run for a Mode, the same run the Mode Builder starts. |
list_engines | Lists the engines you can put in a Mode's seats, up to the tier your plan or key allows. |
validate_mode | Checks a Mode spec against the rules a save runs and reports what would fail. |
ask_genie | Asks the Mode Maker Genie about one of your Modes: where its results are thin, what to test next and at what price, and whether it is ready to certify. |
approve_quote | Approves a price the Genie quoted and launches that run once, at no more than the quoted price. |
genie_propose_mode | Describe what a Mode is for and the Genie designs all of it, checked against the save rules and priced. |
save_mode | Saves a Mode spec through the same save the Mode Maker uses. |
list_mode | Publishes a released Mode to the Marketplace, as the Publish button in the Mode Builder does. |
deliberate takes model, messages, and optionally max_cost_usd and tier. estimate takes model and messages. get_receipt takes request_id. A connected app signed in to a plan pays real cost with no API surcharge. max_cost_usd applies to API-key calls.
Convene a panel for judgement calls: a consequential assumption, options close enough that the numbers no longer separate them, a diagnosis you cannot check, a decision you will have to defend, or a "what have I missed" check. Use a single model for lookups, syntax, formatting, conversions, arithmetic and anything you need reproduced word for word.
Every error has the OpenAI shape plus a quorum block.
{
"error": { "message": "Rate limit exceeded for this API key.", "type": "rate_limit_error", "code": "rate_limit_exceeded", "param": null },
"quorum": { "request_id": null }
}Branch on error.code. Messages can change.
| Code | HTTP | Retry | Meaning |
|---|---|---|---|
invalid_api_key | 401 | No | Missing, malformed or unknown key. |
revoked_api_key | 401 | No | The key expired or was revoked. |
invalid_oauth_token | 401 | No | A connected app's access token was refused. The message gives the reason. Sign in again. |
org_suspended | 403 | No | The organization's access is suspended. |
browser_origin_not_allowed | 403 | No | The request carried a browser Origin header. |
insufficient_scope | 403 | No | The key lacks the scope for this endpoint or Mode. |
invalid_model | 400 | No | model is missing or not a string. Returned by /v1/estimate, /v1/tests and /v1/certify. |
invalid_messages | 400 | No | messages is missing or empty. /v1/chat/completions also returns it when model is missing. |
invalid_tier | 400 | No | tier is not experience, plus or pro. |
invalid_max_cost_usd | 400 | No | quorum.max_cost_usd is not a positive number. |
stream_not_supported | 400 | No | The request had stream: true. |
context_length_exceeded | 413 | No | The prompt is over 8000 tokens. |
model_not_found | 404 | No | No Mode with that ticker is available to you. Call GET /v1/models. |
tier_not_permitted | 403 | No | Connected apps only. The requested tier or Mode is above the signed-in plan. |
mode_not_permitted | 403 | No | The organization is not entitled to that Mode. |
mode_not_published | 403 | No | The Mode has not been published. |
lab_not_available | 403 | No | The :LAB version is open to its author, team and invited users. |
request_in_progress | 409 | Wait | A call with this Idempotency-Key is still running. |
missing_request_id | 400 | No | The receipt call had no id. |
receipt_not_found | 404 | No | No receipt for that id under your organization. |
method_not_allowed | 405 | No | Wrong HTTP method for the path. |
verification_required | 402 | No | The activation allowance is used. Activate the organization. |
insufficient_balance | 402 | No | The organization's balance is too low. On /v1/tests and /v1/certify it also covers a used quota, a suspended organization or missing billing; the message names the reason. |
quota_exceeded | 402 | No | The monthly quota is used. |
no_billing_configured | 402 | No | The organization has no billing set up. |
plan_limit_reached | 402 | No | A connected app's plan limit is used up. Top up Turbo or wait for the reset. |
rate_limit_exceeded | 429 | Yes | Requests per minute for this key. Honor Retry-After. |
concurrency_limit_exceeded | 429 | Yes | Too many calls in flight for the organization. |
too_many_concurrent_requests | 429 | Yes | Too many deliberations in flight for one person on a connected app. |
classification_failed | 502 | Yes | /v1/estimate could not classify the prompt. Not billed. |
service_unavailable | 503 | Yes | A check could not complete. Try again. |
mode_unavailable | 503 | Yes | No seat the Mode approved can run right now. Not billed. |
upstream_provider_error | 502 | Yes | The deliberation failed. Not billed. |
mode_resolution_mismatch | 502 | Yes | The Mode that ran was not the Mode requested. Not billed. |
internal_error | 500 | Yes | A fault on our side. Retry with the same Idempotency-Key. |
price_unavailable | 400 | No | The Mode has no API price set for the question bands in the run. |
mode_not_owned | 403 | No | Lab and certification run on Modes you own or can edit. |
launch_refused | 400 | No | The launcher refused the run. The message says why. |
listing_required | 400 | No | The Mode is not listed yet. Certification needs a listing. |
invalid_punch_up | 400 | No | punch_up is not true or false. |
invalid_questions | 400 | No | /v1/tests: questions is missing or is not an object like {"easy": 2, "medium": 1}. |
invalid_difficulty | 400 | No | /v1/tests: a questions key is not easy, medium or hard. |
invalid_bucket | 400 | No | /v1/tests: a legacy buckets key is not light, medium or hard. |
invalid_question_count | 400 | No | /v1/tests: a count is not a whole number in the allowed range. The message gives the range. |
too_many_questions | 400 | No | /v1/tests: the counts add up to more than one run allows. The message gives the cap. |
invalid_tiers | 400 | No | /v1/tests: tiers is not a non-empty list drawn from free, plus and pro. experience is accepted as another name for free. |
missing_batch_id | 400 | No | A poll had no batch_id. |
batch_not_found | 404 | No | No run for that batch_id under your organization. |
provider_unavailable | 409 | Wait | A provider the run needs is unavailable. Try later. |
idempotency_key_reused | 422 | No | The key was used with a different body. Send a new key. |
reconciliation_required | 409 | No | An earlier attempt with this key stopped mid-charge. Retry with a new key. |
test_launch_limit_exceeded | 429 | Yes | The organization reached its daily Lab launch limit. Retry-After: 3600. |
certify_launch_limit_exceeded | 429 | Yes | The organization reached its daily certification launch limit. Retry-After: 3600. |
cost_unreadable | 502 | Yes | The deliberation could not be recorded. Not billed. |
MCP tool calls that fail return a result with isError: true. The text of the result is JSON in one of three shapes.
Tools that forward to the REST API (deliberate, list_modes, estimate, get_receipt, run_test, certify_mode) return the REST error object, with the HTTP status beside it. request_id and quorum appear only when the API sent them.
{
"http_status": 404,
"error": { "message": "Unknown model: ACME:NOPE", "type": "invalid_request_error", "code": "model_not_found", "param": null }
}The tools Quorum answers itself (list_engines, validate_mode, ask_genie, approve_quote, genie_propose_mode, save_mode, list_mode) return error.code and error.message. Extra detail, such as refusals or retry_after_seconds, sits in quorum.
{
"http_status": 409,
"error": { "code": "quote_already_used", "message": "Request failed (quote_already_used)." },
"quorum": { "quote": { "status": "launched" } }
}upstream_unreachable and a tool that fails before it can answer (internal_error) return a plain string: { "error": "upstream_unreachable", "detail": "..." }. Branch on error.code when error is an object and on error when it is a string.
These failures arrive outside the tool result, as a JSON-RPC error or an HTTP status:
Authorization: Bearer header is refused before any tool runs, initialize included. It answers HTTP 401 with WWW-Authenticate: Bearer resource_metadata="https://www.quorum.dog/.well-known/oauth-protected-resource/mcp" and a JSON-RPC error with code -32001 and the message Authentication required: sign in with OAuth, or send your Quorum API key as a Bearer token. A connected app reads the challenge and starts sign-in.-32602 and a message naming the argument. The check runs in the MCP server, for run_test, certify_mode and the tools the server answers itself. deliberate, estimate and get_receipt skip it and send the call on, so a missing argument comes back as the REST error in the tool result: deliberate with no model returns invalid_messages, and estimate with no model returns invalid_model. run_test needs model and questions (or the legacy buckets; or batch_id alone), certify_mode needs model (or batch_id alone), ask_genie needs mode and message (or batch_id alone), genie_propose_mode needs description, approve_quote needs quote_id, validate_mode needs spec, save_mode needs spec (or mode_id with release: true), and list_mode needs mode_id or mode.-32700. A body that is not a JSON object or array answers HTTP 400 with code -32600.id and a method the server does not have gets code -32601 in an HTTP 200 reply. A notification, which has no id, gets an empty 202.-32603 and the message Internal error. Retry.GET and DELETE answer HTTP 405 with an Allow: POST header. Send every MCP message as a POST.WWW-Authenticate challenge, so the app refreshes the token or signs in again. An API key that fails gets invalid_api_key or revoked_api_key in the tool result instead.The REST codes above come back unchanged through the forwarding tools. The server adds these:
| Code | HTTP | Retry | Meaning |
|---|---|---|---|
oauth_unavailable | 503 | Yes | Sign-in is temporarily unavailable. |
gate_check_failed | 503 | Yes | The usage check failed. Try again. |
not_configured | 500 | No | The server is not configured for this call. |
upstream_unreachable | none | Yes | The MCP server could not reach the API. |
name_taken | 409 | No | save_mode or list_mode: your organization already has a Mode with that name. |
ticker_taken | 409 | No | save_mode: the ticker is in use. The error carries no suggestion. Each ticker_taken entry that validate_mode lists in refusals, and in quorum.refusals of mode_invalid, carries suggested_ticker. |
batch_too_large | 413 | No | A batch carried more than 8 calls to the tools Quorum answers itself. The extra calls did not run. Send them in a separate request. |
engines_unavailable | 503 | Yes | The engine list could not be read. genie_propose_mode returns it as 503, or as 422 when no engine list was available to build the proposal from. |
mode_not_found | 404 | No | No Mode by that name that you own or your organization has. |
draft_required | 400 | No | save_mode always saves a draft. Pass release: true to release it. |
mode_invalid | 422 | No | save_mode: the spec failed validate_mode. Nothing was saved. quorum.refusals lists why. |
invalid_request | 400 | No | save_mode or list_mode: the save endpoint refused the request and sent no code of its own. |
forbidden | 403 | No | save_mode or list_mode: the key or sign-in cannot change that Mode. |
test_key_not_allowed | 403 | No | save_mode or list_mode: a test key cannot change Modes. Use a live key. |
mode_not_allowed | 403 | No | save_mode or list_mode: the key is limited to named Modes. It cannot create a Mode or change one outside its list. |
org_required | 400 | No | save_mode or list_mode: the person has no organization yet. Modes are saved and listed inside one. |
org_owner_missing | 409 | No | save_mode: the key's organization has no owner to own a new Mode. |
name_too_short | 400 | No | save_mode: the name is too short. The message gives the minimum. |
name_too_long | 400 | No | save_mode: the name is too long. The message gives the maximum. |
name_bad_chars | 400 | No | save_mode: use letters, numbers, spaces and - ' & . , only. |
name_edge_punctuation | 400 | No | save_mode: a name cannot start or end with punctuation. |
name_reserved | 400 | No | save_mode: the name is reserved. Choose another. |
description_bad_chars | 400 | No | save_mode: the description contains <, > or a control character. |
price_below_floor | 400 | No | save_mode: a price is under the Mode's floor. quorum.floor_usd and quorum.recommended_usd carry the numbers. |
pricing_unavailable | 503 | Yes | save_mode: the price floor could not be read. Nothing was saved, or with release: true, nothing was released. |
validation_unavailable | 503 | Yes | save_mode with release: true: the draft could not be checked against the engine list. Nothing was released. |
save_unavailable | 503 | Yes | save_mode: saving Modes is unavailable. Nothing was saved. |
draft_pending | 409 | No | save_mode: the Mode has a draft that was never released. Release it with mode_id and release: true, then save again. |
draft_changed | 409 | Yes | save_mode with release: true: the draft changed while it was being released. Release again. |
no_draft | 409 | No | save_mode with release: true: the Mode has no draft to release. |
draft_unvalidated | 409 | No | save_mode with release: true: the draft has no saved spec. Save it again, then release. |
mode_not_released | 409 | No | list_mode: the Mode was saved as a draft and never released. Release it with save_mode, then list it. |
listing_rejected | 409 | No | list_mode: the listing was rejected in review. |
listing_delisted | 409 | No | list_mode: the listing was taken down. |
listing_changed | 409 | Yes | list_mode: the listing changed while it was being written. List again. |
listing_read_failed | 503 | Yes | list_mode: the current listing could not be read. |
published_version_uncertified | 409 | No | list_mode: the published version is not the certified one. Certify the published version, or publish the certified one, then list. |
ticker_required | 400 | No | save_mode: the Mode needs a four-letter ticker. |
ticker_invalid | 400 | No | save_mode: a ticker is exactly four letters, A to Z. |
conflict | 409 | No | save_mode or list_mode: the save endpoint reported a conflict with the Mode as stored. |
save_failed | varies | Maybe | save_mode or list_mode: the save endpoint failed with no code of its own. The HTTP status is the endpoint's. |
suffix_not_supported | 400 | No | The Genie reads a Mode as it stands. Drop the :LAB or @version suffix. |
genie_unavailable | 502 | Yes | The Genie could not answer. Not billed. |
too_few_engines | 422 | No | genie_propose_mode: the proposal could not fill every seat from the engines open to you. |
no_proposal | 502 | Yes | genie_propose_mode: the Genie answered without a proposal. Ask again. |
chat_history_required | 503 | No | approve_quote: Genie quotes need chat history, which is not switched on. Use run_test or certify_mode. |
invalid_batch_id | 400 | No | ask_genie with batch_id: the id is not a run id. |
status_unavailable | 503 | Yes | The run status could not be read. |
invalid_quote_id | 400 | No | approve_quote: quote_id is not a quote id. |
quote_not_found | 404 | No | approve_quote: no quote with that id under your key or sign-in. |
quote_already_used | 409 | No | approve_quote: the quote was already approved. |
quote_expired | 410 | No | approve_quote: the quote expired. Ask ask_genie for a new one. |
payer_mismatch | 403 | No | approve_quote: the quote bills to the plan or the organization the call does not. Approve it where it was quoted. |
approval_not_recorded | 503 | Yes | The approval could not be saved to the chat, so nothing launched or charged. Approve again. |
quote_read_failed | 503 | Yes | approve_quote: the quote could not be read. |
launch_failed | 502 | Yes | approve_quote: the run could not be confirmed as started. Approve the same quote again, from an API key or a connected app; it returns that run if it started and never launches twice. |
requoted | 409 | No | approve_quote: the price rose above what you approved. Nothing launched. When a new quote could be made, quorum.quote carries it; otherwise ask ask_genie for one. |
approve_quote also passes on the /v1/tests or /v1/certify code that refused the launch, such as insufficient_balance or listing_required. A refusal with no code of its own comes back as launch_refused, and http_status is the status the launch answered with, or 502 when it gave none. That status can be 200 when the launch answered without a run id.
save_mode and list_mode pass on the save endpoint's own code. The codes it can send are in the table above and in the validation codes below. When it sends a status with no code, the tool uses invalid_request (400), invalid_api_key (401), forbidden (403), mode_not_found (404), conflict (409) or save_failed.
save_mode with release: true checks the stored draft again before it releases it. A draft that no longer passes returns the check's own code as error.code, usually with HTTP 400, and the message says what to fix. validate_mode reports the same codes in body.error of each entry in refusals. A check with no code of its own, such as a round count out of range, returns invalid_request with the reason in the message.
| Area | Codes |
|---|---|
| Seats | six_seats_required duplicate_seat_engine seat_engine_not_found seat_engine_above_tier single_family_row seat_selection_required seats_ambiguous engine_unavailable |
| Seat rows by tier | seats_by_tier_invalid seat_tier_unknown seat_tiers_not_contiguous seats_by_tier_needs_fixed seat_tiers_min_tier_mismatch seat_tiers_below_staff tier_required |
| Staff | staff_role_unknown staff_auto_not_allowed staff_tiers_shape staff_tier_unknown staff_tier_is_lowest staff_backup_without_primary staff_backup_same_as_primary staff_engine_above_tier |
| System Modes | system_mode_seats system_mode_seats_untiered system_mode_tier_dropped |
| Auto seats | auto_needs_fixed_pool auto_seats_mixed auto_rows_differ_by_tier auto_seat_backup auto_picked_needs_backup family_diversity_invalid family_diversity_needs_auto |
| Other settings | fidelity_retired fidelity_inert invalid_value |
| Listing and price | description_too_long use_when_invalid use_when_too_long price_required price_override_not_allowed ticker_required ticker_invalid org_required |
price_override_not_allowed (400) means the spec sets api_commercial.price_override_acknowledged. That confirmation is for a person in the Builder. An API key or a connected app may set any price at or above the floor without it, so drop the field. validate_mode lists it first in refusals, and save_mode returns it inside mode_invalid's quorum.refusals.
ask_genie with a quote answers successfully even when it cannot price the run. The result then carries quote_unavailable in place of quote, set to one of these values or to the /v1/tests or /v1/certify error code that refused the price.
| Value | Meaning |
|---|---|
invalid_spec | The run spec is not valid. Fix it and ask again. |
mode_not_owned | A run billed to your plan launches only on a Mode you own. |
chat_history_required | Genie quotes need chat history, which is not switched on. Use run_test or certify_mode. |
quote_not_stored | The quote could not be saved. Ask again. |
quote_failed | The quote could not be made. Ask again. |
| Limit | Default | Scope |
|---|---|---|
| Requests per minute | 60 | Per API key |
| Concurrent requests | 5 | Per organization |
| Input tokens | 8000 | Per request |
| Cost per call | the higher of $2.00 and the price of the Mode being called, unless the API key carries its own ceiling | Per API-key call |
A key can be given higher request and concurrency limits, and a different input limit. rate_limit_exceeded and concurrency_limit_exceeded send Retry-After in seconds.