Endpoint, credentials and current version

TypeSafe handles decisions, classification and scoring through POST https://monkeytheme.com/v1/systemone. It returns a complete JSON response, rather than chat messages or a stream. Register or sign in, then select a TypeSafe group key under API keys.

The current backend accepts only jev-latest. The model plaza also lists jev-1.13.0 and jev-1.14.0, but pinned versions are not currently guaranteed to be callable. A listing is not proof that a version can be called. All requests here use jev-latest, without asserting its current version mapping. For reproducibility, retain the request, timestamp and returned model, and confirm version support first.

No example was sent to the service and no response values are fabricated. Running these scripts transmits your data and may incur charges. Read keys from the environment; keep them out of browser code and Git.

bash
export TYPESAFE_API_KEY="YOUR_API_KEY"
powershell
$env:TYPESAFE_API_KEY="YOUR_API_KEY"

Minimal Bash/WSL cURL request (requires the environment variable; running it sends a request):

bash
curl --fail-with-body --connect-timeout 15 --max-time 120   "https://monkeytheme.com/v1/systemone"   -H "Authorization: Bearer $TYPESAFE_API_KEY"   -H "Content-Type: application/json"   --data-binary '{"model":"jev-latest","state":"Please replace my damaged parcel.","questions":{"replacement":{"type":"noul","instructions":"Is a replacement requested?"}}}'

state, questions and answer structure

Supply model, state and a nonempty questions object. State holds the material as a string, object or array. Question keys are your result IDs; each question's instructions defines the task. The response's answers uses those IDs. Do not send messages, prompt or stream=true. Image assets belong in top-level assets.

typecriteriaReading the result
noulOptional object with true / false descriptionsanswers.ID.noul is a probability from 0 to 1, not a boolean; apply a calibrated application threshold
choiceObject mapping option IDs to descriptions; reference protocol allows 1–255answers.ID.choice identifies an option; also inspect confidence and probabilities
scoreOrdered descriptions from low to high; reference protocol allows 1–10 levels indexed from 0answers.ID.score is a probability-weighted score and can be fractional; inspect legend, confidence and probabilities
boxInstructions identify one specific targetExperimental image localization; current MonkeyAI availability is unverified

Questions share state but are evaluated independently. A question cannot depend on another question's answer within the same request. Implement dependent stages as separate application calls. Confidence and probability are different measures. Calibrate thresholds such as 0.8 using your own validation set; probability is not a boolean or a guaranteed accuracy figure.

Python: decisions, classification, scoring and file mode

Python 3 requires only its standard library. Save as typesafe_demo.py and run python3 typesafe_demo.py text for text or python3 typesafe_demo.py file for a local document. Image mode is an unverified example: confirm channel support before using it. The program prints actual responses rather than assuming successful answers.

python
import base64
import json
import os
import sys
import urllib.error
import urllib.request
import uuid
from pathlib import Path

# python typesafe_demo.py text|file|image

def evaluate(body):
    request = urllib.request.Request(
        "https://monkeytheme.com/v1/systemone",
        data=json.dumps(body, ensure_ascii=False).encode("utf-8"),
        headers={"Authorization": "Bearer " + os.environ["TYPESAFE_API_KEY"],
                 "Content-Type": "application/json"}, method="POST")
    try:
        with urllib.request.urlopen(request, timeout=120) as response:
            result = json.load(response)
    except urllib.error.HTTPError as error:
        # Do not log credentials or private request contents.
        raise RuntimeError(f"HTTP {error.code}; inspect a redacted error body locally") from error
    if not isinstance(result.get("answers"), dict):
        raise ValueError("Response has no answers object")
    return result

