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
- Generate an API key in the console and keep it server-side.
- Call
POST /api/v1/value-decisionwith a product URL. - Display price fairness, resale, and circularity outputs.
- 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_keyand optionallybase_url. - Pre-request script auto-generates
payload_b64anduuidfor request-id. - Inspect headers:
X-RateLimit-*,X-Scorer-Version,X-App-Env, and echoedx-request-id. - Health probes: use
/healthzfor liveness and/statusfor 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, optionalbase_url. - Payload is auto-encoded: edit
payload_jsonand we base64 it viabase64Encode; `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
uuidvalue in the environment to reusex-request-idacross 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.