Decisions

The POST /api/v1/systemone contract — request fields, response fields, idempotency, and cancellation.

POSThttps://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

FieldTypeRequiredNotes
statestring, number, boolean, object or arrayyesWhat the questions are about. Plain text is the common case; a JSON object is fine and is serialised for you.
questionsobjectyesQuestion id → question. 1–20 entries.
modelstringnojev-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

HeaderPurpose
Authorization: Bearer sk-glm5-...Required. See Authentication.
Content-Type: application/jsonRequired.
Idempotency-KeyOptional. 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
}
FieldMeaning
idThis request. Keep it: it is what support and request status look up.
modelThe id you asked for.
model_versionThe build that actually answered, date suffix included.
answersOne entry per question id, shaped by its type.
usage.input_tokensWhat the model read. This is the billed quantity.
usage.output_tokensWhat it produced. Counted, never billed.
usage.charged_tokens / charged_credits / walletWhat this call actually cost you. See Billing.
latency_msRound 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:

CodeMeaning
request_in_progressThe original is still running. Poll its status.
request_already_completedThe original finished. Read its status instead of resubmitting.
idempotency_key_reusedSame 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.