Decisions
The POST /api/v1/systemone contract — request fields, response fields, idempotency, and cancellation.
https://jev-ai.org/api/v1/systemone/One state, up to 20 questions, one round trip.
Trailing slash
Every URL on this site canonicalises with a trailing slash, so
/api/v1/systemone answers 308 → /api/v1/systemone/. Browsers, fetch,
requests, axios and the OpenAI SDKs all follow that and preserve the
POST; curl needs -L. Sending the trailing-slash form is simplest.
Request
| Field | Type | Required | Notes |
|---|---|---|---|
state | string, number, boolean, object or array | yes | What the questions are about. Plain text is the common case; a JSON object is fine and is serialised for you. |
questions | object | yes | Question id → question. 1–20 entries. |
model | string | no | jev-1.13 (default) or jev-latest. |
Question ids come back as the keys of answers, so pick names your code will
read: letters, digits, _ and -, starting with a letter or digit, up to 64
characters.
Each question is { "type", "instructions", "criteria"? }. instructions is
free text up to 1,000 characters and should describe the decision, not the
output format — there is no output format to describe. The criteria field
depends on the type and is covered in Question
types.
{
"model": "jev-1.13",
"state": "Order #8814 arrived with a cracked screen. Third time this quarter.",
"questions": {
"refund_requested": {
"type": "noul",
"instructions": "Is the customer asking for a refund?"
}
}
}
Headers
| Header | Purpose |
|---|---|
Authorization: Bearer sk-glm5-... | Required. See Authentication. |
Content-Type: application/json | Required. |
Idempotency-Key | Optional. 8–255 characters from A-Za-z0-9._:-. |
Response
{
"id": "dec_24088aa50f8c6c46361f8a2a",
"model": "jev-1.13",
"model_version": "jev-1.13-20260917",
"provider": "jev-ai.org",
"answers": { "...": "one entry per question id" },
"usage": {
"input_tokens": 503,
"output_tokens": 70,
"charged_tokens": 503,
"charged_credits": 0,
"wallet": "tokens"
},
"latency_ms": 1056
}
| Field | Meaning |
|---|---|
id | This request. Keep it: it is what support and request status look up. |
model | The id you asked for. |
model_version | The build that actually answered, date suffix included. |
answers | One entry per question id, shaped by its type. |
usage.input_tokens | What the model read. This is the billed quantity. |
usage.output_tokens | What it produced. Counted, never billed. |
usage.charged_tokens / charged_credits / wallet | What this call actually cost you. See Billing. |
latency_ms | Round trip to the model, excluding our own overhead. |
Useful response headers: X-Request-ID, X-Jev-Tokens-Used,
X-Jev-Credits-Used, X-Jev-Tokens-Remaining, X-Jev-Credits-Remaining,
X-RateLimit-Remaining-Requests. A 409 carries X-Jev-Request-Status. All of
them are listed in Access-Control-Expose-Headers, so a browser client can read
them too.
The balance headers are diagnostics
X-Jev-Tokens-Remaining and X-Jev-Credits-Remaining are read after the
charge is already committed, and a settled decision is never failed over
them — so on a bad day one of them can be missing from an otherwise perfect
200. Treat them as a convenience; usage.charged_tokens and
usage.charged_credits are the authoritative numbers.
One deliberate difference from the upstream shape
Everything is where a migrating integration expects it — state and
questions in, answers and usage out — with one change: usage.cost
is replaced by charged_tokens, charged_credits and wallet. The upstream
cost field reports the provider's own USD charge, which is not what your
account was billed. These three fields are.
Idempotency
Send an Idempotency-Key and a retry can never produce a second decision.
The key is scoped to your API key. The first request with a given key runs; any
later request with the same key answers 409 instead of running again:
| Code | Meaning |
|---|---|
request_in_progress | The original is still running. Poll its status. |
request_already_completed | The original finished. Read its status instead of resubmitting. |
idempotency_key_reused | Same key, different body. Pick a new key. |
All three include request_id and status_url.
A 409 is not a replay of the original answer: it points you at the original
request rather than re-running it. Store the id from your first successful
response if you need the answers again.
Checking or Cancelling a Request
curl https://jev-ai.org/api/v1/requests/dec_2408.../ \
-H "Authorization: Bearer $JEV_API_KEY"
curl -X POST https://jev-ai.org/api/v1/requests/dec_2408.../cancel/ \
-H "Authorization: Bearer $JEV_API_KEY"
A decision is a single short call, so cancellation is mostly relevant when your own client has already given up. Disconnecting is safe: the hold on your balance is released and nothing is charged.
Concurrency
Decisions are independent, so run them in parallel up to your key's rate limit — see Limits. Batch the questions, not the requests: 20 questions in one call read the state once and are charged once, while 20 calls read it 20 times.