Developer API
Call Rigor from your code
Production-quality deliverables from a single API call. Rigor classifies your task, builds a methodology-driven plan, and executes each step — returning a structured deliverable with sources, quality score, and cost breakdown. Base URL: https://plith.ai
The Plith Rigor API classifies tasks across 60+ types, composes methodology sequences from 60+ proven frameworks, applies model-adaptive routing at each step, and returns quality-scored outputs with automated review and revision loops. REST API: JSON in, JSON or SSE out. Plan calls are free; execute calls consume credits at 1,000 credits per USD. API signups receive 5,000 credits (2,500 on key creation, 2,500 after email verification).
Authentication
All API requests require an API key sent via the x-api-key header. The Authorization header is not read — requests without x-api-key return 401 missing_api_key.
Keys are created at signup or via POST /api/keys. Your key identifies your organization — all workflows, billing, and data are scoped to it.
Anonymous mode (MCP only)
When connecting via MCP (Smithery, Claude Desktop, Cursor, etc.) without an API key, rigor_plan is available for evaluation against a demo org. Anonymous calls are rate-limited to 30 requests / minute and 500 requests / day per IP.
Quick start
1 · Get a key
curl -X POST https://plith.ai/api/keys \
-H "Content-Type: application/json" \
-d '{"email": "you@example.com", "name": "My Org"}'
// Response — store api_key immediately, it cannot be retrieved again
{
"org_id": "a1b2c3d4-...",
"api_key": "plth_live_xxxxxxxxxxxx...",
"workspace_id": "ws_...",
"email_verified": false,
"credits": 2500,
"message": "Store this key securely — it will not be shown again. Verify your email to unlock 2,500 bonus credits."
}2 · Plan a workflow (free)
curl -X POST https://plith.ai/api/rigor/plan \
-H "x-api-key: YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{"task_description": "Design a caching layer for our API gateway"}'Returns a workflow_id and step sequence. No credits used.
3 · Execute
curl -X POST https://plith.ai/api/rigor/execute \
-H "x-api-key: YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{"workflow_id": "wr_2a4b6c8d...", "delivery": {"method": "polling"}}'4 · Get results
curl https://plith.ai/api/rigor/status/wr_2a4b6c8d... \ -H "x-api-key: YOUR_KEY"
Returns progress during execution and the full deliverable when complete.
Credits & billing
Plith is metered in credits at 1,000 credits per USD. New organizations receive 2,500 free credits immediately on signup. Verify your email to unlock 2,500 bonus credits (5,000 total). No monthly stipend — credits never expire.
Credit packs
| Amount | Credits |
|---|---|
| $10 | 10,000 |
| $25 | 25,000 |
| $100 | 100,000 |
| $250 | 250,000 |
| Custom ($5–$10,000) | 1,000 credits per dollar |
Purchase via POST /api/billing/checkout. Credits are deducted after each successful call; the amount deducted is reported as credits_used; your new balance as credits_remaining.
For Rigor workflows, credits accumulate server-side as steps execute. The pre-execution estimate appears in plan.cost; final usage is reported by GET /api/rigor/status as credits_used.
Rate limits
| Scope | Limit |
|---|---|
| Rigor endpoints (per org, sliding window) | Free/Pro/Team: 120 req / min · Enterprise: 500 req / min |
/api/rigor/execute (per org, UTC day) | Free/Pro/Team: 1,000 req / day · Enterprise: 100,000 req / day |
| Concurrent active workflows per org | Free: 1 · Pro: 3 · Team: 10 · Enterprise: unlimited |
/api/rigor/workflows/actions | 120 req / min |
| Anonymous MCP (per IP) | 30 req / min, 500 / day |
POST /api/keys (per IP) | 3 per 24 hours |
Active workflows (counting toward the concurrent limit): queued, executing, step_executing, resuming, quality_review, interactive_waiting, pending_approval, pending_human_review. When a rate limit is exceeded, the response includes retry_after (seconds) and a Retry-After HTTP header.
POST /api/rigor/plan
Classifies the task and builds a workflow plan without executing it. The plan is persisted so a subsequent /execute call can pass the workflow_id to skip re-classification.
Request body
| Field | Type | Required | Description |
|---|---|---|---|
task_description | string (≥ 2 words) | Yes | What you want produced. Be specific. |
task_type | string | No | Hint to skip auto-classification. |
available_mcp_connections | object[] | No | Each: { id, label, server_url, capabilities } |
preferences | object | No | See preferences fields below. |
context | object | No | { additional_context: string } |
preferences fields
| Field | Type | Description |
|---|---|---|
rigor_level | "quick" | "standard" | "thorough" | Review depth. Default auto-detected. |
max_budget_usd | number | Spending ceiling in USD. If the quote is still above it after fitting, the plan carries a max_budget_exceeded warning and execute refuses with 402 before anything runs. |
skip_frameworks | string[] | Framework display names to exclude. |
only_frameworks | string[] | Restrict to these frameworks (mutex with skip). |
add_frameworks | (string | obj)[] | Inject frameworks: { name, before_step } |
require_approval | boolean | Pause at pending_approval before the final step. |
approval_before_step | number[] | Zero-based step indices for approval gates. Out-of-range indices return 400 invalid_step_index. |
execution | string | Set to 'direct' to compose the plan's content frameworks into a single call and route cost-first. Research, process steps, and the quality review stay separate calls. See direct execution. |
output_contract | object | Only read when execution is 'direct'. Declares the JSON shape you want back, so the answer is generated against your schema and validated before return. See direct execution. |
Response (200)
{
"ok": true,
"plan": {
"workflow_id": "wr_2a4b6c8d...",
"classification": { "task_type": "solution_design", "value_class": "operational" },
"sequence": [
{ "step": 1, "name": "Requirements Analysis", "estimated_credits": 80, "priority_class": "must" },
{ "step": 2, "name": "Solution Design", "estimated_credits": 240, "priority_class": "must" },
{ "step": 3, "name": "Assumption Verification","estimated_credits": 100, "priority_class": "should", "auto_added": true },
{ "step": 4, "name": "Multi-Perspective Review", "estimated_credits": 120, "priority_class": "must" }
],
"cost": { "credits": 540, "usd": 0.54, "range": { "estimated_min": 400, "estimated_max": 680 } },
"rigor_level": "standard",
"allowed_modes": ["direct"],
"execution_fallback": false,
"warnings": [],
"info": ["Review: Haiku + Sonnet"]
}
}Review steps are named "Multi-Perspective Review". When a plan contains multiple review passes, each is qualified by tier: "Multi-Perspective Review (Lightweight)", "Multi-Perspective Review (Standard)", or "Multi-Perspective Review (Full)". Model names are never included in step names.
When preferences.execution: 'direct' is requested and the workflow is eligible, the response includes "execution": "direct". If the request was overridden (tier, workflow size, or approval conflict), execution_fallback: true appears instead. allowed_modes is always present — it contains "direct" when the org and workflow size qualify; empty array otherwise.
The plan sequence always ends with a Synthesis step. This terminal step compiles outputs from all prior framework steps into a unified deliverable with structured findings and cited references. It carries priority_class: "must" and its estimated cost is included in cost.credits.
Errors: 401 unauthorized · 400 invalid_body · 400 missing_field · 400 invalid_task_description · 400 vague_task_description · 429 rate_limit_exceeded · 504 plan_timeout · 500 plan_error
POST /api/rigor/execute
Executes a workflow end-to-end. Pass workflow_id to re-use a stored plan, or pass task_description to classify, plan, persist, and dispatch in a single call. Cost equals the plan's cost.credits. First Rigor workflow per org is free.
Delivery modes
| Mode | Set by | Returns |
|---|---|---|
| sse (default) | delivery.method: "sse" + Accept: text/event-stream | Event stream until completion or 290 s timeout |
| polling | delivery.method: "polling" | workflow_id + poll_url |
| webhook | delivery.method: "webhook" + HTTPS delivery.webhook_url | workflow_id + webhook_secret (32-byte hex HMAC) |
| interactive | mode: "interactive" | Returns interactive_waiting; driven step-by-step via /step |
SSE events
| Event | When |
|---|---|
workflow_started | Workflow metadata + steps preview |
step_started · step_completed · step_skipped | Per step |
quality_review | Score + summary after execution |
workflow_completed · workflow_pending_approval · workflow_error | Terminal events |
processing | Heartbeat every 15 s while waiting |
stream_timeout | Stream closes at 290 s; workflow continues — poll status |
Reconnect: each event carries an id: field of the form workflow_id:step_or_marker. To resume after a drop, resend the request with Last-Event-ID set to the last received id — missed events will be replayed.
GET /api/rigor/status/{workflow_id}
Returns workflow state and results. Only the owning organization can read a workflow.
Status values
pending · planned · queued · executing · step_executing · resuming · quality_review · interactive_waiting · pending_approval · pending_human_review · approved · completed · failed · cancelled · timed_out · blocked · halted
{
"ok": true,
"workflow": {
"workflow_id": "wr_2a4b6c8d...",
"status": "completed",
"task_type": "solution_design",
"current_step": 5,
"total_steps": 5,
"quality_score": 92,
"quality_review": {
"score": 92,
"summary": "Strong design with clear trade-offs.",
"path_to_100": ["Add p50/p95/p99 latency targets per cache tier."]
},
"deliverable": { "title": "...", "sections": [{ "heading": "...", "content": "..." }] },
"credits_used": 530,
"error": null
}
}POST /api/rigor/approve
{
"workflow_id": "wr_2a4b6c8d...",
"decision": "approved",
"notes": "OK to proceed",
"agent_id": "my-review-bot"
}Decision endpoint for workflows paused at pending_approval. On approval, the workflow automatically resumes. On rejection, the workflow is marked failed. Errors: 404 not_found · 409 invalid_status · 500 approve_failed
POST /api/rigor/resume
{
"workflow_id": "wr_2a4b6c8d...",
"revision_guidance": "Focus the rewrite on enterprise buyers; ignore SMB."
}Resumes a workflow from the last completed step. Permitted source statuses: executing, failed, approved, blocked, timed_out, pending_approval. Uses an optimistic lock — concurrent resume calls return resume_conflict.
POST /api/rigor/step
{
"workflow_id": "wr_2a4b6c8d...",
"action": "next",
"feedback": "Focus on security implications."
}Drives a workflow created with mode: "interactive". action accepts "next" | "skip" | "abort". Sessions expire after 30 minutes of inactivity. Errors: 409 invalid_status · 410 session_expired · 409 concurrent_step
GET /api/rigor/workflows
Lists workflows for the authenticated org with filtering and cursor-based pagination. Also returns a real-time concurrent-usage summary against your plan's concurrent workflow limit.
| Param | Type | Description |
|---|---|---|
status | string | Comma-separated (executing,queued). |
task_type | string | Filter by task type. |
created_after | ISO 8601 | After this time. |
created_before | ISO 8601 | Before this time. |
cursor | string | Opaque cursor from prior pagination.cursor. |
limit | integer | 1–100, default 20. |
POST /api/rigor/workflows/actions
{
"action": "cancel",
"workflow_ids": ["wr_aaa...", "wr_bbb..."],
"confirm": false,
"force": false
}Bulk workflow action endpoint. Currently supports action: "cancel". Two-phase: confirm: false (default) shows a preview, confirm: true executes. Max 20 workflow IDs per call.
GET /api/rigor/lineage/{workflowId}
Walks the prior_workflow_id chain backward and returns the full lineage, ordered oldest-first. Capped at 10 hops. Errors: 404 not_found · 422 chain_depth_exceeded
GET /api/rigor/templates
{
"templates": [{
"id": "uuid",
"task_type": "solution_design",
"template_name": "Solution Design",
"template_prompt": "...",
"placeholder_fields": ["system_name", "constraints"],
"description": "Architecture and design document for a technical system.",
"display_order": 1,
"version": 1,
"category": "Engineering"
}]
}Returns deliverable templates — 43 types across 8 categories. No auth required. Query: ?task_type= and/or ?category=.
POST /api/rigor/enhance
{ "task_description": "Design a caching layer", "tier": "llm" }Scores and optionally enhances a task description. tier: "deterministic" (default) is free, rule-based, no auth. tier: "llm" uses an LLM for richer suggestions — 270 credits, requires x-api-key. Response includes score (0–100), suggestions[], and (for llm tier) enhanced_prompt — a rewritten task. Errors: 400 input_too_long (10,000 char max) · 500 enhance_error
Direct execution
Rigor runs a plan 1 of 2 ways, controlled by the optional preferences.execution field. By default each framework step runs as a separate LLM call, and intermediate outputs are available in real time via SSE events and GET /api/rigor/status. Set execution: 'direct' and the plan's content frameworks are composed into a single call, routed cost-first against a per-task-type model floor that moves when a cheaper model earns the work.
It is not a one-call-per-workflow guarantee. Research steps, process steps (CLASSIFICATION-VERIFY, REVIEW-PROTOCOL, SYNTHESIS) and the quality review each remain their own call. An atomic task type has a single content framework, so there is nothing to compose — measured across every atomic workflow to date, direct and standard execution both issue 2 logged step calls. On atomic work the saving is the model, not the call count. Declaring an output_contract replaces the quality-review call with deterministic validation, which is 1 fewer call and the only one.
It is not faster in wall-clock terms for a large plan. Available at every tier — it is a cost optimization, not a paid perk.
When it is selected
Either you ask for it, or Rigor selects it for atomic task types — tag, score, rerank, extract_entities, parse_query, and the quick_* types — when you express no execution preference and request no approval gate. An explicit choice is never overridden, in either direction. GET /api/rigor/task-types returns each type's shape, so you can tell which types these are.
When allowed_modes in the plan response does not contain "direct", the plan is not eligible regardless of the preferences.execution field.
Capabilities
Each task type below is a single-content-framework primitive. Pass the id as task_type, declare an output_contract for the shape you want back, and Rigor routes to the cheapest model that holds it. This list is generated from the same vocabulary the API routes — GET /api/rigor/task-types returns it with each type's shape and capability, so you can read it at runtime rather than from this page.
| Task type | What it does | What you get back |
|---|---|---|
tag | Assign 1 label, or several, from a closed set you define. | The chosen label or labels. |
extract_entities | Pull named values out of unstructured text. | 1 typed record per match, against the schema you declared. |
score | Judge relevance or quality on a scale you define. | A numeric score per item, with the reason when you ask for one. |
rerank | Reorder a list of candidates you supply, best first. | A permutation of your own candidate ids, checked for validity before return when you send the candidates as a JSON list. |
compose | Write one artefact, or a bounded set, to a specification you supply. | Your declared fields, filled — validated against your own schema before return. |
parse_query | Turn a natural-language query into structured search parameters. | 1 record of the query fields you declared. |
cluster | Group items you supply into labelled groups, by a criterion you state. | Groups of your own item ids, plus the ids left in no group. |
grade_set | Grade a set of items you supply as a whole, from the perspectives and against the rubric you declare. | Per perspective: a set score, a top-k score, rubric scores, the questions asked and which items answer them, the noise and the gaps; plus perspective weights and your diagnosis categories. |
quick_research | A single factual lookup with no framework sequence behind it. | A short factual answer. |
quick_classification | General-purpose classification when your labels are not a fixed vocabulary you reuse. | The chosen category. |
quick_extraction | General-purpose extraction when the fields change from call to call. | The extracted values, against the schema you declared. |
quick_scoring | General-purpose rating against criteria you state in the request. | A score, with the reasoning when you ask for one. |
What it supports
These behave the same as standard execution: delivery.method (SSE, polling, and webhook — all 3), rigor_level (all 3), skip_frameworks / only_frameworks / add_frameworks / max_budget_usd, language, MCP connections, the bounded review-protocol loop, independent quality review and rigor_qualval_score, references and citations, step-output transparency, workspace LLM policy, credits and workspace budgets, idempotency, and refund plus partial-result salvage on failure.
What falls back to standard execution
require_approval: true and mode: "interactive" are structurally incompatible — direct execution has no intermediate steps to pause between — and a plan above the composition size limit is too large to compose. In all 3 cases the workflow still runs; only the execution shape changes.
execution_fallback: true appears only when you explicitly asked for direct execution and it could not be honored. When Rigor auto-selected direct execution and fell back, the flag is not set — you did not ask, so nothing was promised.
What is not eligible
The standard executor applies these; direct execution has no code path for either. Rather than run and drop them, a request carrying either is excluded from direct-mode eligibility and runs as standard execution, so the context is applied:
| Request | What happens |
|---|---|
| Attachments and saved artifacts | The workflow runs as standard execution, which builds the attachment manifest. |
prior_workflow_id chaining | The workflow runs as standard execution, which reads inherited_context. |
You are told which applied: the response carries a direct_execution_not_eligible warning per reason, and execution_fallback: true if you asked for direct execution explicitly. No action is needed — the context reached the model.
output_contract
Direct execution's default answer is a prose document. If your consumer is a program rather than a person, declare the shape you want. Rigor asks the vendor for it at generation time — Anthropic forced tool-use, or an OpenAI-compatible json_schema response format — then validates whatever comes back against your declaration before returning it. Supply it under preferences together with execution: 'direct'; it persists with the plan, so a later execute against the stored workflow_id inherits it.
| Field | Type | Required | Description |
|---|---|---|---|
task_type | string | Yes | Your own label for the work. Echoed into telemetry; not read as a framework name and does not change routing. |
shape | "object" | "array" | Yes | "object" for 1 record, "array" for 1 entry per input item. |
fields | object[] | Yes | The schema for 1 record: name, type, and optionally required, enum, minimum, maximum, items, properties, description. |
count | object | No | { min, max } bounds on the number of entries. Only read when shape is "array". |
selection | object | No | { scoreField, minScore, minCount, maxCount } — over-generate candidates, then sort, threshold, and cap before the count bounds are checked. |
The schema is closed. A record carrying a key you did not declare is rejected exactly as a record missing a required key is. Declare every field you want, marking optional ones required: false. A conforming run costs 1 LLM call rather than 2, because the contract is the quality gate and the separate review call is skipped. A run that fails validation is reported as a failed step, not returned as though it had succeeded — the step's extras.output_validation_status carries the outcome and output_validation_violations lists every violation by field path.
Send task_type alongside it. A caller supplying output_contract already knows its task type — the contract names it. Passing it explicitly skips the embedding and LLM-fallback classification tiers, which are a real part of the time-to-first-byte on a direct call.
POST /api/rigor/plan
{
"task_description": "Extract the header fields from each supplied invoice.",
"task_type": "quick_extraction",
"preferences": {
"execution": "direct",
"output_contract": {
"task_type": "invoice_header",
"shape": "array",
"count": { "min": 1, "max": 50 },
"fields": [
{ "name": "invoiceNumber", "type": "string" },
{ "name": "issueDate", "type": "string", "description": "ISO 8601 date" },
{ "name": "totalAmount", "type": "number", "minimum": 0 },
{ "name": "currency", "type": "string", "enum": ["EUR", "GBP", "USD"] },
{ "name": "purchaseOrder", "type": "string", "required": false }
]
}
}
}
// Plan response when eligible — allowed_modes is the only execution field
// /plan returns. Verified against production 2026-08-20.
// "allowed_modes": ["direct"]
// Plan response when not eligible (approval requested, or plan too large):
// "allowed_modes": []
// "execution" and "execution_fallback" appear on the /execute response, not
// on /plan. "execution_fallback" is set only when you asked for direct
// execution explicitly and it could not be honoured.POST /api/rigor/connections
Registers a new MCP server connection. Server URL is validated for SSRF safety at registration time. Credentials are encrypted at rest immediately and never returned. Returns 201 with connection.status: "pending". Call /test to promote to active.
| Field | Type | Required | Description |
|---|---|---|---|
label | string | Yes | Human-readable name. |
server_url | string | Yes | HTTPS URL. |
auth_type | string | Yes | bearer_token | api_key | oauth | none |
transport | string | No | streamable-http (default) or sse |
auth_credential | object | Conditional | Required when auth_type is not none. |
GET /api/rigor/connections
Returns all connections registered for your organization. Credentials are never included. Status values: pending, active, error, revoked. capabilities is null until a connection test completes.
PATCH /api/rigor/connections/{id}
Updates a registered connection. All fields optional.
POST /api/rigor/connections/{id}/test
Tests connectivity. Performs DNS-level SSRF check. On success, status → active, last_tested_at recorded. On SSRF failure, status → error and the response is 400.
DELETE /api/rigor/connections/{id}
Soft delete. Record retained for audit, status set to revoked, credentials cleared. Revoked connections cannot be used in workflow execution.
Connection security
| Control | What it does |
|---|---|
| Credential encryption | AES-256-GCM at rest. Never returned in any response. |
| SSRF — registration | HTTPS only, no private IPs, no cloud metadata endpoints, no internal hostnames. |
| SSRF — test | DNS resolved at test time; all A/AAAA records checked against blocklist. |
| Read-only enforcement | Only resources/read and resources/list are executed. tools/call, prompts/*, sampling/*, all writes are blocked regardless of server capabilities. |
| Org isolation | Every request filtered by your org ID, derived from your API key. |
| Content sanitization | External MCP content wrapped in containment framing, scanned for prompt-injection patterns. |
Limits: max 10 connections per org · duplicate server_url rejected with 409 · HTTPS only.
MCP tools
Connect to Plith via MCP at https://plith.ai/api/mcp/rigor. Compatible with Claude Desktop, Cursor, Windsurf, Smithery, and any MCP-compatible client. Authenticate via the x-api-key header, or use anonymous mode (rate-limited: 30 req/min, 500 req/day per IP) for evaluation.
Discoverable tools
| Tool | Description |
|---|---|
rigor_plan | Plan a workflow without executing it (free). Available anonymously for evaluation. |
rigor_execute | Execute a planned or fresh workflow. |
rigor_status | Get workflow progress and results. |
Hidden tools
Callable but do not appear in tools/list:
| Tool | Description |
|---|---|
rigor_resume | Resume a stalled or failed workflow. |
rigor_approve | Approve or reject a workflow pending human review. |
rigor_step | Drive a single step of an interactive workflow. |
rigor_connections | Manage MCP connections. |
Plan-then-execute (recommended)
Plan first to see the step sequence and cost estimate. Execute only when satisfied.
# Step 1: Plan (free)
POST /api/rigor/plan
{ "task_description": "...", "preferences": { "rigor_level": "standard" } }
→ { plan: { workflow_id: "wr_...", sequence: [...], cost: { credits: 540 } } }
# Step 2: Execute with polling delivery
POST /api/rigor/execute
{ "workflow_id": "wr_...", "delivery": { "method": "polling" } }
→ { workflow_id: "wr_...", status: "queued", poll_url: "/api/rigor/status/wr_..." }
# Step 3: Poll until "completed"
GET /api/rigor/status/wr_...
→ { workflow: { status: "completed", deliverable: { ... } } }Webhook delivery
Receive results pushed to your endpoint instead of polling. Store webhook_secret and verify incoming payloads via HMAC-SHA256.
POST /api/rigor/execute
{
"workflow_id": "wr_...",
"delivery": {
"method": "webhook",
"webhook_url": "https://your-server.example.com/rigor-webhook"
}
}
→ { webhook_url: "...", webhook_status: "registered", webhook_secret: "..." }Approval gate
Request a human decision before final steps execute.
# Plan with approval gate
POST /api/rigor/plan
{ "task_description": "...", "preferences": { "require_approval": true } }
# Execute — workflow will pause at pending_approval
POST /api/rigor/execute
{ "workflow_id": "wr_...", "delivery": { "method": "polling" } }
# Poll until status === "pending_approval", then approve
POST /api/rigor/approve
{ "workflow_id": "wr_...", "decision": "approved", "notes": "Looks good" }
# workflow automatically resumes after approvalrequire_approval is incompatible with preferences.execution: 'direct'. If both are set, the system honors the approval preference and falls back to default multi-call execution. execution_fallback: true will appear in the plan response.
Interactive mode
Drive execution one step at a time. Sessions expire after 30 minutes of inactivity.
POST /api/rigor/execute
{ "task_description": "...", "mode": "interactive" }
→ { status: "interactive_waiting", sequence: [...] }
POST /api/rigor/step
{ "workflow_id": "wr_...", "action": "next" }
→ { status: "step_executing", poll_url: "..." }Workflow chaining
Inherit context from a completed workflow into a new one. Chain depth limit: 10 links.
POST /api/rigor/execute
{
"task_description": "Implement the solution from the prior design",
"prior_workflow_id": "wr_parent...",
"context_fields": ["deliverable", "quality_review"]
}Worked example
A complete plan → execute → poll cycle for a Solution Design.
curl -X POST https://plith.ai/api/rigor/plan \
-H "x-api-key: YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{"task_description": "Design a caching layer for our API"}'
# → plan.cost.credits = 560 · estimated cost shown before committing
curl -X POST https://plith.ai/api/rigor/execute \
-H "x-api-key: YOUR_KEY" \
-d '{"workflow_id": "wr_...", "delivery": {"method": "polling"}}'
# → status: queued · poll_url returned
curl https://plith.ai/api/rigor/status/wr_... \
-H "x-api-key: YOUR_KEY"
# → status: completed · quality_score: 92 · credits_used: 545Error reference
All endpoints return errors in this envelope:
{
"error": {
"code": "missing_field",
"message": "workflow_id is required.",
"request_id": "req_abc123def456...",
"fix": "POST /api/rigor/plan with your task_description",
"docs": "https://plith.ai/docs/errors#missing_field",
"details": { "field": "workflow_id" }
}
}fix, docs, and details are included only when available. request_id is always present.
Auth
| Code | HTTP | Summary |
|---|---|---|
missing_api_key | 401 | Provide x-api-key header. |
invalid_api_key | 401 | Key not found or revoked. |
forbidden | 403 | Resource belongs to a different org. |
Validation
| Code | HTTP | Summary |
|---|---|---|
invalid_body | 400 | Body must be valid JSON. |
missing_field | 400 | <field> required. |
invalid_task_description | 400 | Must contain at least 2 words. |
vague_task_description | 400 | Classifier confidence too low. |
input_too_long | 400 | Input exceeds maximum length. |
invalid_step_index | 400 | approval_before_step index out of range. |
Rate limiting
| Code | HTTP | Summary |
|---|---|---|
rate_limit_exceeded | 429 | Wait per Retry-After header. |
concurrent_limit_exceeded | 429 | Plan's concurrent workflow limit reached (Free: 1, Pro: 3, Team: 10). |
Workflow state
| Code | HTTP | Summary |
|---|---|---|
not_found | 404 | Workflow not found. |
no_plan | 400 | Workflow has no plan. |
insufficient_credits | 402 | Top up via /api/billing/checkout. |
workspace_budget_exceeded | 402 | Adjust budget or reduce plan. |
workflow_has_progress | 409 | Use /resume. |
invalid_status | 409 | Action not allowed in current state. |
duplicate_task | 409 | Identical task in last 5 min. |
already_complete | 409 | Nothing to resume. |
resume_conflict | 409 | Another resume in progress. |
pending_human_review | 409 | Use /approve. |
Chaining & connections
| Code | HTTP | Summary |
|---|---|---|
prior_not_found | 404 | Prior workflow not found. |
prior_not_terminal | 409 | Wait for prior to complete. |
chain_depth_exceeded | 422 | Max 10 hops. |
context_too_large | 422 | Use context_fields to restrict. |
feature_not_enabled | 501 | MCP gateway disabled. |
session_expired | 410 | 30-min TTL — use /resume. |
plan_timeout | 504 | Simplify or split the task. |
provider_exhausted | 503 | All providers failed — retry. |
internal_error | 500 | Retry; include request_id. |
Response conventions
Every successful response includes:
| Field | Type | Description |
|---|---|---|
request_id | string | Always req_ + 24 hex chars. Include when contacting support. |
credits_used | number | Credits deducted by this request. 0 for free endpoints. |
credits_remaining | number | Org balance after this request. |
fallback_behavior | string | What the system did when upstream was degraded — "none" when normal. |
Rigor workflow endpoints (/plan, /execute, /status, /approve, /resume, /step) use product-specific response shapes and do not include credits_used / credits_remaining at the top level. Credit usage for workflows is reported via the status endpoint as workflow.credits_used.