verified.create
The hero endpoint: grounded generation with verbatim citations and ungrounded claims removed.
- SDK:
mx.verified.create(params) - HTTP:
POST https://api.maxmodel.com/v1/verified - Auth:
Authorization: Bearer <MAXMODEL_KEY>
Parameters
| Field | Type | Default | Notes |
|---|---|---|---|
model | string | — | Any model your gateway key can call (e.g. gpt-5.5-pro). |
messages | { role, content }[] | — | role is system | user | assistant. |
sources | { id, text }[] | — | Your own chunks. id is yours and is echoed back in citations. MaxModel does not retrieve in v1. |
mode | 'strict' | 'lenient' | 'strict' | How quotes are matched. See How verification works. |
minCoverage | number | 0 (off) | If the grounded share (coverage) is below this, the result is flagged abstained. |
retry | number | 0 | Re-generate up to N more times while any claim is unsupported; stops early when fully grounded. usage.modelCalls reflects the attempts. |
checkNumbers | boolean | 'source' | 'row' | false | Require numbers in a claim to appear verbatim in its source. true/'source' = anywhere in the source. 'row' = in the same table cell / line as the claim’s row+column context — catches the right number, wrong row/column error in tables (e.g. grabbing Q3 revenue as Q3 profit). A claim with an ungrounded number → unsupported with number_not_found. |
allowUngrounded | boolean | false | If true and a question can’t be grounded, return a flagged plain answer instead of erroring. |
temperature | number | — | Passed through to the gateway. |
maxTokens | number | — | Passed through to the gateway. |
The total size of
sourcestext has a budget (~100k chars). Over-budget requests return400 sources_too_large.
Request (HTTP)
{
"model": "gpt-5.5-pro",
"messages": [{ "role": "user", "content": "What is our refund policy?" }],
"sources": [
{ "id": "refunds.md", "text": "Full refunds within 30 days. No refunds after 30 days." },
{ "id": "pricing.md", "text": "Pro is $29/month, billed annually." }
],
"mode": "strict",
"allow_ungrounded": false
}Over HTTP the fields are snake_case (
allow_ungrounded,max_tokens). The TypeScript SDK uses camelCase (allowUngrounded,maxTokens) and converts for you.
Response
{
"id": "vfd_abc123",
"model": "gpt-5.5-pro",
"text": "Full refunds are available within 30 days [refunds.md]. After 30 days, no refunds [refunds.md].",
"citations": [
{
"claim": "Full refunds are available within 30 days",
"quote": "Full refunds within 30 days",
"source": "refunds.md",
"range": [0, 27]
}
],
"unsupported": [
{ "claim": "We also offer a 14-day free trial", "reason": "quote_not_found" }
],
"grounded": true,
"usage": { "prompt_tokens": 812, "completion_tokens": 143, "total_tokens": 955, "model_calls": 1 }
}Response fields
| Field | Type | Notes |
|---|---|---|
text | string | The grounded answer, with inline [sourceId] markers. |
citations[] | Citation | One per grounded claim. |
citations[].claim | string | The grounded statement. |
citations[].quote | string | The verbatim span copied from the source. |
citations[].source | string | The Source.id it came from. |
citations[].range | [number, number] | Char offsets [start, end) into that source’s text — usable for highlighting. |
unsupported[] | Unsupported | Claims that could not be grounded — caught before your users see them. |
unsupported[].reason | 'quote_not_found' | 'unknown_source' | 'number_not_found' | Why it was dropped (number_not_found only when checkNumbers is on). |
coverage | number | Grounded claims / total claims (deterministic). 0 when there are no claims. |
abstained | boolean | true when minCoverage > 0 and coverage < minCoverage. |
grounded | boolean | false only when allowUngrounded=true and we fell back to a plain answer. |
usage.modelCalls | number | A verified call routes ≥ 1 model call through the gateway. |
In the SDK,
usageis camelCase:promptTokens,completionTokens,totalTokens,modelCalls.
Table / cell-level number grounding
Financial, legal, and medical answers live in tables, where the classic failure is the
right number from the wrong row or column. With checkNumbers: 'row', a number must
appear in the table cell whose row label and column header the claim names (or, for
prose, the same line as the claim’s context) — else the claim is dropped:
const out = await mx.verified.create({
model: 'gpt-5.5-pro',
checkNumbers: 'row',
messages: [{ role: 'user', content: 'What was Q3 revenue?' }],
sources: [{ id: 'fin.md', text:
`| Quarter | Revenue | Profit |
|---|---|---|
| Q3 | $12.4M | $3.1M |
| Q2 | $9.8M | $2.0M |` }],
})
// "Q3 revenue was $12.4M [fin.md]" survives.
// "Q3 profit was $12.4M" or "Q3 revenue was $9.8M" would be dropped (wrong cell).Deterministic, markdown tables, no LLM. Keep one fact per claim for the tightest cell binding.
Highlighting with range
Each citation carries the exact character offsets [start, end) of the verbatim quote in the
original source text (computed by the same string match that verified it — exact, not fuzzy).
That makes it trivial to render source-highlighted UIs, footnotes, or audit logs:
const out = await mx.verified.create({ model, messages, sources })
for (const c of out.citations) {
const src = sources.find((s) => s.id === c.source)!
const [start, end] = c.range
// src.text.slice(start, end) === c.quote ← always true, by construction
console.log(highlight(src.text, start, end)) // wrap the span in <mark>, etc.
}Because the offsets come from the deterministic match (not from the model), they’re safe to use for click-to-source highlighting and never drift from the text.
Streaming
/v1/verified is non-streaming in v1 — verification needs the complete claim set
before it can return a trustworthy answer. Use chat.completions.create if you need
token streaming without verification. Streaming verified output is a later item.
TypeScript types
interface VerifiedParams {
model: string
messages: { role: 'system' | 'user' | 'assistant'; content: string }[]
sources: { id: string; text: string }[]
mode?: 'strict' | 'lenient'
allowUngrounded?: boolean
temperature?: number
maxTokens?: number
}
interface VerifiedResult {
id: string
model: string
text: string
citations: { claim: string; quote: string; source: string; range: [number, number] }[]
unsupported: { claim: string; reason: 'quote_not_found' | 'unknown_source' }[]
grounded: boolean
usage: { promptTokens: number; completionTokens: number; totalTokens: number; modelCalls: number }
}