> ## Documentation Index
> Fetch the complete documentation index at: https://read.sqwish.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# API behavior

> Question types, billing, retries, errors, and data retention.

D1 answers structured questions about a shared text or JSON-object context.
Staging is invitation-only. Sign in to the console, wait for admission, and create
an API key. Copy the secret when it is displayed; it cannot be displayed again.
Keep API keys on your server, never in a public browser bundle or source control.

Check `simulated` in `/v1/models`: when true, predictions are random sample answers
returned after a short artificial delay. They exercise integration and test-credit
accounting; scores, timing and synthetic token counts do not describe a real model.
The playground labels these results. Replays still return the original stored answer
without another charge.

Once deployed, the initial real model uses an **uncalibrated baseline**. Probabilities
are model scores, not verified frequencies or guarantees of correctness. Evaluate
them on representative examples before relying on them. A model appears in discovery
only when available to that deployment; an empty list means prediction is not ready there.

## Authentication

Use the staging HTTPS origin supplied with your invitation. All `/v1` routes use
`Authorization: Bearer <API key>`. The browser console manages its own session.

The [quickstart](/quickstart) includes a complete request. The API reference
provides curl, Python, and JavaScript examples. Generate a new operation key only when
deliberately starting a new prediction.

The deployment's [OpenAPI schema](https://d1-staging-5gh8d.ondigitalocean.app/openapi.json)
and [local API reference](https://d1-staging-5gh8d.ondigitalocean.app/docs) describe
its running API.

| Method and route                | Purpose                                                                                                   |
| ------------------------------- | --------------------------------------------------------------------------------------------------------- |
| `POST /v1/predictions`          | Predict and settle the actual charge before returning `200`.                                              |
| `POST /v1/predictions/estimate` | Validate and meter the same input without inference or a debit.                                           |
| `GET /v1/models`                | Discover opaque model IDs, simulation status, capabilities, effective limits, price, and score semantics. |
| `GET /v1/balance`               | Read available credit, holds, expiring grants, and any financial shortfall.                               |
| `GET /v1/usage`                 | Read request metadata and actual charges without context or answers.                                      |

## Questions and answers

A request contains `model` (optional; defaults to `"default"`), `context`, and an
ordered `questions` array. Context must be a string or JSON object, not a top-level
array or scalar. IDs contain 1–128 ASCII letters, digits, underscores, periods, or
hyphens. Question IDs are unique within the request. Prompts and descriptions are
nonempty strings. Unknown fields are rejected.

| Type     | Question fields                                                                                          | Answer fields and meaning                                                                                                                                                                                                                          |
| -------- | -------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `binary` | `id`, `type`, `prompt`                                                                                   | `id`, `type`, `probability_true`: finite number in `[0, 1]`.                                                                                                                                                                                       |
| `choice` | Common fields plus ordered `options: [{id, description}, …]`; option IDs are unique within the question. | Ordered `options: [{id, probability}, …]` covers every input option. Probabilities are in `[0, 1]` and sum to one within the release's validation tolerance. `selected` is the greatest-probability option ID; ties choose the first input option. |
| `score`  | Common fields plus ordered `levels: [description, …]`.                                                   | `id`, `type`, `value`. The staging baseline returns the expected zero-based level index: `0` through `levels.length - 1`, including fractional values. Read the model's `score` metadata for the active release's semantics.                       |

Answers preserve question order and IDs. A five-level score can be `2.4`; it is
not automatically a probability or
an integer category. The baseline normalizes scores over fixed answer labels;
that computation does not establish probability calibration.

Global ceilings are 16 questions, 32 options per choice, 10 levels per score, and
262,144 UTF-8 request-body bytes. Choice and score questions require at least two
entries. Deployments and validated releases can impose lower ceilings; use the
`limits` returned by `/v1/models`. Context, total billable input, and native
formatted-input limits are checked separately. An estimate can therefore reject
input that is below the byte ceiling. Do not silently truncate rejected input.

## Estimates, charges, and credit

Send the prediction body to `/v1/predictions/estimate` to receive `model`,
`input_tokens`, `estimated_cost_usd`, `metering_version`, and `pricing_version`.
An estimate is advisory: it does not reserve credit or pin a later prediction's
price. A successful prediction reports its actual pinned `model`, ordered
`answers`, and `usage` with `input_tokens`, `cost_usd`, `metering_version`, and
`pricing_version`.

USD amounts are exact decimal **strings with nine fractional digits**, for
example `"0.000200000"`. Preserve them as decimal strings or use decimal/integer
arithmetic; do not add balances using binary floating point.

Billing counts the context once, then each question prompt, choice description,
and score-level description in separate tokenizer calls. IDs, API field names,
hidden templates, generated answers, and repeated internal processing are not
billed. Text context is preserved. JSON-object context uses deterministic compact
UTF-8 serialization described in [canonical JSON and money](/fixtures/canonical-json).
The model's metering version identifies the tokenizer and counting convention.
In simulated mode, `meter_simulated_v1` counts each of these billable fields as
`ceil(UTF-8 bytes / 4)` instead of using a model tokenizer.

The request charge rounds up **once**, after summing billable tokens:

```text theme={null}
nano-USD = ceil(input_tokens × price_nano_USD_per_million / 1,000,000)
1 USD = 1,000,000,000 nano-USD
```

`available_usd` excludes holds and expired credit. `paid_usd` and
`promotional_usd` include unspent held credit while its grant is valid;
`reserved_usd` can also contain an in-flight allocation from a grant that expired
after acceptance. That reservation does not make the expired grant spendable
again. `financial_shortfall_usd` is a positive amount owed after reversal of
already-spent paid credit; spendable paid credit and availability are then zero.
Expiring promotional credit is used first, then non-expiring promotional credit,
then paid credit.

## Idempotency, deadlines, and retries

Send a unique nonempty `Idempotency-Key` of at most 200 characters for every
logical prediction. The header is optional, but a request without one cannot use
key-based replay. Scope is your account and prediction route. Send the same key
and the same full request when recovering from a lost response. Object-key order
and JSON whitespace do not matter; question/option order, IDs, and original model
selection do. Omitting `model` and specifying `"default"` are equivalent.

An accepted operation keeps its selected model, tariff, and allocation even if
the default model or pricing changes. A replay never starts another inference
or settles another charge. Replays still require a valid key and active account
and have an independent read limit. An inference pause does not block an
otherwise authorized completed replay.

* `200` means the answer and charge are durably committed.
* `409 request_in_progress` means retry the identical operation after
  `Retry-After` (normally one second).
* `409 idempotency_conflict` means the key was used with different input.
* Accepted terminal failures are replayed, remain uncharged, and need a new key
  for a deliberate new attempt. Pre-acceptance validation, quota, or insufficient
  credit rejection does not consume the key.
* Default replay retention is 24 hours from terminal completion. Afterwards,
  `409 idempotency_expired` prevents new inference until the tombstone expires
  30 days from initial acceptance. **Never recycle old keys**: after tombstone
  expiry, an old key may be accepted as new work.

The default acceptance-to-settlement deadline is 30 seconds. Orphan recovery
starts after 60 seconds from acceptance. Client timeouts include network and
validation time too; the examples allow 40 seconds. A client disconnect or
intermediary timeout can race successful settlement. Keep the key and request,
then replay; do not infer “not charged” from a lost response or immediately
create a new operation. These guarantees are at-most-once settlement per accepted
operation, not a guarantee of network delivery.

## Errors and quotas

Errors use this envelope, with a server-generated `X-Request-ID` header:

```json theme={null}
{"error":{"code":"insufficient_balance","message":"Your available balance is below the cost of this request.","request_id":"req_example"}}
```

Use `error.code` for handling and preserve `request_id` when contacting support.
Do not parse human-readable message text. Honor `Retry-After` when present.

| HTTP                | Typical codes                                                        | Action                                                                              |
| ------------------- | -------------------------------------------------------------------- | ----------------------------------------------------------------------------------- |
| `400`, `415`, `422` | `invalid_json`, `invalid_request`, `model_unavailable`               | Correct the JSON, media type, model, or question.                                   |
| `401`               | `invalid_api_key`                                                    | Check or replace the server-side key.                                               |
| `402`               | `insufficient_balance`                                               | Add credit or reduce the request.                                                   |
| `403`               | `access_waitlisted`, `account_suspended`                             | Check access in the console.                                                        |
| `409`               | Idempotency codes above; `request_cancelled`                         | Resolve the existing operation. Cancellation is a terminal uncharged result.        |
| `413`               | `request_too_large`                                                  | Reduce input explicitly.                                                            |
| `429`               | `rate_limit_exceeded`, `concurrency_limit_exceeded`                  | Wait for the indicated delay.                                                       |
| `502`               | `invalid_model_output`                                               | Terminal uncharged serving failure.                                                 |
| `503`               | `capacity_unavailable`, `inference_paused`, `dependency_unavailable` | Preserve the operation key and resolve uncertain outcomes before creating new work. |
| `504`               | `prediction_timeout`                                                 | Replay the same operation to resolve its recorded outcome.                          |

Unexpected `500` responses and non-JSON errors from network intermediaries can
also leave the outcome uncertain. Do not automatically retry billable work with
a new key.

Account RPM/TPM windows are UTC calendar minutes, so a boundary burst is possible.
New authenticated prediction and estimate attempts count toward RPM; accepted
prediction tokens count toward TPM. Active reservations count toward account
concurrency. Estimates also have a separate tokenization budget. The console
shows the account's effective limits.

## Usage and pagination

`GET /v1/usage` accepts `start` (inclusive), `end` (exclusive), `model`, `limit`,
and `cursor`. Supply timezone-aware ISO 8601 timestamps, preferably UTC `Z`.
Default page size is 50; valid sizes are 1–100. Results use newest-first stable
`(created_at, id)` order and contain `{items, next_cursor}`. Send `next_cursor`
unchanged with the same filters to continue; `null` ends the list. Cursors are
opaque and account/filter scoped. Never construct or share them between accounts.

Each item includes `request_id`, `model`, question/token counts, `status`,
`charge_usd`, `pricing_version`, `created_at`, and nullable `completed_at`.
Only successful requests have a charge. Usage contains accepted request metadata,
not every rejected HTTP attempt, and never exposes context or answers.

## Staging payments and data retention

Use the console's server-defined packages and hosted **test-mode** checkout.
Staging credit purchases do not represent live sales. A successful browser return
does not itself grant credit: payment history shows when fulfillment is confirmed.
Refunds/reversals remove paid principal; reversing already-spent credit can
suspend inference pending account review. Authentication and billing history
remain available during suspension.

By default the application retains encrypted successful responses and terminal
error receipts for 24-hour replay, inference-key tombstones for 30 days, and usage
metadata for 90 days. Input bodies are not retained by default. Diagnostic content
capture is disabled for invited staging; any later capture must be separately
account-scoped, time-limited, and audited. Application email payloads default to
30 days and delivery metadata to 90 days. Financial entries and their minimal
deduplication evidence are retained separately rather than expiring with replay.

These are application access/retention periods, not promises about every backup
or external provider copy. Expired replay data is inaccessible before physical
cleanup. Backup and provider retention must be disclosed for the deployed service;
do not send data requiring an unconfirmed retention guarantee.
