Quick start

Jepela is a hosted API at https://api.jepela.com. There is nothing to install or run. Three steps.

1. A key

Create an account at app.jepela.com and choose your region. Confirm your email address, then pay by card under Billing to start at once (the account is approved as soon as the payment goes through), or wait for Jepela to approve it; then you create your keys in the console. Keep it in JEPELA_API_KEY. Keys start with jj_live_; only their hash is stored.

2. A decision

curl -s https://api.jepela.com/v1/systemone \
  -H "Authorization: Bearer $JEPELA_API_KEY" -H "Content-Type: application/json" \
  -d '{
    "state": {"message": "We were charged twice for September again. We just want the money back."},
    "model": "jepela-latest",
    "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"]}
    }
  }'

The answer, measured on 2026-09-23 through a gateway and product started from this repository (jepela-latest resolved to jepela, whose router picked the English checkpoint). engine.runtime, the engine's own metadata, is left out here; the engine adds fields of its own, such as action, which Jepela passes through (its act_probability was 1.0 on every recorded answer and tells you nothing; see the API reference). The warning says that route has no option for a message that fits none of the three; add "other": "None of these" and it goes away:

{
 "decision_id": "dec_02561a958a6ea3a255a69a71",
 "model": "jepela",
 "engine": {
  "name": "jepela",
  "checkpoint": "english"
 },
 "answers": {
  "route": {
   "type": "choice",
   "choice": "billing",
   "probabilities": {
    "billing": 0.992,
    "support": 0.005,
    "sales": 0.002
   },
   "confidence": 0.955,
   "action": {
    "act_probability": 1.0
   }
  },
  "refund": {
   "type": "noul",
   "noul": 0.897,
   "confidence": 0.897,
   "action": {
    "act_probability": 1.0
   }
  },
  "urgency": {
   "type": "score",
   "score": 1.671,
   "legend": {
    "0": "Can wait a week",
    "1": "Should be handled today",
    "2": "Blocking the customer now"
   },
   "probabilities": {
    "0": 0.008,
    "1": 0.313,
    "2": 0.679
   },
   "confidence": 0.396,
   "action": {
    "act_probability": 1.0
   }
  }
 },
 "usage": {
  "input_characters": 487,
  "questions": 3,
  "answers": 3,
  "answers_billed": 3,
  "engine_answers": 3,
  "memory_lines_used": 0,
  "memory_lines_seen": 0,
  "engine_ms": 36.0,
  "cached": false,
  "state_tokens": 20,
  "state_tokens_read": 20,
  "state_truncated": false,
  "input_tokens": 163,
  "cost": 3e-05,
  "currency": "USD",
  "balance": 4.99994,
  "gateway_ms": 37.1
 },
 "warnings": [
  {
   "code": "no_other_option",
   "question": "route",
   "message": "add an option such as other or none_of_these, so a state that fits nothing is not forced into the least bad option"
  }
 ],
 "reading": {
  "route": {
   "state_tokens": 20,
   "read": 20,
   "room": 480,
   "chars_read": 86,
   "truncated": false
  },
  "refund": {
   "state_tokens": 20,
   "read": 20,
   "room": 478,
   "chars_read": 86,
   "truncated": false
  },
  "urgency": {
   "state_tokens": 20,
   "read": 20,
   "room": 475,
   "chars_read": 86,
   "truncated": false
  }
 }
}

reading says how much of the state the engine read for each question (all of it here); decision_id is what you send back with POST /v1/feedback once you know the right answer.

3. A memory

Write what you know about the subject once, then name it in decisions:

curl -s https://api.jepela.com/v1/memory/remember -H "Authorization: Bearer $JEPELA_API_KEY" -H "Content-Type: application/json" \
  -d '{"subject": "acme", "text": "ACME Logistics holds an enterprise contract; refunds for ACME go to the billing team within one business day.\nACME'\''s account is handled by the enterprise desk."}'

Add "subject": "acme" and "memory": {"compare": true} to the decision request. The answer gains a memory block: the lines used (hits), how many the engine read (lines_seen), the answer without memory, and what changed.

Python

pip install "git+https://github.com/jepelasweden/jepela#subdirectory=sdk/python"
from jepela import JepelaClient, Choice, Noul, Score

with JepelaClient() as client:
    r = client.system_one(
        state={"message": "We were charged twice for September again."},
        questions={"route": Choice("Which team should handle this?", {"billing": "Charges and refunds", "support": "Product problems"}),
                   "refund": Noul("Is the customer asking for money back?"),
                   "urgency": Score("How urgent is this?", ["Can wait a week", "Should be handled today", "Blocking the customer now"])},
        subject="acme", memory={"compare": True})
    print(r.answers["route"].choice, r.answers["route"].confidence, r.memory.lines_used, r.usage.cost)