Docs·API reference
Execute
POST /v1/execute runs a program over a document, for example an answer's program after you edit it. No model call, no tokens billed.
POST https://platform.xerj.ai/v1/execute
Authorization: Bearer $XERJ_API_KEY
Content-Type: application/jsonEvery answer comes with the program that produced it, in symbolic.program. Execute runs a program over a document with the same executor, without the model. Change a number format, point a quote at other characters, add a step, and see what the program gives. The program editor in the playground does the same.
No tokens are billed and nothing is charged from your balance. Each call counts against your requests per minute.
Request #
| Field | Type | Required | Description |
|---|---|---|---|
document |
string | yes | The text the program reads. state and text are accepted as other names for it. Up to 1,000,000 characters; the model's token window does not apply, because the model does not read it. |
program |
array or string | yes | The program as JSON, or the same as a JSON string of up to 20,000 characters. How to read and write one. |
type |
string | no | The type the result must have: one of the field types, or auto (default: inferred from question, else text). |
question |
string | no | The question the program answers. It comes back in the result, and it sets the type when type is auto. |
options, id_kind, name, spec |
no | The rest of the field, as in /v1/answers. A choice needs options. |
|
ref_date |
string | no | YYYY-MM-DD: the date that “today” means, and the year for dates written without one. |
model |
string | no | Default xerj-reader-1. |
A quote instead of offsets #
A SPAN can give its text instead of its character offsets:
["SPAN", "d1", "48,200.00 EUR"]is the first place where that text occurs;["SPAN", "d1", "EUR", 2]is the second place.
Quotes are turned into offsets before the program runs, and the response shows the program with offsets. A quote that is not in the document is 400 invalid_program.
Response #
The same fields as an answer, with these differences:
| Field | Description |
|---|---|
id |
exe_… |
object |
Always "execution". |
question |
The question you sent, or "(edited program)". |
confidence |
null, and answer.confidence too: no model wrote this program. |
symbolic |
The program with offsets, and its run. There are no model fields (confidence, model, model_label, finished, decode_error, n_prompt_tokens, n_gen_tokens), and ms has only execute. |
usage |
Always {"input_tokens": 0, "output_tokens": 0, "total_tokens": 0}. |
A program that does not give a value is not an error. The response is a 200 whose status says why, such as UNKNOWN or UNSUPPORTED, with a reason.
Examples #
The document is the invoice of the Extract example. Every response is a real run; id, created and latency_ms are left out.
Run a program, with a quote #
curl https://platform.xerj.ai/v1/execute \
-H "Authorization: Bearer $XERJ_API_KEY" \
-H "Content-Type: application/json" \
-d @- <<'EOF'
{"document": "…the invoice…", "question": "What is the total amount due?", "type": "money",
"program": [["SPAN", "d1", "48,200.00 EUR"], ["MONEY", "auto"]]}
EOFr = client.execute.create(
document=invoice,
program=[["SPAN", "d1", "48,200.00 EUR"], ["MONEY", "auto"]],
type="money",
question="What is the total amount due?",
)
print(r.status, r.value_text, r.symbolic.program)const r = await client.execute.create({
document: invoice,
program: [["SPAN", "d1", "48,200.00 EUR"], ["MONEY", "auto"]],
type: "money",
question: "What is the total amount due?",
});
console.log(r.status, r.value_text, r.symbolic.program);The complete response:
{
"object": "execution",
"model": "xerj-reader-1",
"question": "What is the total amount due?",
"type": "money",
"status": "ANSWER",
"value": {"amount": "48200.00", "currency": "EUR"},
"value_text": "48200.00 EUR",
"reason": null,
"confidence": null,
"evidence": [{"start": 459, "end": 472, "quote": "48,200.00 EUR"}],
"answer": {"type": "value", "text": "48200.00 EUR", "start": null, "end": null, "confidence": null},
"best_guess": null,
"symbolic": {
"program": [["SPAN", "d1", 459, 472], ["MONEY", "auto"]],
"program_compact": "MONEY(“48,200.00 EUR”, \"auto\") = 48200.00 EUR",
"spans": [{"doc": "d1", "start": 459, "end": 472, "quote": "48,200.00 EUR"}],
"status": "ANSWER",
"value": {"amount": "48200.00", "currency": "EUR"},
"value_text": "48200.00 EUR",
"reason": null,
"evidence": [{"doc_id": "d1", "char_start": 459, "char_end": 472, "quote": "48,200.00 EUR", "exact": true, "path": "", "chain": []}],
"checks": [],
"coverage": null,
"withheld_value": null,
"candidates": null,
"typecheck": "Money",
"field": {"name": "answer", "instruction": "What is the total amount due?", "spec": {"type": "money"}},
"type": "money",
"type_inferred": false,
"ms": {"execute": 0}
},
"usage": {"input_tokens": 0, "output_tokens": 0, "total_tokens": 0}
}The quote became the offsets 459, 472.
Edit a program #
The same program with the number format "nl-NL" instead of "auto", sent as a JSON string:
{"document": "…the invoice…", "question": "What is the total amount due?", "type": "money",
"program": "[[\"SPAN\", \"d1\", \"48,200.00 EUR\"], [\"MONEY\", \"nl-NL\"]]"}MONEY(“48,200.00 EUR”, "nl-NL") → UNKNOWN ('48,200.00 ' is not a nl-NL number)status is UNKNOWN and value is null: in that format 48,200.00 is not a number, so the executor gives no value rather than a wrong one. A field named total once got this exact program from the model; see field names.
A computed value #
{"document": "…the invoice…", "type": "number",
"program": ["SUB", [["SPAN", "d1", "30 November 2025"], ["DATE", "auto"]],
[["SPAN", "d1", "14 October 2025"], ["DATE", "auto"]]]}SUB(DATE(“30 November 2025”, "auto"), DATE(“14 October 2025”, "auto")) = 47value is "47", and evidence holds both dates: 30 November 2025 at 75–91 and 14 October 2025 at 49–64.
The wrong type #
A quote alone is text. Asked for money, the program is refused with a reason:
{"document": "…the invoice…", "type": "money", "program": ["SPAN", "d1", "PO-5512"]}“PO-5512” → UNSUPPORTED (program returns Text, field is money)Errors #
| Status | Code | When |
|---|---|---|
| 400 | missing_program |
program is missing. |
| 400 | invalid_program |
program is not valid JSON, is longer than 20,000 characters, or quotes text that is not in the document. The message says which, for example SPAN: quoted text 'no such text' not found in d1 (occurrence 1). |
| 400 | missing_document, document_too_long |
document is missing, empty, or longer than 1,000,000 characters. |
| 400 | invalid_type |
type is not one of the field types. |
| 400 | invalid_fields |
options, id_kind, name or spec is malformed; a choice without two options has param options. |
| 400 | missing_question |
question is not a string. |
| 400 | invalid_ref_date, invalid_json |
As in /v1/answers. |
| 401 | missing_api_key, invalid_api_key, revoked_api_key |
See errors. |
| 403 | access_not_active |
The account's access is not active yet. |
| 404 | model_not_found |
The model does not exist. |
| 429 | requests_per_minute |
See rate limits. |
| 503 | queue_timeout |
The service is starting. Retry after retry-after seconds. |
There is no 402: execute bills no tokens, so it runs at any balance.