try:
    mode = sys.argv[1]
    body = {"model": "jev-latest", "state": {"note":
        "The delivery arrived damaged. Can someone arrange a replacement this week?"},
        "questions": {
            "replacement": {"type": "noul", "instructions": "Is a replacement requested?",
                "criteria": {"true": "A replacement is requested.",
                             "false": "No replacement is requested."}},
            "route": {"type": "choice", "instructions": "Select the responsible team.",
                "criteria": {"delivery": "Damaged or missing deliveries.",
                             "account": "Account access.", "other": "Other issues."}},
            "priority": {"type": "score", "instructions": "Assess the stated time pressure.",
                "criteria": ["No deadline.", "Within a week.", "Immediate action required."]}}}
    if mode == "file":
        body["state"] = Path("document.txt").read_text(encoding="utf-8")
        body["questions"] = {"renewal": {"type": "noul",
            "instructions": "Does the document require an explicit renewal decision?"}}
    elif mode == "image":
        # Unverified on MonkeyAI: confirm image capability before using this mode.
        ref = str(uuid.uuid4())
        body["state"] = {"photo": ref}
        body["assets"] = {ref: {"type": "image", "format": "png",
            "data_base64": base64.b64encode(Path("input.png").read_bytes()).decode("ascii")}}
        body["questions"] = {"label_visible": {"type": "noul",
            "instructions": "Is a shipping label visible in the image?"}}
    elif mode != "text":
        raise ValueError("Use text, file or image")
    result = evaluate(body)
    print(json.dumps(result, ensure_ascii=False, indent=2))
    if mode == "text":
        probability = result["answers"]["replacement"]["noul"]
        if isinstance(probability, bool) or not isinstance(probability, (int, float)) or not 0 <= probability <= 1:
            raise ValueError("Invalid noul probability")
        # 0.8 is an illustrative policy threshold, not a service guarantee.
        print("Review for replacement:", probability >= 0.8)
    print("usage:", result.get("usage"))
except (OSError, ValueError, KeyError, TypeError, RuntimeError) as error:
    print("TypeSafe request failed:", error, file=sys.stderr)
    sys.exit(1)

Node.js: the same tasks without dependencies

Use Node.js 18+ with fetch and AbortSignal.timeout, preferably a maintained LTS release. Save as typesafe_demo.mjs; run node typesafe_demo.mjs text, file, or image after confirming support. HTTP, JSON, file and timeout errors reach the error handler. There are no automatic retries that silently add requests.

javascript
import fs from "node:fs/promises";
import {randomUUID} from "node:crypto";

// node typesafe_demo.mjs text|file|image
async function evaluate(body) {
  const key = process.env.TYPESAFE_API_KEY;
  if (!key) throw new Error("Set TYPESAFE_API_KEY");
  const response = await fetch("https://monkeytheme.com/v1/systemone", {
    method: "POST", headers: {Authorization: `Bearer ${key}`, "Content-Type": "application/json"},
    body: JSON.stringify(body), signal: AbortSignal.timeout(120000)
  });
  if (!response.ok) throw new Error(`HTTP ${response.status}; inspect a redacted error body locally`);
  const result = await response.json();
  if (!result.answers || typeof result.answers !== "object" || Array.isArray(result.answers))
    throw new Error("Response has no answers object");
  return result;
}
try {
  const mode = process.argv[2];
  const body = {model: "jev-latest", state: {note:
    "The delivery arrived damaged. Can someone arrange a replacement this week?"},
    questions: {
      replacement: {type: "noul", instructions: "Is a replacement requested?",
        criteria: {true: "A replacement is requested.", false: "No replacement is requested."}},
      route: {type: "choice", instructions: "Select the responsible team.",
        criteria: {delivery: "Damaged or missing deliveries.", account: "Account access.", other: "Other issues."}},
      priority: {type: "score", instructions: "Assess the stated time pressure.",
        criteria: ["No deadline.", "Within a week.", "Immediate action required."]}
    }};
  if (mode === "file") {
    body.state = await fs.readFile("document.txt", "utf8");
    body.questions = {renewal: {type: "noul",
      instructions: "Does the document require an explicit renewal decision?"}};
  } else if (mode === "image") {
    // Unverified on MonkeyAI: confirm image capability before using this mode.
    const ref = randomUUID();
    body.state = {photo: ref};
    body.assets = {[ref]: {type: "image", format: "png",
      data_base64: (await fs.readFile("input.png")).toString("base64")}};
    body.questions = {label_visible: {type: "noul",
      instructions: "Is a shipping label visible in the image?"}};
  } else if (mode !== "text") throw new Error("Use text, file or image");
  const result = await evaluate(body);
  console.log(JSON.stringify(result, null, 2));
  if (mode === "text") {
    const probability = result.answers.replacement.noul;
    if (typeof probability !== "number" || probability < 0 || probability > 1)
      throw new Error("Invalid noul probability");
    console.log("Review for replacement:", probability >= 0.8);
  }
  console.log("usage:", result.usage);
} catch (error) {
  console.error("TypeSafe request failed:", error.message);
  process.exitCode = 1;
}

