JK
JustKalm
API v1Stable

Developer Documentation

Everything you need to integrate JustKalm into your applications. From authentication to streaming, we've got you covered with comprehensive guides and examples.

Quickstart

  1. Generate an API key in the console and keep it server-side.
  2. Call POST /api/v1/value-decision with a product URL.
  3. Display price fairness, resale, and circularity outputs.
  4. Use streaming (/stream-get) for real-time updates.
Base URL
https://api.justkalm.com/api/v1
Auth Header
Authorization: Bearer <API_KEY>

Client Metadata

Identify the surface to keep scoring versions aligned.

"client": {
  "id": "browser-extension-demo",
  "surface": "extension_overlay",
  "version": "1.0.0"
}
POST

Request Shape

POST /api/v1/value-decision
{
  "url": "https://retailer.com/product/123",
  "overrideProduct": {
    "brand": "Example Brand",
    "name": "Wool Coat",
    "category": "coat",
    "price": 249,
    "currency": "USD",
    "materials": [
      { "name": "wool", "percentage": 80 },
      { "name": "polyamide", "percentage": 20 }
    ]
  },
  "client": {
    "id": "browser-extension-demo",
    "surface": "extension_overlay",
    "version": "1.0.0"
  }
}
200

Response Highlights

HTTP/1.1 200 OK
{
  "productId": "exb-123",
  "priceFairnessScore": 78,
  "resalePotentialScore": 65,
  "circularity": {
    "circularImpactScore": 58,
    "materialLoopScore": 55,
    "durabilityScore": 80,
    "reparabilityScore": 40,
    "recyclabilityScore": 50,
    "takebackAvailable": true,
    "tokens": ["mixed_material_complex"]
  },
  "explanations": [
    "Price is slightly above typical...",
    "High wool content improves..."
  ],
  "generatedAt": "2025-11-29T16:00:00Z",
  "version": "0.2.0"
}

Use server-sent events to watch agent logs and capture the final result.

GET /api/value-decision/stream-get
  ?payload=<BASE64_PAYLOAD>
  &api_key=<API_KEY>

Events:
- heartbeat: { "ts": 1710000000 }
- log: { "message": "Parsing..." }
- result: { <ValueDecisionResponse> }
- failure: { "message": "..." }
  • • Close stream on `result` or `failure` events
  • • Retry with jitter on network disconnects
  • • Send `x-request-id` to correlate logs

cURL Example

curl -X POST \
  https://api.justkalm.com\
    /api/v1/value-decision \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer <KEY>" \
  -d '{
    "url": "https://retailer.com/123",
    "client": {
      "id": "demo",
      "surface": "app",
      "version": "1.0.0"
    }
  }'
  • • Check `X-RateLimit-*` headers
  • • Honor `Retry-After` on 429s

Node/TypeScript

const res = await fetch(
  "https://api.justkalm.com" +
    "/api/v1/value-decision",
  {
    method: "POST",
    headers: {
      "Content-Type": "application/json",
      Authorization: `Bearer ${KEY}`,
    },
    body: JSON.stringify({
      url: "https://retailer.com/123",
      client: { id: "demo" }
    })
  }
);

const data = await res.json();
console.log(data);
  • Keep `x-request-id` stable across POST and stream calls for the same user action.
  • Surface `X-Scorer-Version` in your logs to track scorer drift.
  • Honor `Retry-After` on 429s; add exponential jitter for 5xx/stream drops.

Browser/mobile streaming example

const payload = {
  url: "https://retailer.com/product/123",
  overrideProduct: { brand: "Example", price: 129, currency: "USD" },
  client: { id: "phia-demo", surface: "mobile_web", version: "1.0.0" }
};

// Encode payload for GET SSE convenience
const b64 = btoa(JSON.stringify(payload)).replace(/=+$/, "");
const reqId = crypto.randomUUID();

const es = new EventSource(
  "https://api.justkalm.com/api/v1/value-decision/stream-get?payload=" +
    b64 +
    "&api_key=" +
    encodeURIComponent('<API_KEY>'),
  { withCredentials: false }
);

const close = () => es.close();

