TOD Docs

API Reference

Get an API key at tod.parseclab.ai

TOD answers typed decisions over a state and returns a calibrated probability for every option. The API follows the Jev /v1/systemone contract: several questions per request, each a choice (2 to 512 labels), a noul (yes/no) or a score (ordinal levels), optionally over images. Nothing is generated, so you pay for input tokens only.

Quickstart

curl https://tod.parseclab.ai/v1/systemone \
  -H "Authorization: Bearer $TOD_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "state": "My card was stolen yesterday and I need it cancelled now.",
    "questions": {
      "intent": {"type": "choice", "instructions": "Which intent is this?",
                 "criteria": {"card_lost_or_stolen": "Card lost or stolen",
                              "cancel_subscription": "Cancel a subscription",
                              "card_arrival": "Waiting for a new card"}},
      "urgent": {"type": "noul", "criteria": {"true": "Needs action today", "false": "Can wait"}},
      "severity": {"type": "score", "criteria": ["none", "minor", "major"]}
    }
  }'
import os, requests

r = requests.post(
    "https://tod.parseclab.ai/v1/systemone",
    headers={"Authorization": f"Bearer {os.environ['TOD_API_KEY']}"},
    json={
        "state": "...",
        "questions": {
            "action": {"type": "choice",
                       "criteria": {"refund": "Give the money back", "replace": "Ship a new unit"}},
        },
    },
    timeout=60,
)
r.raise_for_status()
a = r.json()["answers"]["action"]
print(a["choice"], a["probabilities"], a["confidence"])

Request: POST /v1/systemone

  • state: a string, a JSON object, or a list of {text} / {image} segments.
  • questions: an object of question id → {type, instructions, criteria}.
    • choice: criteria is label → description; returns choice.
    • noul: criteria is {true, false}; returns noul = P(yes).
    • score: criteria is a list of level descriptions; returns score (the most likely level) and a legend.
  • images: optional, up to 8, appended after the state (see Images).
  • model: optional; echoed back.

Context is up to 49,152 tokens; a longer state is refused with state_too_long.

Images

The model is multimodal. Images travel inline in the JSON body, in either form:

  • a data URL: {"image": "data:image/png;base64,…"}
  • raw base64: {"image_b64": "…"}

Put image segments anywhere in a state list to interleave them with text, or pass them in images to append them after the state. PNG, JPEG, WebP and GIF are accepted; the type is read from the bytes, not the data URL. Each image may be up to 5 MB decoded, and a request may carry up to 8. File paths and http(s) URLs are not fetched. Images count toward input tokens like text.

import base64, os, requests

def data_url(path):
    mime = "image/png" if path.endswith(".png") else "image/jpeg"
    return f"data:{mime};base64," + base64.b64encode(open(path, "rb").read()).decode()

r = requests.post(
    "https://tod.parseclab.ai/v1/systemone",
    headers={"Authorization": f"Bearer {os.environ['TOD_API_KEY']}"},
    json={
        "state": [
            {"text": "Customer says the parcel arrived like this:"},
            {"image": data_url("parcel.jpg")},
        ],
        "questions": {
            "damaged": {"type": "noul", "criteria": {"true": "Visibly damaged", "false": "Intact"}},
        },
    },
    timeout=60,
)
print(r.json()["answers"]["damaged"]["noul"])

A malformed, oversized, or unsupported image is refused with 422 bad_image and not billed.

Request: POST /v1/pick

A simpler single-choice form: state, 2 or more options (a list of labels, or label → description), an optional question, and optional images in the same forms as above. Returns choice and probs.

curl https://tod.parseclab.ai/v1/pick \
  -H "Authorization: Bearer $TOD_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "state": "Which screenshot shows the checkout error?",
    "options": ["first", "second"],
    "images": [{"image": "data:image/png;base64,iVBORw0…"}, {"image": "data:image/png;base64,iVBORw0…"}]
  }'

Response

{
  "id": "req_…",
  "model": "utod-picker-12b-v1",
  "answers": {
    "intent": {"type": "choice", "choice": "card_lost_or_stolen",
               "probabilities": {"card_lost_or_stolen": 0.94, "cancel_subscription": 0.05, "card_arrival": 0.01},
               "confidence": 0.91, "confidence_kind": "policy_max",
               "policy_max_confidence": 0.91, "entropy_confidence": 0.71, "latency_ms": 212.0},
    "urgent": {"type": "noul", "noul": 0.88, "probabilities": {"yes": 0.88, "no": 0.12}, …},
    "severity": {"type": "score", "score": 2.0, "probabilities": {"0": 0.03, "1": 0.31, "2": 0.66},
                 "legend": {"0": "none", "1": "minor", "2": "major"}, …}
  },
  "usage": {"input_tokens": 1236, "output_tokens": 0, "cost_usd": 0.000124},
  "latency_ms": 655.2
}

Probabilities sum to 1 across each question's options. confidence is the normalised max, (K·pmax− 1)/(K − 1). Every answered question counts as one decision against the account's free allowance. Usage is billed once per request, on total input tokens.

Errors

{"error": {"code": "bad_labels", "message": "question 'q': choice needs a criteria dict with >=2 labels"}}
  • 401 unauthorized: missing, unknown or revoked key.
  • 402 insufficient_credit: the account's free decisions are used up.
  • 413 state_too_long: the state exceeds the context window.
  • 422: no_questions, bad_question, bad_type, bad_labels, missing_state, bad_image, bad_request.
  • 503 overloaded: at capacity; retry after the Retry-After header.
  • 502 backend_unavailable: the model backend is down.

Failed requests are never charged.