Q for developers

Public schema and error contracts

Validate request and response schemas and handle the common structured error envelope.

The published contract is GET /v1/openapi.public.json, selected by the capability registry from Q's mounted routes and Pydantic models. Use the contract from the deployment you call. The repository's repaired Q2.2 contract is checked against sdks/openapi/contract-baseline.json; it has been verified locally, not deployed.

Responses and operation identity

Every published operation declares its actual success media types and status codes. JSON resources, collections, answers, jobs, runs and protocol envelopes have named structures. Existing lists, cursor pages, offset responses, empty 204 responses, downloads and SSE streams retain their respective wire formats. Tool arguments, provider results and explicitly extensible metadata remain flexible leaf fields; a typed envelope does not validate arbitrary external payloads.

The registry pins each HTTP operation_id, which becomes OpenAPI operationId. Moving a handler or changing its Python name must not rename the generated operation. The schema uses one primary tag per operation to avoid duplicate generated declarations; additional groupings live in x-q-tags.

Nine retained operations are explicitly unavailable: answer clarify/resume, three agent best-of-n/multitask operations, best-of-n merge and checkpoint branch/resume/rollback. They retain their documented rejection responses and no longer advertise successful execution. Consult each entry's policy.disabled_reason and availability.conditions; discovery is not a provider-readiness probe.

HTTP errors and retry decisions

app/contracts/common.py defines the error model used by runtime handlers and OpenAPI. For example:

{
  "error": {
    "code": "validation_error",
    "message": "Request validation failed",
    "status": 422,
    "correlation_id": null,
    "doc_url": "https://developers.getq.ai/errors#validation_error",
    "retryable": false,
    "outcome": "rejected",
    "issues": [{"location": ["body", "query"], "code": "missing"}]
  }
}

Branch on code, not message text. Existing structured domain codes are preserved. Validation issues contain locations and error codes, never submitted values or validation context, and are capped at 50. correlation_id can be null, especially for admission failures before request logging. request_log_url, operation_id and an identifier receipt may be present when available. Internal exception details are withheld.

retryable is a retry hint, not permission to repeat a side effect. The default is true for rate-limit rejection (429), and for safe GET/HEAD/OPTIONS requests with 408, 502, 503 or 504. Other cases default to false; operation-specific idempotency handling can override this. Mutation failures at 5xx and idempotency_outcome_unknown report outcome: "unknown": reconcile the existing operation before submitting new work. Honour X-Q-Retryable: false, receipts, request cancellation and the idempotency protocol. Preserve the original key when a keyed retry is explicitly permitted.

Two protocol exceptions are explicit: MCP JSON-RPC success/failure envelopes use their JSON-RPC contract at HTTP 200; /v1/status can return its typed health document at HTTP 503 (x-q-error-contract: status-document). Once an SSE response has started, errors follow that stream's event format; an HTTP envelope cannot replace already-sent headers.

Result quality and events

Answer and job/execution contracts share a quality vocabulary:

{
  "completeness": "partial",
  "degraded": true,
  "issues": [{"code": "retrieval_component_failed", "component": "knowledge", "retryable": false}]
}

completeness is complete, partial or unknown. Retrieval component failures mark answer evidence partial and degraded. Producers without completeness measurements return unknown; an empty issue list or HTTP success is not evidence of complete or accurate work. A job's terminal status and its result quality answer different questions. Job errors distinguish recoverable execution state from retryable resubmission.

Answer, AI job and gateway execution stream envelopes have shared models referenced by x-q-sse-data. Existing event names and framing remain in place. These envelope models do not establish a uniform replay protocol, event ordering guarantee or complete schema for every event payload; Q2.4 owns that convergence.

Input limits and pagination

Public /v1/* HTTP admission limits bodies to 2 MiB and encoded query strings to 16 KiB, before body-buffering middleware or handlers. Actual body bytes are counted, including fragmented bodies without a trustworthy Content-Length. Over-limit responses are 413 request_body_too_large or 414 query_too_large. Endpoint schemas may impose stricter limits. These limits do not apply to legacy /api/v1/* paths.

Cursor inputs are limited to 2,048 characters. Malformed, structurally invalid and wrong-type anchors produce 400 invalid_cursor; schema-level length violations produce 422. Cursor pages support forward and backward traversal with deterministic ID tie-breaking. has_more means a next_cursor is available. Cursors identify a position, not a transaction snapshot or permission grant; concurrent writes and deletions can change later pages.

x-q-pagination identifies cursor, offset or unpaginated operations. Existing collection formats have not been replaced with one new envelope. Spaces and artifacts now expose their existing cursor and limit controls on the public wrapper. See pagination. MCP JSON-RPC batches contain 1–100 typed request objects; malformed batches fail validation before tool execution.

Maintaining and checking compatibility

  1. Change the producer and its Pydantic model together. Use a response model or explicitly validate a manually constructed JSON response. Declare stream/text/binary media types instead of a fictional JSON result.
  2. Preserve the registry operation ID and review auth, availability, pagination and input-limit metadata with the route.
  3. Run the required contract check. It exports this checkout's actual schema in an isolated environment, compares it with the baseline, runs HTTP/schema regressions, then generates and compiles fresh clients.
# After the deterministic backend bootstrap, with Node 22 and Java 17 available:
.venv/bin/python -m scripts.release.sdk_tools --install
.venv/bin/python -m scripts.release.check contract
.venv/bin/python -m scripts.release.check policy --base HEAD

The installation command needs network access once. It verifies OpenAPI Generator 7.25.0 by SHA-256, installs TypeScript 5.9.3 from a lockfile and Python dateutil from a hashed requirement into .cache/q-test/contract-tools. Verification itself uses that cache and fails if tools are missing or invalid. Generated output is temporary: Python syntax and exported model/API imports are checked, and TypeScript is compiled in strict mode. Existing generated SDK source directories are not overwritten. Reports and the schema digest are saved under .cache/q-test/release/.

The compatibility checker rejects removed operations/properties, changed operation IDs/security, narrowed inputs, widened outputs, incompatible required fields/enums/bounds, missing response/media types and changed stream contracts. It resolves local component references and compares supported constructs conservatively; unfamiliar structural changes require review. Descriptions and examples can change. Deliberate breaking mutations are regression-tested. The policy check also compares the baseline against the Git base so editing both the schema and its baseline cannot conceal a break.

The initial baseline records the repaired Q2.2 contract, including previously unspecified responses; it does not invent compatibility guarantees for the old 80 missing schemas. Future incompatible changes require an explicit version/migration decision and a reviewed change to enforcement. Do not regenerate the baseline just to make a failing check pass, add schema exceptions or disable validation.

Generated-client compilation verifies this checkout's schema and generation configuration. It does not qualify released packages, handwritten SDK transports, streamed response decoding, a canonical production hostname or live providers. Q2.3 adds installed-package transport qualification and a verified default hostname; see SDK support. Full stream semantics and live-provider/deployment qualification remain separate work. See release checks, migration notes and ADR 0011.

On this page