es.addEventListener("heartbeat", (e) => {
  // connection is alive
});

es.addEventListener("log", (e) => {
  const data = JSON.parse(e.data);
  console.info("log", data.message, data.requestId);
});

es.addEventListener("result", (e) => {
  const data = JSON.parse(e.data);
  console.info("done", data);
  close();
});

es.addEventListener("failure", (e) => {
  console.error("failed", e.data);
  close();
});

es.onerror = () => {
  console.warn("stream error — retry with jitter");
  close();
  setTimeout(() => {
    // re-run with a fresh EventSource; reuse reqId if you want continuity
  }, 500 + Math.random() * 1500);
};
  • Heartbeat event fires first to keep mobile EventSource connections alive.
  • Reuse `x-request-id` across POST + stream when correlating UI actions and backend logs.
  • Apply jittered retries (500–1500ms) on network errors; obey `Retry-After` for 429s.

Retry helper (JS)

const sleep = (ms) => new Promise((r) => setTimeout(r, ms));

async function withRetry(fn, { max = 3, base = 400 } = {}) {
  let attempt = 0;
  while (true) {
    try {
      return await fn();
    } catch (err) {
      attempt += 1;
      if (attempt > max) throw err;
      const backoff = base * 2 ** (attempt - 1) + Math.random() * 150;
      await sleep(backoff);
    }
  }
}

// Usage: wrap fetch/stream setup
// await withRetry(() => fetch(url, opts));
  • Backoff with jitter; cap attempts (default 3) to avoid retry storms.
  • Pair with `Retry-After` on 429s and a fresh `x-request-id` per user action.
  • Use the same helper for stream re-connects and POST calls to align behavior across surfaces.

Postman collection

Import the ready-made collection to exercise POST and SSE GET with base64 payloads.

  • File: /app/documentation/postman.collection.json
  • Set collection vars: api_key and optionally base_url.
  • Pre-request script auto-generates payload_b64 and uuid for request-id.
  • Inspect headers: X-RateLimit-*, X-Scorer-Version, X-App-Env, and echoed x-request-id.
  • Health probes: use /healthz for liveness and /status for uptime + version/env (included in the collection).

Insomnia workspace

Import the Insomnia export for the same POST + SSE + status calls.

  • File: /app/documentation/insomnia.json
  • Set environment vars: api_key, optional base_url.
  • Payload is auto-encoded: edit payload_json and we base64 it via base64Encode; `uuid` helper available in Insomnia.
  • Includes POST + GET SSE calls and /status; check headers for rate limits, version/env, and request-id echo.
  • For correlation, pin a custom uuid value in the environment to reuse x-request-id across POST + stream.

Changelog

  • 0.2.0 — Added durability tokens, health explanations, streaming fixes.
  • 0.1.1 — Improved price fairness economist rationale; better error messages.
  • 0.1.0 — Initial release of price/resale/circularity scorers.

Error handling

  • 400 — Invalid URL or malformed payload.
  • 401 — Missing/invalid API key.
  • 429 — Back off and retry with exponential jitter.
  • 5xx — Retry with jitter; contact support if repeated.

SLOs

  • p99 latency: < 4.5s for cached retailers.
  • Availability: 99.5% monthly.
  • Streaming: first log < 800ms, final result < 6s typical.
  • Rate limits: enforced per-client (default 60 RPM); 429 returns `Retry-After: 60` plus headers `X-RateLimit-Limit`, `X-RateLimit-Remaining`, `X-RateLimit-Reset`.
  • Meta headers: `X-Scorer-Version` and `X-App-Env` included on all responses/streams for tracing and cache-busting.

Webhook notes

If you need async callbacks, use polling today; webhook delivery is on the roadmap. Reach out via Support to join the beta list.

Integration patterns

  • Server-to-server — recommended: keep API keys off the client, add request-level idempotency keys.
  • Browser extension — scope keys per client.id, throttle to avoid 429s, cache by URL.
  • AI agents/tools — pass rich `overrideProduct` to reduce network hops; stream for conversational latency.
