Jers: decisions with memory

Typed questions in, typed answers with probabilities out, one call, no text generation. The same request and answer shape as TypeSafe's /v1/systemone, plus a per-subject memory that lives in RAM on this machine: a subject is whatever you decide about, a customer, a user, a player, a device, a case. Input is free; you pay per answer. Start with getting started and the examples; all pages at /docs, for agents /llms.txt. Try it in the playground.

Jers runs on an open decision engine (Apache-2.0, acknowledged on the models page); its calibration is its authors' claim until you measure it on your own labels (quality). This gateway adds keys, tenants, metering, credit and memory; it never invents an answer: if the engine is down the request fails with 502.

Getting a key

python3 decision_api_server.py keys add --tenant acme --credit 5.00   # prints the key once

A decision

export JERS_API_KEY=jj_live_...
export JERS_BASE_URL=http://127.0.0.1:8797      # or this gateway's address
curl -s $JERS_BASE_URL/v1/systemone \
  -H "Authorization: Bearer $JERS_API_KEY" -H "Content-Type: application/json" \
  -d '{"state": {"message": "We were charged twice for September again."}, "model": "jers-english", "subject": "acme-ops",
       "questions": {"route": {"type": "choice", "instructions": "Which team should handle this?",
                                "criteria": {"billing": "Charges and refunds", "support": "Product problems", "sales": "New licences"}},
                     "refund": {"type": "noul", "instructions": "Is the customer asking for money back?"},
                     "urgency": {"type": "score", "instructions": "How urgent is this?",
                                 "criteria": ["Can wait a week", "Should be handled today", "Blocking the customer now"]}}}'

Leave out subject and the call is stateless. With it, the answer carries a memory block: the lines used, and with "memory": {"compare": true} the answer without memory and what changed.

Memory

POST /v1/memory/remember  {"subject": "acme-ops", "text": "ACME holds an enterprise contract; refunds go to billing within one business day."}
POST /v1/memory/forget    {"subject": "acme-ops", "concept": "enterprise contract"}     removal verified against recall
POST /v1/memory/delete    {"subject": "acme-ops"}
GET  /v1/memory?subject=acme-ops&lines=true
POST /v1/memory/rules     {"subject": "acme-ops", "when": "open_tickets >= 3", "text": "Escalate ACME to a person."}     checked in code on the request's values
GET  /v1/memory/rules?subject=acme-ops
POST /v1/memory/rules/delete {"subject": "acme-ops", "rule_id": "r_..."}

Models

jersJers; its language router picks the checkpoint
jers-englishJers, English checkpoint (ModernBERT-large)
jers-multilingualJers, multilingual checkpoint (mmBERT-base)
jers-typed-decisionsJers, checkpoint fine-tuned on typed-decision workflows

GET /v1/models says which ones answer right now and what is pinned, and lists the jers-ft- models fine-tuned for your tenant.

Prices

per request0.00000 USD
per million input characters0.00000 USD
per answer0.00001 USD
per memory line written0.00001 USD
bill engine answersno

placeholder prices written by the code, not by the owner; input is free, each answer and each memory line written is priced; edit this file and restart. Input, the state and the questions, is free and counted in characters; each question answered is one answer, times the option orders (robust), windows and placebo pass you ask for, and usage.answers_billed shows it; tokens are reported only when the engine counted them. GET /v1/usage shows credit, spending and the last requests; every priced request is one line in the tenant's ledger.

Errors

400 bad Content-Length · 401 no or bad key · 402 no credit left · 403 sign-up off or invite needed · 404 not found · 409 a limit, such as 5,000 golden cases, 10,000 memory lines or 100 rules · 413 body over 1 MB (16 MB for /v1/batches and /v1/golden) · 415 not JSON · 422 bad request, with the reason · 429 over the key's requests per minute, with Retry-After · 500 gateway error, not charged · 502 engine or product down, not charged.

Rules that work in memory

A one-pass engine does not compare numbers and does not read negation. Put conditions in code: a memory rule with when (POST /v1/memory/rules) or derive in the request, checked on values and the state's fields. Leave options that must never be chosen out of criteria, and keep memories short.