Image input and Box: unverified capabilities

Image mode illustrates the reference JSON + Base64 structure: create a UUID, place type: image, format: png and plain data_base64 under assets, then reference the UUID from state.photo. Base64 must have no data URL prefix or line breaks. For JPEG, change both the filename and format to jpg or jpeg. Repeated references may expand input; reference a shared image once in state where possible. URLs and local paths are not image bytes, and automatic retrieval must not be assumed. Multipart, SDK uploads and compressed bodies remain unverified here.

The reference describes static PNG/JPEG, dimensions of 280–1568 pixels per side, a 16 MiB image limit and a 32 MiB body limit. These are not verified MonkeyAI limits or capacity promises. Confirm supported formats, dimensions and limits for the gateway and current alias. Correct orientation, resize or pad if necessary; Base64 adds roughly a third to byte size. This does not establish animation or video support.

Box is also unverified. A plaza listing for 1.14.0 establishes neither callable version support nor localization capability. After verification, a question would have the illustrative shape below; this is a fragment, not a standalone request or an instruction to enable it in production:

json
{"target": {"type": "box", "instructions": "Locate the shipping label in the referenced image."}}

Reference coordinates are normalized left/top/right/bottom, with the origin at the top left. For submitted width W and height H, the pixel box is (left×W, top×H, right×W, bottom×H). Round, clamp and map back after resizing or cropping. A box may be returned when the target is absent, so first assess presence with noul before accepting coordinates. A null confidence is not zero. Use explicit single-target questions for multiple objects. This explains the protocol without asserting live Box availability.

Large files and context boundaries

Prepare UTF-8 document.txt in the current directory. File mode in both languages reads its contents into state and asks whether an explicit renewal decision is required. A filename or URL alone is not read. Extract text or use OCR locally for PDF, office files and scans; do not decode arbitrary binary data as UTF-8.

The reference mentions a 262K-token limit for the whole request and for state plus one question. Confirm the context limit, gateway body limit and long context billing for MonkeyAI's current jev-latest; reference limits are not MonkeyAI capacity guarantees. Budget for state, questions and images; characters are not tokens. Split large documents into identified sections and aggregate in your application. Cross-section dependencies can affect decisions: absence from one segment does not establish absence from the entire document.

Usage and billing

Read usage.input_tokens and usage.output_tokens, then reconcile with live pricing and account usage records. TypeSafe lists token billing with zero output price; effective rates still depend on current group rules. If input-only billing is confirmed, estimate input tokens divided by 1,000,000 times the effective price per million input tokens. Rounded display prices are unsuitable for precise reconciliation. Do not promise no image surcharge or unchanged long context rates without confirmation. Images affect input usage; inspect records rather than guessing how failures or retries were charged.

Troubleshooting and readiness checks

SymptomCheck and action
401 / 403Check environment, complete key, expiry, balance and TypeSafe group; never print keys
404 / protocol errorUse /v1/systemone; Chat, Responses and Messages are not substitutes
model_not_found / rejected pinned versionUse only jev-latest currently; pinned versions are not guaranteed to be callable, so do not loop on a rejected version
400 / 422Validate nonempty questions, state, type, instructions and criteria; omit stream; check image encoding and references
413Reduce files or images, allow for Base64 expansion and gateway limits, and split requests
max_tokens_exceededReduce state, questions and images together; avoid duplicate references and confirm the actual context limit
429 / 503Reduce concurrency and apply bounded backoff following Retry-After when present; retain redacted request IDs for support
Timeout / non-JSON / missing answersMark failure, retain redacted status and content type, inspect the gateway response and never synthesize answers

Before use, confirm alias mapping, response fields, image and Box support, input limits, runtime compatibility and billing. A successful text request does not establish every capability.