fetch("https://api.justkalm.com/api/v1/value-decision", {
  method: "POST",
  headers: {
    "Content-Type": "application/json",
    Authorization: "Bearer undefined"
  },
  body: JSON.stringify(payload)
});

Observability & resilience

  • Send `x-request-id` (or `x-correlation-id`); we echo it in headers and SSE payloads for tracing.
  • Capture `generatedAt` and `version` to pin decisions to a scorer release.
  • Backoff: exponential jitter, max 3 retries for 429/5xx; avoid retry storms on stream timeouts.
  • Use status.justkalm.com for live uptime and maintenance windows.

Data & safety

  • No PHI/PII is required; send only product data.
  • Payloads are encrypted at rest; purge by client.id on request.
  • Health messaging is informational only; not a medical device or clinical diagnostic.
  • Chain-of-custody: `generatedAt` + `version` let you audit scorer lineage.

Scoring ingredients

  • Economist price bands + resale comparables.
  • Material science heuristics: mono-material vs blends, repairability hints, microfiber risk.
  • Circularity tokens: take-back, recycled content, durability proxies, recyclability pathways.
  • Health/comfort: dermal irritant signals (finishes, coatings) are flagged in explanations.

Environments

  • Sandbox: rate limits relaxed; scores may use cached mocks.
  • Production: full scoring graph; stricter limits.
  • Distinct API keys per environment; do not reuse.
  • For blockchain passport pilots: use sandbox chain IDs; rotate keys monthly.

AI usage notes

  • Use structured explanations: we return prefix + content so you can render headings safely.
  • Keep LLM prompts grounded by passing the raw response JSON plus `version` to the model.
  • Guardrails: cap user-supplied overrides (price > 0, materials max 20 lines) before forwarding.
  • Store the SSE transcript to improve post-incident debugging and evaluation.

Media/health guidance

  • Health-related text is for consumer education (e.g., finishes, coatings, allergens); avoid clinical claims.
  • When surfacing dermal or respiratory cautions, pair with source tokens (e.g., `pfas_free`, `low_voc`).
  • For broadcast/commerce surfaces, keep copy factual and avoid prescriptive language.
  • API keys hashed at rest; rotate every 90 days.
  • Payload retention: 30 days (sandbox), 90 days (prod) unless purge requested.
  • Threat model focus: auth replay, prompt injection via overrides, webhook forgery (when enabled).
  • Supply chain: npm + Python deps pinned; SCA weekly; SBOM available on request.

Responsible AI & UX

  • Keep explanations concise; avoid anthropomorphic language in UI copy.
  • Detect and redact user-supplied PII/PHI in overrides before sending to the API.
  • Label AI-generated content and show `version` so users know the scorer release.
  • Offer a “Why?”/“Disagree?” affordance to collect corrections for continuous tuning.

Performance guide

  • Retailers with cached signals: p95 ~3s, p99 ~4.5s; streaming first log ~0.8s.
  • Cold starts (new domains): add 1–2s for scrape/normalization.
  • Batching: prefer up to 20 URLs per minute per client.id by default.
  • Profile heavy pages: reduce unnecessary query params to cut parser time.

Benchmarking tips

  • Log `version` with outcomes (CTR, resale lifts) to attribute scorer changes.
  • Segment by category and brand tier; price bands differ for luxury vs value.
  • Track “agree/disagree” user taps to refine explanations and tokens.

Glossary (tokens)

  • brand_takeback_program: verified end-of-life loop.
  • mixed_material_complex: multi-fiber blend hard to recycle.
  • pfas_free: no perfluorinated finishes detected in copy.
  • high_durability_design: construction cues implying longevity.

Rollout playbook

  • Stage to sandbox, validate against your golden set, then promote with canary (5–10% traffic) before full cutover.
  • Pin scorer `version` per release; only flip to latest after your acceptance run passes.
  • Record baseline metrics (CTR, add-to-cart, returns, resale lift) before enabling new explainers/tokens.

Accessibility & UX

  • Ensure WCAG AA contrast on score badges; avoid color-only distinctions.
  • Expose tooltip copy as plain text for screen readers; avoid motion-only cues.
  • Keep health/circularity language plain and non-alarmist to reduce undue anxiety.