KRANTH API — Plain-text reference for agents and SDK callers curl https://kranth.ai/docs.txt ================================================================ VERSION 0.1.0 BASE URL https://api.kranth.ai SPEC https://kranth.ai/docs (interactive) OPENAPI https://kranth.ai/openapi.yaml ---------------------------------------------------------------- WHAT IS KRANTH ---------------------------------------------------------------- Synthetic-audience simulation engine. POST an idea, a piece of copy, a product concept, or a policy proposal. The engine spins up 10–5000 adversarial AI personas with distinct archetypes, biases, and worldviews. Each persona reads your idea independently and reacts. You get back a live stream of reactions plus a final verdict: - overall sentiment (−1.0 … +1.0) - breakdown: hostile / neutral / aligned - top objections - emergent themes - representative quotes Use it to stress-test ideas before strangers do. ---------------------------------------------------------------- AUTHENTICATION ---------------------------------------------------------------- Pass a bearer token in every authenticated request: Authorization: Bearer Two token types are accepted on all authenticated endpoints: kr_live_… Long-lived API keys. Mint at /app/account or via POST /v1/api-keys. Store immediately — the full key is only returned once (we hash it with argon2id). Nexus JWT Issued by accounts.vylth.com SSO. Used by the web dashboard. Works on all API routes. Endpoints marked [PUBLIC] require no auth. ---------------------------------------------------------------- CREDITS ---------------------------------------------------------------- Simulations cost credits. 1 credit ≈ $0.05 of compute. Credits are deducted at POST /v1/sims time based on: persona_count × model tier rate Model tier rates (credits per 100 personas): haiku 2 sonnet 5 opus 15 gpt-4o-mini 3 gpt-4o 8 gemini-flash 2 gemini-pro 8 llama-70b 2 deepseek-v3 2 qwen-72b 2 Monthly plan credits reset on your billing anniversary. Top-up packs don't expire. Plan credits are consumed first. ---------------------------------------------------------------- ERRORS ---------------------------------------------------------------- Every error response is JSON: {"error": "", "message": ""} HTTP status conventions: 401 Missing or invalid bearer token 402 Out of credits — top up or upgrade 403 Forbidden (not an owner for owner-only routes) 404 Not found, or belongs to a different org (we don't distinguish) 422 Request body validation failed 429 Rate limited — check Retry-After header (seconds) 5xx Server fault — paste the x-request-id header into support@kranth.ai ---------------------------------------------------------------- RATE LIMITS ---------------------------------------------------------------- POST /v1/sims per-org, tier-dependent (see your plan) All other endpoints uncapped On overflow: HTTP 429 + Retry-After: ---------------------------------------------------------------- ENDPOINTS ---------------------------------------------------------------- ════════════════════════════════════════════════════════════════ IDENTITY ════════════════════════════════════════════════════════════════ GET /v1/health [PUBLIC] Liveness probe. Returns 200 if the service is up. curl https://api.kranth.ai/v1/health Response 200: {"status": "ok"} ──────────────────────────────────────────────────────────────── GET /v1/me Returns the resolved identity for the current token. curl https://api.kranth.ai/v1/me \ -H "Authorization: Bearer kr_live_..." Response 200: { "user_id": "uuid", "org_id": "uuid", "auth_source": "api_key" | "nexus" } Errors: 401 ════════════════════════════════════════════════════════════════ MODELS ════════════════════════════════════════════════════════════════ GET /v1/models [PUBLIC] Lists every LLM the engine can dispatch to. curl https://api.kranth.ai/v1/models Response 200: { "models": [ { "id": "sonnet-4-6", "tier": "sonnet", "display_name": "Claude Sonnet 4.6", "provider": "anthropic", "credits_per_100_personas": 5, "plan_min": "starter" }, ... ] } Current model IDs: haiku-4-5 Claude Haiku 4.5 (anthropic) sonnet-4-6 Claude Sonnet 4.6 (anthropic) opus-4-7 Claude Opus 4.7 (anthropic) gpt-4o-mini GPT-4o mini (openai) gpt-4o GPT-4o (openai) gemini-2-5-flash Gemini 2.5 Flash (google) gemini-2-5-pro Gemini 2.5 Pro (google) llama-3.3-70b Llama 3.3 70B (meta/together) deepseek-v3 DeepSeek V3 (deepseek/together) qwen-3-72b Qwen 3 72B (alibaba/together) ════════════════════════════════════════════════════════════════ SIMS ════════════════════════════════════════════════════════════════ Scoring Each persona reacts independently and returns a sentiment in [-1.0, +1.0] (-1.0 = maximally hostile, +1.0 = fully aligned). avg_sentiment is a weighted average across all personas in the sim — hostile reactions (<= -0.3) count 1.5x, since real objections carry more decision weight than polite enthusiasm. Convert to the 0-100 display score used in the dashboard with (avg_sentiment + 1) * 50 — 50 is neutral. This is a synthetic signal from an adversarial audience, not a prediction of real-world reception. POST /v1/sims Submit a simulation. Returns immediately with a sim_id. Poll GET /v1/sims/{id} or stream GET /v1/sims/{id}/events. Request body (JSON): idea_text string required The idea to stress-test. Max 10,000 chars. persona_count integer required Number of personas to spin up. Range: 1–5000. model_id string required LLM to use. Fetch valid IDs from GET /v1/models. training_opt_in boolean optional Allow Kranth to train on this sim's reactions. Default: false. bias_axes string[] optional Archetype hints that weight persona sampling toward matching archetypes, e.g. ["cfo","finance"]. confidence_mode boolean optional Resample each verdict 3x for a per-persona confidence score + a sim-level score band. Charges 3x credits. Default: false. public boolean optional Publish the sim. When true and the sim completes, it is readable without auth at https://kranth.ai/s/{id} and public_url is returned. Toggle later via PATCH /v1/sims/{id}. Default: false. redact_input boolean optional Hide the brief (idea_text) on the public page and OG card so the verdict can be shared without exposing the prompt. Default: false. curl -X POST https://api.kranth.ai/v1/sims \ -H "Authorization: Bearer kr_live_..." \ -H "Content-Type: application/json" \ -d '{ "idea_text": "A subscription gym app for remote workers", "persona_count": 50, "model_id": "sonnet-4-6", "confidence_mode": true, "public": true }' Response 202: { "sim_id": "uuid", "status": "queued", "model_tier": "sonnet", "credits_charged": 3, "persona_count": 50, "public_url": null // https://kranth.ai/s/{id} once public AND complete } Errors: 401, 402, 422, 429 ──────────────────────────────────────────────────────────────── GET /v1/sims List your simulations, newest first. Paginated. Query parameters: status string filter by queued|running|complete|failed|canceled cursor string ISO 8601 datetime — return sims older than this limit integer default 50, max 200 curl "https://api.kranth.ai/v1/sims?status=complete&limit=20" \ -H "Authorization: Bearer kr_live_..." Response 200: { "sims": [ , ... ], "next_cursor": "2026-05-10T12:00:00Z" // null if last page } Errors: 401 ──────────────────────────────────────────────────────────────── GET /v1/sims/{id} Read one simulation. curl https://api.kranth.ai/v1/sims/SIM_UUID \ -H "Authorization: Bearer kr_live_..." Response 200 — Sim object: { "id": "uuid", "org_id": "uuid", "created_by": "uuid" | null, "status": "queued" | "running" | "complete" | "failed" | "canceled", "idea_text": "...", "persona_count": 50, "personas_done": 50, "model_tier": "sonnet", "credits_charged": 3, "avg_sentiment": 0.42, // null while running "error_message": null, "started_at": "2026-05-22T09:00:00Z", "completed_at": "2026-05-22T09:01:14Z", "created_at": "2026-05-22T09:00:00Z", "public": false, "redact_input": false, "public_url": null // https://kranth.ai/s/{id} once public AND complete } Errors: 401, 404 ──────────────────────────────────────────────────────────────── PATCH /v1/sims/{id} Publish / unpublish a sim, or toggle brief redaction. Body fields are optional; omit one to leave it unchanged. Returns the updated Sim object. Request body (JSON): public boolean optional Make the sim publicly readable (or not). redact_input boolean optional Hide the brief on the public page + OG card. curl -X PATCH https://api.kranth.ai/v1/sims/SIM_UUID \ -H "Authorization: Bearer kr_live_..." \ -H "Content-Type: application/json" \ -d '{ "public": true, "redact_input": true }' Errors: 401, 404 ──────────────────────────────────────────────────────────────── POST /v1/sims/{id}/cancel Cancel a queued or running sim. A partial-run credit refund is posted if applicable. curl -X POST https://api.kranth.ai/v1/sims/SIM_UUID/cancel \ -H "Authorization: Bearer kr_live_..." Response 200: {"status": "canceled"} Errors: 401, 404 ──────────────────────────────────────────────────────────────── GET /v1/sims/{id}/export Download the full simulation result as JSON. Includes the sim row, all reactions, the verdict, and top quotes. Only available for sims with status=complete. curl https://api.kranth.ai/v1/sims/SIM_UUID/export \ -H "Authorization: Bearer kr_live_..." Response 200: application/json — full export object Errors: 401, 404 ──────────────────────────────────────────────────────────────── POST /v1/sims/{id}/stream-token Mints a short-lived HMAC-signed token for the SSE stream. Required because EventSource in browsers can't set headers. TTL: 5 minutes. curl -X POST https://api.kranth.ai/v1/sims/SIM_UUID/stream-token \ -H "Authorization: Bearer kr_live_..." Response 200: { "token": "eyJ...", "expires_in": 300 } Errors: 401, 404 ──────────────────────────────────────────────────────────────── GET /v1/sims/{id}/events [PUBLIC — token required in query] Server-Sent Events stream. Real-time persona reactions. Pass the token from POST /v1/sims/{id}/stream-token as ?token=. A 15-second heartbeat comment keeps proxies alive. Typical flow: 1. POST /v1/sims/{id}/stream-token → get token 2. GET /v1/sims/{id}/events?token=TOKEN → open EventSource curl "https://api.kranth.ai/v1/sims/SIM_UUID/events?token=TOKEN" SSE event types: persona.ready {"persona_id":"uuid","name":"...","archetype":"...","bias_axes":{...}} reaction.partial {"persona_id":"uuid","text":"...","chunk_index":0} reaction.complete {"persona_id":"uuid","sentiment":0.8,"stance":"aligned","full_text":"..."} cluster.formed {"cluster_id":"uuid","label":"...","size":12,"avg_sentiment":0.3} sim.complete { "sim_id": "uuid", "avg_sentiment": 0.42, "hostile_pct": 18, "neutral_pct": 40, "aligned_pct": 42, "top_objections": ["...", "..."], "themes": ["...", "..."], "quotes": [{"persona":"...","text":"...","sentiment":0.9}] } sim.failed {"sim_id":"uuid","error":"..."} Errors: 400 (bad token), 404 ════════════════════════════════════════════════════════════════ DEBATES ════════════════════════════════════════════════════════════════ Adversarial debate mode. POST a topic; personas argue it live in one of five formats, a judge synthesizes the verdict, and (optionally) a Cartesia voice track renders each turn. Starter plan and above. Modes 1v1 2 personas, strict alternating turns (12 turns) panel 3-6 analysts deliberating (18 turns) town-hall alias of panel today press 1 defender vs up to 5 reporters (20 turns) pre-mortem 2-6 personas explain why the idea already failed (12 turns) team-debate 6-15 personas: Blue (for) vs Red (against) plus a neutral scoring audience; turns scale with team size (12-30 turns) Scoring When a debate completes, a judge model reads the full transcript and returns a verdict on the same 0-100 scale as sims (50 = dead neutral, higher = the idea won the room). The judging directive is per-mode: 1v1 is arbitrated on logic/evidence/rebuttals; panel on where panelists ended up; press on whether hostile questions were genuinely answered; pre-mortem on survival likelihood given the failure modes raised; team-debate on the neutral audience's net alignment shift. The verdict also carries a room split (pct_for / pct_against / pct_undecided), verdict_reasons, the decisive argument, and the top unrebutted concern. team-debate additionally reports blue_score / red_score — the cumulative audience alignment for each side, sampled after every turn. Like sims, this is a synthetic signal, not a prediction. Credits: 8 credits per turn, reserved for the mode's max turns at create time; unspent turns are refunded when the debate ends early. POST /v1/debates Start a debate. Returns immediately with the debate row (status=queued). Request body (JSON): topic string required What the room argues about. mode string required 1v1 | panel | town-hall | press | pre-mortem | team-debate participants integer optional Speaker count (mode-clamped; see Modes). model_id string optional Default claude-sonnet-4-6. Debates require medium-or-better models. archetype_slugs string[] optional Exact archetypes to seat as debaters (custom persona slugs resolve too). voice_enabled boolean optional Render Cartesia voice per turn. public boolean optional Publish at https://kranth.ai/d/{id}. training_opt_in boolean optional Override the org's training setting. GET /v1/debates List the org's debates. GET /v1/debates/{id} One debate + synthesis (verdict) once complete. GET /v1/debates/{id}/turns Full transcript (role, body, audio_url). GET /v1/debates/{id}/export Download transcript + verdict as JSON. POST /v1/debates/{id}/cancel End the room early; partial turns refund. PATCH /v1/debates/{id} Toggle `public`. POST /v1/debates/{id}/stream-token + GET /v1/debates/{id}/events Same SSE pattern as sims: text fragments per turn, turn.ready, audience.score (team-debate cumulative blue/red), debate.end. ════════════════════════════════════════════════════════════════ RECON ════════════════════════════════════════════════════════════════ Web-grounded research mode. POST an idea; the engine dispatches a swarm of research agents that search the web, fetch sources, cross-verify claims, and synthesize a cited market-reality report. Returns grounded findings (each tagged supported / neutral / contradicted), a competitor landscape, top risks, and a branded downloadable PDF. Credits are deducted at POST /v1/recon based on tier: quick 25 credits (default models) standard 50 credits deep 80 credits Cost scales if you override worker_model or synth_model. Scoring Research agents extract atomic findings, each cited to the web sources that back it. Synthesis grades every finding against ITS OWN sources (supported / neutral-unverifiable / contradicted), then a skeptical judge scores how sound the idea is on the same 0-100 scale (higher = the evidence validates demand and feasibility; 50 = genuinely mixed or insufficient evidence). Zero verifiable findings returns 50 with an explicit insufficient-evidence verdict, never a confident score. The score is only as good as what the public web knows — treat it as due diligence, not a prediction. ──────────────────────────────────────────────────────────────── POST /v1/recon Submit a recon run. Returns immediately with a recon_id. Poll GET /v1/recon/{id} or stream GET /v1/recon/{id}/events. Request body (JSON): idea_text string required The idea or question to research. Max 10,000 chars. tier string required quick | standard | deep audience string optional founder | researcher | agency company_url string optional Your company URL — anchors competitor discovery. worker_model string optional Override the research-agent LLM. synth_model string optional Override the synthesis LLM. curl -X POST https://api.kranth.ai/v1/recon \ -H "Authorization: Bearer kr_live_..." \ -H "Content-Type: application/json" \ -d '{ "idea_text": "A B2B SaaS tool for async video code review", "tier": "standard" }' Response 202: { "recon_id": "uuid", "status": "queued", "tier": "standard", "credits_charged": 50 } Errors: 401, 402, 422, 429 ──────────────────────────────────────────────────────────────── GET /v1/recon List your recon runs, newest first. curl https://api.kranth.ai/v1/recon \ -H "Authorization: Bearer kr_live_..." Response 200: { "recons": [ { "id": "uuid", "status": "complete", "idea_text": "...", "tier": "standard", "score": 72, "credits_charged": 50, "created_at": "2026-06-20T09:00:00Z", "completed_at": "2026-06-20T09:04:31Z", "supported": 14, "contradicted": 3, "neutral": 8, "source_domains": ["techcrunch.com", "crunchbase.com"] } ] } Errors: 401 ──────────────────────────────────────────────────────────────── GET /v1/recon/tiers Tier catalog — slugs, labels, worker counts, and credit prices. curl https://api.kranth.ai/v1/recon/tiers Response 200: [ {"slug": "quick", "label": "Quick", "worker_count": 3, "price_credits": 25}, {"slug": "standard", "label": "Standard", "worker_count": 6, "price_credits": 50}, {"slug": "deep", "label": "Deep", "worker_count": 12, "price_credits": 80} ] [PUBLIC] — no auth required. ──────────────────────────────────────────────────────────────── GET /v1/recon/{id} Read one recon run, including findings, sources, and the synthesized report. curl https://api.kranth.ai/v1/recon/RECON_UUID \ -H "Authorization: Bearer kr_live_..." Response 200 — run detail object: { "run": { "id": "uuid", "status": "complete", "idea_text": "...", "tier": "standard", "score": 72, "credits_charged": 50, "created_at": "2026-06-20T09:00:00Z", "completed_at": "2026-06-20T09:04:31Z", "supported": 14, "contradicted": 3, "neutral": 8, "source_domains": ["techcrunch.com"], "report": { "summary": "...", "themes": ["Market growing 40% YoY", "..."], "top_risks": ["No clear monetisation precedent", "..."], "competitors": [ { "name": "Acme Reviews", "what": "Async code review platform", "angle": "Enterprise DevOps", "metric": "$4M ARR", "url": "https://acmereviews.com", "business_model": "SaaS subscription", "valuation": "$30M", "revenue": "$4M", "employees": "28" } ] } }, "findings": [ { "id": "uuid", "agent_role": "market-sizing", "claim": "Developer tools market is $28B in 2026.", "grounding": 1 } ], "sources": [ { "finding_id": "uuid", "url": "https://techcrunch.com/...", "title": "Developer tools market forecast", "snippet": "..." } ] } grounding values: 1 = supported · 0 = neutral · -1 = contradicted Errors: 401, 404 ──────────────────────────────────────────────────────────────── GET /v1/recon/{id}/export Download the branded PDF report. Only available when status = complete. curl https://api.kranth.ai/v1/recon/RECON_UUID/export \ -H "Authorization: Bearer kr_live_..." \ -o recon-report.pdf Response 200: application/pdf — branded Kranth report Errors: 401, 404 ──────────────────────────────────────────────────────────────── POST /v1/recon/{id}/stream-token Mints a short-lived HMAC-signed token for the SSE event stream. TTL: 5 minutes. curl -X POST https://api.kranth.ai/v1/recon/RECON_UUID/stream-token \ -H "Authorization: Bearer kr_live_..." Response 200: { "token": "eyJ...", "expires_in": 300 } Errors: 401, 404 ──────────────────────────────────────────────────────────────── GET /v1/recon/{id}/events [PUBLIC — token required in query] Server-Sent Events stream. Real-time recon progress. Pass the token from POST /v1/recon/{id}/stream-token as ?token=. A 15-second heartbeat comment keeps proxies alive. Typical flow: 1. POST /v1/recon/{id}/stream-token → get token 2. GET /v1/recon/{id}/events?token=TOKEN → open EventSource curl "https://api.kranth.ai/v1/recon/RECON_UUID/events?token=TOKEN" SSE event types: recon.started {"recon_id":"uuid","tier":"standard"} agent.started {"recon_id":"uuid","agent_role":"market-sizing"} agent.complete {"recon_id":"uuid","agent_role":"market-sizing","findings_count":5} synthesizing {"recon_id":"uuid"} recon.complete {"recon_id":"uuid","score":72,"supported":14,"contradicted":3,"neutral":8} recon.failed {"recon_id":"uuid","error":"..."} Errors: 400 (bad token), 404 ---------------------------------------------------------------- QUICK START: RUN A RECON END-TO-END ---------------------------------------------------------------- # 1. Submit the recon RECON=$(curl -s -X POST https://api.kranth.ai/v1/recon \ -H "Authorization: Bearer kr_live_..." \ -H "Content-Type: application/json" \ -d '{"idea_text":"B2B async code review SaaS at $49/seat/mo","tier":"quick"}') RECON_ID=$(echo $RECON | jq -r .recon_id) echo "Recon ID: $RECON_ID" # 2. Mint a stream token TOKEN=$(curl -s -X POST https://api.kranth.ai/v1/recon/$RECON_ID/stream-token \ -H "Authorization: Bearer kr_live_..." | jq -r .token) # 3. Stream live progress curl -N "https://api.kranth.ai/v1/recon/$RECON_ID/events?token=$TOKEN" # 4. Or poll for completion curl https://api.kranth.ai/v1/recon/$RECON_ID \ -H "Authorization: Bearer kr_live_..." | jq .run.score # 5. Download the PDF report curl https://api.kranth.ai/v1/recon/$RECON_ID/export \ -H "Authorization: Bearer kr_live_..." \ -o recon-report.pdf ════════════════════════════════════════════════════════════════ API KEYS ════════════════════════════════════════════════════════════════ POST /v1/api-keys Mint a new long-lived API key. The full key (kr_live_…) is returned ONCE. We only store the argon2id hash. If you lose it, revoke and mint a new one. Request body: name string required Human label, max 80 chars. curl -X POST https://api.kranth.ai/v1/api-keys \ -H "Authorization: Bearer kr_live_..." \ -H "Content-Type: application/json" \ -d '{"name": "prod-worker"}' Response 200: { "id": "uuid", "name": "prod-worker", "prefix": "kr_live_abc1", "key": "kr_live_abc1xxxxxxxxxxxxxxxxxxxxxxxxxxxx", "created_at": "2026-05-22T09:00:00Z" } Errors: 401 ──────────────────────────────────────────────────────────────── GET /v1/api-keys List all keys for this workspace. Secret values are NOT returned. curl https://api.kranth.ai/v1/api-keys \ -H "Authorization: Bearer kr_live_..." Response 200: { "keys": [ { "id": "uuid", "name": "prod-worker", "prefix": "kr_live_abc1", "last_used_at": "2026-05-22T09:00:00Z", "revoked_at": null, "created_at": "2026-05-01T00:00:00Z" } ] } ──────────────────────────────────────────────────────────────── DELETE /v1/api-keys/{id} Revoke a key immediately. Any inflight requests using it will start failing within seconds. curl -X DELETE https://api.kranth.ai/v1/api-keys/KEY_UUID \ -H "Authorization: Bearer kr_live_..." Response 204: (empty) Errors: 401, 404 ════════════════════════════════════════════════════════════════ WEBHOOKS ════════════════════════════════════════════════════════════════ Outgoing HTTP callbacks. Register an endpoint URL and a list of events; Kranth POSTs a signed JSON payload to it whenever one of those events fires in your workspace. Requires the Starter plan or higher. Signing Every delivery carries three headers: x-kranth-event e.g. "sim.complete" x-kranth-timestamp unix seconds at send time x-kranth-signature "v0=" user-agent "kranth-webhook/1.0" The signature is HMAC-SHA256, keyed with the endpoint's signing secret (returned once at creation — see POST /v1/webhooks below), computed over: v0:{timestamp}:{raw request body} Verify it like this: import hmac, hashlib, time def verify(signing_secret: str, timestamp: str, raw_body: bytes, signature_header: str) -> bool: if abs(time.time() - int(timestamp)) > 300: return False # reject replays mac = hmac.new( signing_secret.encode(), f"v0:{timestamp}:".encode() + raw_body, hashlib.sha256, ) expected = mac.hexdigest() got = signature_header.removeprefix("v0=") return hmac.compare_digest(expected, got) Always compare against the raw request body (before any JSON re-serialization) and reject if the timestamp is more than 300s from now. Delivery + retries 8s timeout per attempt. On non-2xx response or timeout, retries fire at 5s, 30s, then 300s (up to 3 retries, ~5.6 min total per event). One delivery row is recorded per terminal attempt. After 25 consecutive failed deliveries the endpoint is automatically set active=false — re-enable it with PATCH /v1/webhooks/{id}. Events sim.complete A sim finished. {"event","sim_id","org_id","status","avg_sentiment","persona_count","credits_charged","completed_at"} sim.failed A sim hit a terminal error. Same shape as sim.complete. sim.canceled A running sim was canceled. Same shape as sim.complete. debate.complete A debate finished. {"event","debate_id","org_id","topic","mode","score","completed_at"} recon.complete A recon run finished. {"event","recon_id","org_id","status","score","credits_charged","completed_at"} recon.failed A recon run errored. Same shape as recon.complete. plan.change Subscription plan changed (up or down). {"event","org_id","plan"} credits.topup A top-up purchase landed in the ledger. {"event","org_id","credits"} credits.low Org dropped below 20% of its monthly cap. Throttled to once per 24h. {"event","org_id","credits_remaining","monthly_cap"} member.invite A teammate was invited to the org. {"event","org_id","email","role"} apikey.revoke An API key was revoked by anyone in the org. {"event","org_id","api_key_id"} Unknown event names are rejected at create/update time with 422. POST /v1/webhooks Register an endpoint. Requires Starter plan or higher. Request body (JSON): url string required Must start with http(s)://. Rejects localhost / private / link-local / cloud-metadata targets (SSRF guard). events string[] optional Event names to subscribe to (see Events above). Default: []. curl -X POST https://api.kranth.ai/v1/webhooks \ -H "Authorization: Bearer kr_live_..." \ -H "Content-Type: application/json" \ -d '{ "url": "https://api.yourapp.com/hooks/kranth", "events": ["sim.complete", "debate.complete"] }' Response 201 — Webhook object + one-time secret: { "id": "uuid", "url": "https://api.yourapp.com/hooks/kranth", "events": ["sim.complete", "debate.complete"], "active": true, "last_delivered_at": null, "last_response_code": null, "created_at": "2026-07-02T09:00:00Z", "success_count": 0, "fail_count": 0, "avg_response_ms": 0, "daily_deliveries": [0, 0, "...", 0], // 14 slots, not yet backfilled "recent": [], "signing_secret": "..." // returned ONCE — store it now } Errors: 401, 402 (below Starter), 422 ──────────────────────────────────────────────────────────────── GET /v1/webhooks List all endpoints for this workspace, newest first. signing_secret is never included in list/read responses. curl https://api.kranth.ai/v1/webhooks \ -H "Authorization: Bearer kr_live_..." Response 200: {"webhooks": [ , ... ]} // same shape as above, minus signing_secret Errors: 401 ──────────────────────────────────────────────────────────────── PATCH /v1/webhooks/{id} Update the event subscription list, or pause/resume delivery. Body fields are optional; omit one to leave it unchanged. Returns the updated Webhook object. Request body (JSON): active boolean optional false pauses delivery. events string[] optional Replaces the full subscription list. curl -X PATCH https://api.kranth.ai/v1/webhooks/WEBHOOK_UUID \ -H "Authorization: Bearer kr_live_..." \ -H "Content-Type: application/json" \ -d '{"active": false}' Errors: 401, 404, 422 ──────────────────────────────────────────────────────────────── DELETE /v1/webhooks/{id} Remove the endpoint. Delivery history is not retained after deletion. curl -X DELETE https://api.kranth.ai/v1/webhooks/WEBHOOK_UUID \ -H "Authorization: Bearer kr_live_..." Response 204: (empty) Errors: 401, 404 ════════════════════════════════════════════════════════════════ SETTINGS ════════════════════════════════════════════════════════════════ GET /v1/settings Workspace settings: name, slug, plan, training opt-in default. curl https://api.kranth.ai/v1/settings \ -H "Authorization: Bearer kr_live_..." Response 200: { "name": "Acme Corp", "slug": "acme-corp", "training_opt_in": false, "plan_slug": "starter" } ──────────────────────────────────────────────────────────────── PATCH /v1/settings Update workspace name and/or training opt-in default. Request body (all optional): name string New workspace display name. Max 80 chars. training_opt_in boolean Default opt-in for new simulations. curl -X PATCH https://api.kranth.ai/v1/settings \ -H "Authorization: Bearer kr_live_..." \ -H "Content-Type: application/json" \ -d '{"training_opt_in": true}' Response 200: updated Settings object (same shape as GET /v1/settings) Errors: 401, 422 ════════════════════════════════════════════════════════════════ BILLING ════════════════════════════════════════════════════════════════ GET /v1/usage Credit usage for the current billing period. curl https://api.kranth.ai/v1/usage \ -H "Authorization: Bearer kr_live_..." Response 200: { "plan": "starter", "monthly_cap": 300, "credits_consumed": 142, "credits_remaining": 158, "period_start": "2026-05-01T00:00:00Z" } Errors: 401 ──────────────────────────────────────────────────────────────── POST /v1/billing/checkout Start a Stripe Checkout session to upgrade your plan. Returns a URL to redirect your user to. Request body: plan string required starter | pro | scale billing_period string optional monthly | annual (default: monthly) success_url string optional Redirect after success. Defaults to /app/billing. cancel_url string optional Redirect on cancel. Defaults to /app/billing. curl -X POST https://api.kranth.ai/v1/billing/checkout \ -H "Authorization: Bearer kr_live_..." \ -H "Content-Type: application/json" \ -d '{"plan": "pro", "billing_period": "annual"}' Response 200: {"url": "https://checkout.stripe.com/..."} Errors: 401, 422 ──────────────────────────────────────────────────────────────── POST /v1/billing/portal Mint a Stripe Customer Portal URL. Use this to let users manage their subscription, update payment methods, view invoices, or cancel. curl -X POST https://api.kranth.ai/v1/billing/portal \ -H "Authorization: Bearer kr_live_..." Response 200: {"url": "https://billing.stripe.com/session/..."} Errors: 401 ──────────────────────────────────────────────────────────────── POST /v1/billing/topup Buy a one-shot credit pack via Stripe Checkout. Top-up credits never expire and stack on top of plan credits. Plan credits are consumed first. Request body: credits integer required 500 | 2000 | 10000 curl -X POST https://api.kranth.ai/v1/billing/topup \ -H "Authorization: Bearer kr_live_..." \ -H "Content-Type: application/json" \ -d '{"credits": 2000}' Response 200: {"url": "https://checkout.stripe.com/..."} Errors: 401, 422 ════════════════════════════════════════════════════════════════ ACCOUNT ════════════════════════════════════════════════════════════════ DELETE /v1/account Delete your user account. Cascades deletion of any workspace you solely own. Refuses while you own a workspace on a non-free plan — cancel via POST /v1/billing/portal first. Request body: confirm string required Must be exactly: "delete my account" curl -X DELETE https://api.kranth.ai/v1/account \ -H "Authorization: Bearer kr_live_..." \ -H "Content-Type: application/json" \ -d '{"confirm": "delete my account"}' Response 204: (empty) Errors: 401, 422 ──────────────────────────────────────────────────────────────── DELETE /v1/orgs/me Delete this workspace and all its data. Owner-only. Refuses while plan ≠ free. Pass the workspace slug to confirm — typos won't nuke you. Request body: confirm string required Your workspace slug (e.g. "acme-corp") curl -X DELETE https://api.kranth.ai/v1/orgs/me \ -H "Authorization: Bearer kr_live_..." \ -H "Content-Type: application/json" \ -d '{"confirm": "acme-corp"}' Response 204: (empty) Errors: 401, 403, 422 ════════════════════════════════════════════════════════════════ WAITLIST ════════════════════════════════════════════════════════════════ POST /v1/waitlist [PUBLIC] Join the launch waitlist. Idempotent — submitting the same email twice is a no-op. Request body: email string required utm_source string optional utm_medium string optional utm_campaign string optional curl -X POST https://api.kranth.ai/v1/waitlist \ -H "Content-Type: application/json" \ -d '{"email": "you@example.com"}' Response 204: (empty) Errors: 422, 429 ---------------------------------------------------------------- QUICK START: RUN A SIM END-TO-END ---------------------------------------------------------------- # 1. Submit the sim SIM=$(curl -s -X POST https://api.kranth.ai/v1/sims \ -H "Authorization: Bearer kr_live_..." \ -H "Content-Type: application/json" \ -d '{"idea_text":"Charging $1/day for a focus-mode app","persona_count":25,"model_id":"haiku-4-5"}') SIM_ID=$(echo $SIM | jq -r .sim_id) echo "Sim ID: $SIM_ID" # 2. Mint a stream token TOKEN=$(curl -s -X POST https://api.kranth.ai/v1/sims/$SIM_ID/stream-token \ -H "Authorization: Bearer kr_live_..." | jq -r .token) # 3. Stream reactions live curl -N "https://api.kranth.ai/v1/sims/$SIM_ID/events?token=$TOKEN" # 4. Or just poll for completion curl https://api.kranth.ai/v1/sims/$SIM_ID \ -H "Authorization: Bearer kr_live_..." | jq .avg_sentiment # 5. Export the full result curl https://api.kranth.ai/v1/sims/$SIM_ID/export \ -H "Authorization: Bearer kr_live_..." > sim-result.json ---------------------------------------------------------------- SDKS ---------------------------------------------------------------- Python (pip install kranth): https://github.com/VYLTH/kranth/tree/main/sdks/python TypeScript / Node (npm install @kranth/sdk): https://github.com/VYLTH/kranth/tree/main/sdks/typescript Go (go get github.com/VYLTH/kranth/sdks/go): https://github.com/VYLTH/kranth/tree/main/sdks/go Rust (crates.io: kranth): https://github.com/VYLTH/kranth/tree/main/sdks/rust ---------------------------------------------------------------- SUPPORT ---------------------------------------------------------------- Docs: https://kranth.ai/docs OpenAPI: https://kranth.ai/openapi.yaml Email: support@kranth.ai Status: https://status.kranth.ai GitHub: https://github.com/VYLTH/kranth (issues) ----------------------------------------------------------------