Plith
Инфраструктурный набор для AI-агентов — дедупликация задач, прогнозирование затрат, валидация выходных данных, управление поведением, общая информация об ошибках. 5 продуктов, 14 инструментов MCP, 1 ключ 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+ deliverable 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 once they verify their email.
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.
x-api-key: plth_your_key_here
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 (Claude Code, 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": 0,
"credits_pending_verification": 5000,
"message": "API key created. Plan and status calls work now. Verify your email to add 5,000 free credits; until then, paid calls return insufficient_credits. We grant free signup credits once per person, so an address that already received them gets none."
}
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 accounts created at plith.ai receive 20,000 free credits; accounts created through POST /api/keys receive 5,000 free credits once they verify their email. Free signup credits are granted once per person. 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
POST/api/rigor/planAuth: x-api-key · Cost: Free
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
POST/api/rigor/executeAuth: x-api-key · Cost: Variable
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}
GET/api/rigor/status/{workflow_id}Auth: x-api-key · Cost: Free
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
GET/api/rigor/workflowsAuth: x-api-key · Cost: Free
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
POST/api/rigor/workflows/actionsAuth: x-api-key · Cost: Free · 120/min
{
"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.
DELETE /api/rigor/workflows/{id}
{ "deleted": true, "workflow_id": "wr_abc123..." }
Deletes one workflow and its content: task, plan, steps, deliverable, follow-ups, versions, and workflow attachments. Saved artifacts, billing records, and workflows built on it stay; those workflows keep their own copy. Deleting an id you already deleted returns 200 again. Errors: 404 not_found · 409 workflow_running (cancel first) · 409 children_not_finished (details.children). Not available as an MCP tool.
GET /api/rigor/lineage/{workflowId}
GET/api/rigor/lineage/{workflowId}Auth: viewer · Cost: Free
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
POST/api/rigor/enhanceAuth: Tier-conditional · Cost: Free / 270c
{ "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. |
rewrite | Rewrite text you supply to an instruction you give, keeping every fact it states. | The same blocks, rewritten, between the markers you sent; numbers, dates, names, citations and web addresses kept as they were. |
fill_arguments | Fill the arguments of operations you declare, from an instruction and the items you supply. | A JSON list of operations, each with arguments that match your declared schema and name only ids you sent. |
synthesise | Answer a question from source text you supply, citing only that text. | A short answer whose citations and web addresses all come from your source text. |
verify | List the checkable claims a text you supply makes. | A JSON list of claims, each with the exact numbers, dates, names, qualifiers and negations the text states. |
compare | Compare 2 claim lists you supply and say where they differ. | A JSON verdict, unchanged or changed, with the claims added, removed and altered. |
classify | Assign 1 label from a closed set you declare to an input you supply. | 1 of your labels, as JSON, with the arguments you declared for it. |
grounded_edit | Edit or add text you supply to an instruction you give, citing only sources you supply for anything new. | The edited and added blocks between the markers you sent, with every new statement citing 1 of your sources. |
grounded_answer | Answer a question only from passages you supply, synthesising and comparing across them, every sentence citing them. | Cited sentences, or a refusal that says what the passages lack, with each citation's passage id and quote offsets computed by Rigor. |
grounded_lookup | Look up 1 fact in passages you supply, citing them, on a lighter model than grounded_answer. | Up to 4 cited sentences, or a refusal that says what the passages lack, with each citation's passage id and quote offsets computed by Rigor. |
claim_verify | Split an answer into atomic claims and judge each against the passages it cites, on a model from another creator. | Each claim with supported, partial or unsupported and its evidence span, plus the support rate and the claims to cut. |
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.
You get a format check, not a quality score. Your output contract replaces the quality review, so a direct run with output_contract returns quality_score: null, with no quality_floor and no quality_breakdown. It returns format_check instead: passed is true when the answer matches your schema, validation_status carries the validator's status, and on a failure violations lists up to 5 entries, each a field path and a reason, with violations_total when there are more. Branch on passed; the reason text is for people, not for parsing. A format check tells you the answer has the shape you declared, not that its content is right. Runs without a contract are scored as before.
// A direct run with output_contract, status API
"quality_score": null,
"format_check": { "passed": true, "validation_status": "valid" }
// The same run when the answer does not match
"quality_score": null,
"format_check": {
"passed": false,
"validation_status": "invalid_schema",
"violations": ["result[0].name: required field missing"]
}
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
POST/api/rigor/connectionsAuth: x-api-key · Cost: Free
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
GET/api/rigor/connectionsAuth: x-api-key · Cost: Free
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}
PATCH/api/rigor/connections/{id}Auth: admin · Cost: Free
Updates a registered connection. All fields optional.
POST /api/rigor/connections/{id}/test
POST/api/rigor/connections/{id}/testAuth: x-api-key · Cost: Free
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}
DELETE/api/rigor/connections/{id}Auth: x-api-key · Cost: Free
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, 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 approval
require_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: 545
Error 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.
The error catalog lists every code, what it means, and what you do about it.
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 | The workflow in prior_workflow_id was not found, or your key cannot see it. |
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.