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 an independent quality review.
Base URL:
https://plith.ai
Platform endpoints (authentication, billing, workspaces, notifications, GDPR) are documented in EXTERNAL-PLATFORM-API-REFERENCE.md.
All API requests require an API key sent via the x-api-key header:
x-api-key: plth_your_key_here
Do NOT use the Authorization: Bearer format — that is not supported for API authentication.
Keys are created at signup or via POST /api/keys (see Platform reference). Your key identifies your organization — all workflows, billing, and data are scoped to it.
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.
curl -X POST https://plith.ai/api/keys -H "Content-Type: application/json" -d "{\"email\": \"you@example.com\", \"name\": \"My Org\"}"
Response (store the key — shown once):
{
"org_id": "a1b2c3d4-...",
"key": "plth_live_xxxxxxxxxxxx...",
"prefix": "plth_live_xxx",
"message": "Store this key securely — it will not be shown again."
}
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.
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\"}}"
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.
Plith is metered in credits at 1,000 credits per USD.
| Pack | Price | Credits |
|---|---|---|
| 10 | $10 | 10,000 |
| 25 | $25 | 25,000 |
| 100 | $100 | 100,000 |
| 250 | $250 | 250,000 |
| Custom | $5–$10,000 | 1,000 credits / dollar |
Purchase via POST /api/billing/checkout. See Platform reference.
Credits are deducted after each successful call. The amount deducted is reported in the response 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.
| Scope | Limit |
|---|---|
| Rigor endpoints (per org, sliding window) | 120 requests / minute |
/api/rigor/execute (per org, UTC day) |
1,000 requests / day |
| Concurrent active workflows per org | 10 |
/api/rigor/workflows/actions |
30 requests / minute |
| Anonymous MCP (per IP) | 30 requests / minute, 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 → workflow_id + step sequence (free)
POST /api/rigor/execute → triggers execution (SSE / polling / webhook)
GET /api/rigor/status/{id} → progress + deliverable when done
POST /api/rigor/approve → approve/reject pending_approval workflows
POST /api/rigor/resume → continue failed/stalled workflows
POST /api/rigor/step → drive interactive (Mode C) workflows one step at a time
Auth: Required (x-api-key)
Cost: Free — no credits deducted.
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 | MCP connection metadata for source-map generation. Each: { id, label, server_url, capabilities }. |
preferences |
object | No | See preferences fields below. |
context |
object | No | { additional_context: string } — free-form context the planner uses. |
preferences fields:
| Field | Type | Description |
|---|---|---|
rigor_level |
"quick" | "standard" | "thorough" |
Review depth. Default auto-detected from task type. |
tool_scope |
"mandatory" | "recommended" | "all" |
Which tool tiers to include. |
model_preference |
"quality_first" | "cost_optimized" | "balanced" |
LLM model selection bias. |
max_budget_usd |
number | Budget ceiling; triggers a budget_below_estimate warning if the plan exceeds it. |
skip_frameworks |
string[] | Framework display names to exclude from the plan. |
only_frameworks |
string[] | Restrict the plan to only these frameworks (mutually exclusive with skip_frameworks). |
add_frameworks |
(string | { name, before_step })[] |
Inject additional frameworks into the sequence. |
require_approval |
boolean | Pause at pending_approval before final steps. |
approval_before_step |
number[] | Zero-based step indices at which to insert an approval gate. |
execution |
string | Set to 'direct' to execute all steps in a single composed LLM call. When omitted, each step runs as a separate call with intermediate outputs available via SSE and the status endpoint. Direct mode requires Pro+ tier and is subject to workflow size limits. |
Unknown top-level body fields are detected and surfaced as unknown_field warnings in the response rather than silently ignored.
Response (200):
{
"ok": true,
"plan": { },
"generated_title": "Caching Layer Design — API Gateway"
}
{
"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": "Review (Sonnet)",
"estimated_credits": 120,
"priority_class": "must"
}
],
"cost": {
"credits": 540,
"usd": 0.54,
"range": {
"estimated_min": 400,
"estimated_max": 680
}
},
"alternatives": {
"quick": {
"steps": 3,
"cost": {
"credits": 260,
"range": { "estimated_min": 190, "estimated_max": 330 }
},
"skips": ["Assumption Verification"],
"review_model": "Haiku",
"includes_verification": false,
"tradeoff": "Fewer steps, skips verification."
},
"thorough": {
"steps": 6,
"cost": {
"credits": 860,
"range": { "estimated_min": 640, "estimated_max": 1080 }
},
"adds": ["Gap Analysis"],
"review_model": "Sonnet + Opus",
"includes_verification": true,
"tradeoff": "More verification steps and multi-model review."
}
},
"context": {
"connections": [
{ "name": "GitHub", "type": "github", "status": "active" }
],
"referenced_files": {
"found": ["src/api/gateway.ts"],
"not_found": []
},
"sufficient": true
},
"info": ["Review: Haiku + Sonnet"],
"warnings": [],
"rigor_level": "standard"
}
Field reference — sequence steps:
| Field | Always present | Description |
|---|---|---|
step |
Yes | 1-based step number. |
name |
Yes | Display name. Review steps carry the model label: "Review (Haiku)", "Review (Sonnet)", "Review (Opus)". |
estimated_credits |
Yes | Per-step credit estimate. |
priority_class |
When set | MoSCoW priority: "must", "should", "could". |
auto_added |
When true |
Step was auto-inserted by prerequisite resolution. |
Field reference — cost:
| Field | Description |
|---|---|
credits |
Primary estimate — adaptive when historical data exists, static otherwise. |
usd |
credits × 0.001. |
range.estimated_min / range.estimated_max |
Confidence range when adaptive data exists. Absent for task types with no execution history. |
Field reference — alternatives:
The selected tier is removed. The remaining tiers each contain:
| Field | Description |
|---|---|
steps |
Total step count for this tier. |
cost.credits |
Estimated credits. |
cost.range |
Confidence range when adaptive data exists. |
skips |
Display names of frameworks in the current plan that this tier would omit. |
adds |
Display names of frameworks this tier would add beyond the current plan. |
review_model |
LLM(s) used for quality review: "Haiku", "Haiku + Sonnet", or "Sonnet + Opus". |
includes_verification |
Whether this tier includes Assumption Verification or Dependency Verification. |
tradeoff |
Human-readable tradeoff sentence. |
Field reference — warnings:
Structured actionable warnings. Each entry: { code, message, suggestion }.
| Warning code | Trigger |
|---|---|
unknown_field |
Caller sent an unrecognized top-level body field. |
budget_below_estimate |
preferences.max_budget_usd is below the plan's cost estimate. |
context_insufficient |
An MCP connection returned degraded context. |
connection_error |
An MCP connection failed to respond. |
no_coding_agent |
No coding-agent MCP server is connected; a prompt-protocol step was substituted. |
mcp_cost_optimization |
Claude Code is connected without GitHub MCP — broad codebase scanning will incur extra token cost. |
file_not_found |
A file referenced in context.additional_context was not found in the connected repository. |
Field reference — execution mode:
| Field | Description |
|---|---|
execution |
"direct" when direct execution was requested and the workflow is eligible. Absent otherwise. |
execution_fallback |
true when the requested execution approach was overridden. This occurs when: (1) 'direct' was requested but unavailable due to server configuration, or (2) 'direct' was requested alongside require_approval: true or interactive mode, which require intermediate steps that direct mode cannot provide. Absent when no fallback occurred. |
allowed_modes |
Always present. Contains "direct" when the org and this workflow size are eligible for direct execution; empty array otherwise. |
Synthesis step: Rigor automatically appends a terminal Synthesis step to every workflow. It is always the last entry in
sequenceand carriespriority_class: "must". Its estimated cost (530 credits) is included incost.credits.
When the task description contains multiple independent intents that cannot share a single execution context, Rigor splits them into separate workflow plans. The plan field returns a MultiWorkflowPlan instead of a single WorkflowPlan:
{
"ok": true,
"plan": {
"plans": [
{ },
{ }
],
"total_cost": { "credits": 1200, "usd": 1.20 },
"intent_groups": [
{
"workflow_id": "wr_aaa...",
"intents": [
{ "task_type": "solution_design", "confidence": 0.92, "source_phrase": "design a caching layer" }
],
"primary_task_type": "solution_design",
"reason": "single_intent"
},
{
"workflow_id": "wr_bbb...",
"intents": [
{ "task_type": "code_review", "confidence": 0.88, "source_phrase": "review the existing auth module" }
],
"primary_task_type": "code_review",
"reason": "single_intent"
}
],
"info": ["Task split into 2 separate workflows for independent execution."],
"warnings": []
}
}
To distinguish: a single-plan response has a workflow_id at the top of plan; a multi-plan response has plans[] with no top-level workflow_id.
Errors:
401 unauthorized400 invalid_body400 missing_field — task_description required.400 invalid_task_description — empty or single word.400 vague_task_description — classifier confidence too low; details.classification_confidence, details.classified_as.429 rate_limit_exceeded — 120 req/min.504 plan_timeout — planning exceeded 25 s; simplify or split the task description.500 plan_errorcurl (Windows):
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 with Redis integration\"}"
Auth: Required (x-api-key)
Cost: Variable — equals the plan's cost.credits.
Executes a workflow end-to-end. Two entry points:
workflow_id — re-uses a stored plan from a prior /plan call. Cannot be called on a workflow that already has progress (use /resume instead).workflow_id — pass task_description and Rigor will classify, plan, persist, and dispatch in a single call.Three delivery modes: sse (default), polling, webhook. Interactive mode (mode: "interactive") returns interactive_waiting and is driven step-by-step via /api/rigor/step.
Request body:
| Field | Type | Required | Description |
|---|---|---|---|
workflow_id |
string | Conditional | If supplied, executes a stored plan. If omitted, task_description is required. |
task_description |
string (≥ 2 words) | Conditional | Required when workflow_id is not supplied. |
task_type |
string | No | Classification hint. |
preferences |
object | No | Same shape as /plan. |
context |
object | No | { additional_context: string } |
delivery |
object | No | { method: "sse" | "polling" | "webhook", webhook_url?: string }. Default "sse". |
mode |
string | No | "interactive" for Mode C; otherwise "full". |
language |
string | No | BCP 47 language code (e.g. "fr", "de", "ja"). Overrides org default. |
prior_workflow_id |
string | No | Chain a new workflow off a completed prior workflow. |
context_fields |
string[] | No | Fields to inherit from prior_workflow_id. |
refresh_connections |
boolean | No | Force a re-fetch of MCP connection state before planning. |
agent_id |
string | No | Identifier of the calling agent (for audit / routing). |
workflow_ids |
string[] | No | Pre-planned workflow IDs for multi-intent execution without re-planning. |
Webhook delivery requires delivery.webhook_url to be a valid HTTPS URL.
{
"ok": true,
"workflow_id": "wr_2a4b6c8d...",
"status": "queued",
"task_type": "solution_design",
"value_class": "operational",
"plan": { },
"estimated_credits": 540,
"poll_url": "/api/rigor/status/wr_2a4b6c8d...",
"delivery_mode": "polling",
"available_modes": ["sse", "polling", "webhook", "interactive"]
}
For webhook delivery the body also includes webhook_url, webhook_status: "registered", and (on first creation) a webhook_secret — a 32-byte hex HMAC secret for verifying webhook payloads.
Content-Type: text/event-stream, Cache-Control: no-cache, no-transform, X-Accel-Buffering: no.
Event sequence:
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, or workflow_error.processing — heartbeat every 15 s while waiting.stream_timeout — stream closes at 290 s; workflow continues server-side and you can poll status.Each event carries an id: field of the form {workflow_id}:{step_or_marker}. To reconnect after a drop, resend the request with a Last-Event-ID header set to the last received id: — missed events will be replayed.
{
"ok": true,
"workflow_id": "wr_2a4b6c8d...",
"status": "interactive_waiting",
"classification": { "task_type": "solution_design", "value_class": "operational" },
"sequence": [
{ "index": 0, "name": "Requirements Analysis", "required": true }
],
"total_steps": 5,
"cost": { "credits": 540, "usd": 0.54 },
"delivery_mode": "interactive",
"available_modes": ["sse", "polling", "webhook", "interactive"]
}
Errors (in addition to /plan errors):
400 invalid_language — language is not a valid BCP 47 code.400 no_plan — workflow_id row has no plan.400 webhook_url_required / 400 webhook_url_https / 400 webhook_url_invalid402 insufficient_credits — details.required_credits, details.balance.402 workspace_budget_exceeded — workflow estimate exceeds the workspace's monthly credit budget.403 forbidden — workflow_id belongs to a different org.403 tier_required — feature requires a higher subscription tier.404 not_found — workflow_id not found.404 prior_not_found409 prior_not_terminal409 duplicate_task — same task submitted in last 5 min; details.existing_workflow_id.409 workflow_has_progress — stored workflow already has step results; use /resume.410 workspace_archived — workspace has been archived.422 chain_depth_exceeded — prior_workflow_id chain exceeds 10 links.422 context_too_large — details.estimated_tokens, details.max_tokens.429 concurrent_limit_exceeded — details.active_count, details.limit, details.active_workflows.500 queue_failedcurl (Windows) — polling delivery:
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\"}}"
curl (Windows) — SSE delivery:
curl -X POST https://plith.ai/api/rigor/execute -H "x-api-key: YOUR_KEY" -H "Accept: text/event-stream" -H "Content-Type: application/json" -d "{\"workflow_id\": \"wr_2a4b6c8d...\"}"
Auth: Required (x-api-key)
Cost: Free
Returns workflow state and results. Only the owning organization can read a workflow.
Response (200):
{
"ok": true,
"workflow": {
"workflow_id": "wr_2a4b6c8d...",
"status": "completed",
"task_type": "solution_design",
"value_class": "operational",
"current_step": 5,
"total_steps": 5,
"actual_steps_executed": 5,
"completed_step_summaries": [
{ "step": 1, "tool": "Requirements Analysis", "summary": "...", "model": "claude-sonnet-4-6" }
],
"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": "Solution Design — Rigor Workflow Output",
"sections": [
{ "heading": "Requirements Analysis", "content": "..." }
]
},
"credits_used": 530,
"error": null
}
}
Field reference:
| Field | Description |
|---|---|
workflow_id |
Unique workflow identifier. |
status |
One of: pending, planned, queued, executing, step_executing, resuming, quality_review, interactive_waiting, pending_approval, pending_human_review, approved, completed, failed, timed_out, blocked, halted. |
task_type |
Classified task type (e.g. solution_design, code_review). |
value_class |
One of "operational", "governance", "strategic". |
current_step |
Most recent step number reached. |
total_steps |
Total steps in the plan. |
actual_steps_executed |
Count of steps with status completed, revised, or refined. More accurate than current_step after revisions or runtime insertions. |
completed_step_summaries |
Array of completed steps. Each: { step, tool, summary, model? }. model is the LLM identifier used for that step (Pro+ only). |
quality_score |
0–100 review score. null when not yet scored — never coerced to 0. |
quality_review |
{ score, summary, path_to_100? }. path_to_100 lists concrete improvements to reach a perfect score. |
deliverable |
Present when status is completed or pending_approval. Shape: { title, sections: [{ heading, content }] }. |
credits_used |
Credits consumed so far. |
error |
Error message when the workflow failed. |
Halted workflow fields (present only when status === "halted"):
| Field | Description |
|---|---|
halted_at_step |
Step number where the halt occurred. |
halt_reason |
Human-readable reason (e.g. required context missing, MCP connection failed). |
blocking_issues |
Structured list of issues that triggered the halt. |
credits_saved_by_halt |
Credits not consumed because execution stopped early. |
resolution_options |
Array of four suggested remediation actions. |
Example halted workflow response:
{
"workflow_id": "wr_2a4b6c8d...",
"status": "halted",
"task_type": "solution_design",
"current_step": 2,
"total_steps": 5,
"actual_steps_executed": 1,
"completed_step_summaries": [],
"halted_at_step": 2,
"halt_reason": "Required context missing: file src/api/gateway.ts not accessible via connected repository.",
"blocking_issues": ["File src/api/gateway.ts referenced but not found in GitHub MCP connection"],
"credits_saved_by_halt": 340,
"resolution_options": [
"Skip the blocking framework using preferences.skip_frameworks and re-execute",
"Verify the MCP connection can access the referenced files, then re-execute with refresh_connections: true",
"Re-submit with the missing file content provided inline in context.additional_context",
"Fix the blocking issue and re-execute with prior_workflow_id to inherit completed work"
],
"credits_used": 80
}
Chunk execution fields (present when a step is executing in chunked mode):
| Field | Description |
|---|---|
current_chunk.index |
Zero-based index of the chunk currently executing. |
current_chunk.total |
Total number of chunks for the current step. |
Errors:
401 unauthorized400 missing_field — workflow_id required.404 not_found — also returned when the workflow belongs to a different org.429 rate_limit_exceededcurl (Windows):
curl https://plith.ai/api/rigor/status/wr_2a4b6c8d... -H "x-api-key: YOUR_KEY"
Auth: Required (x-api-key)
Cost: Free
Decision endpoint for workflows paused at pending_approval. On approval, the workflow automatically resumes execution.
Request body:
| Field | Type | Required | Description |
|---|---|---|---|
workflow_id |
string | Yes | Workflow to decide. |
decision |
"approved" | "rejected" |
Yes | Your decision. |
notes |
string | No | Notes captured with the decision and (on approval) merged into preferences for subsequent steps. |
agent_id |
string | No | Identifier of the approving agent (defaults to "board_chair"). |
Response (200) — approved:
{
"ok": true,
"workflow_id": "wr_2a4b6c8d...",
"decision": "approved",
"message": "Workflow approved and resuming execution."
}
Response (200) — rejected:
{
"ok": true,
"workflow": {
"workflow_id": "wr_2a4b6c8d...",
"status": "failed",
"halt_reason": "rejected"
},
"message": "Workflow rejected. No further steps will execute."
}
On rejection, the workflow is marked failed with halt_reason: "rejected".
Errors:
401 unauthorized400 invalid_body400 missing_field — workflow_id, decision.404 not_found409 invalid_status — workflow is not at pending_approval.429 rate_limit_exceeded500 approve_failedcurl (Windows):
curl -X POST https://plith.ai/api/rigor/approve -H "x-api-key: YOUR_KEY" -H "Content-Type: application/json" -d "{\"workflow_id\": \"wr_2a4b6c8d...\", \"decision\": \"approved\", \"notes\": \"OK to proceed\"}"
Auth: Required (x-api-key)
Cost: Variable — incremental cost of remaining steps.
Resumes a workflow from the last completed step. Permitted source statuses: executing, failed, approved, blocked, timed_out, pending_approval, pending_human_review. Uses an optimistic lock — concurrent resume calls return resume_conflict.
For pending_human_review workflows, the operator must explicitly acknowledge the escalation by passing ?approve=true on the query string.
Request body:
| Field | Type | Required | Description |
|---|---|---|---|
workflow_id |
string | Yes | Workflow to resume. |
revision_guidance |
string | No | Free-text guidance injected into the execution context for subsequent steps. |
Response (200):
{
"ok": true,
"workflow_id": "wr_2a4b6c8d...",
"status": "resuming",
"resuming_from_step": 3,
"total_steps": 5,
"poll_url": "/api/rigor/status/wr_2a4b6c8d...",
"message": "Resuming from step 3 of 5. Poll status for progress.",
"revision_guidance": null
}
resuming_from_step is the next step the worker will execute.
Errors:
401 unauthorized400 invalid_body400 missing_field — workflow_id required.404 not_found409 invalid_status — details.allowed_statuses lists permitted statuses.409 resume_conflict — another resume is already in flight.409 already_complete — every step has a result; nothing to resume.429 rate_limit_exceededcurl (Windows):
curl -X POST https://plith.ai/api/rigor/resume -H "x-api-key: YOUR_KEY" -H "Content-Type: application/json" -d "{\"workflow_id\": \"wr_2a4b6c8d...\"}"
Auth: Required (x-api-key)
Cost: Variable — incremental cost of one step.
Drives a workflow created with mode: "interactive". The interactive session expires after 30 minutes of inactivity; on timeout the workflow is marked timed_out and the call returns session_expired.
Request body:
| Field | Type | Required | Description |
|---|---|---|---|
workflow_id |
string | Yes | Interactive workflow to drive. |
action |
"next" | "skip" | "abort" |
Yes | What to do with the current step. |
feedback |
string | No | Optional free-text forwarded to the step execution context. |
Response (200) — action: "abort":
{
"ok": true,
"workflow_id": "wr_2a4b6c8d...",
"status": "aborted",
"steps_completed": 2,
"total_steps": 5
}
Response (200) — action: "next" (step dispatched):
{
"ok": true,
"workflow_id": "wr_2a4b6c8d...",
"status": "step_executing",
"step": { "index": 2, "name": "Research" },
"poll_url": "/api/rigor/status/wr_2a4b6c8d...",
"message": "Step execution started. Poll status for result."
}
Response (200) — action: "skip":
{
"ok": true,
"workflow_id": "wr_2a4b6c8d...",
"status": "interactive_waiting",
"step_executed": {
"step": 2,
"name": "Assumption Verification",
"status": "skipped"
},
"next_step": { "index": 3, "name": "Solution Design" },
"steps_completed": 1,
"total_steps": 5
}
Errors:
401 unauthorized400 invalid_body400 missing_field — workflow_id required.400 invalid_action — must be next, skip, or abort.400 invalid_field — workflow must have been created with mode: "interactive".400 no_plan403 tier_required — feature requires a higher subscription tier.404 not_found409 invalid_status — workflow is not at interactive_waiting.409 concurrent_step — another step execution holds the lock; retry in 2–3 seconds.410 session_expired — 30-min TTL exceeded; workflow marked timed_out.429 rate_limit_exceeded500 step_errorcurl (Windows):
curl -X POST https://plith.ai/api/rigor/step -H "x-api-key: YOUR_KEY" -H "Content-Type: application/json" -d "{\"workflow_id\": \"wr_2a4b6c8d...\", \"action\": \"next\"}"
Auth: Required (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.
Query parameters:
| Parameter | Type | Description |
|---|---|---|
status |
string | Comma-separated status filter (e.g. executing,queued). |
task_type |
string | Filter by task type. |
created_after |
ISO timestamp | Workflows created after this time. |
created_before |
ISO timestamp | Workflows created before this time. |
counts_toward_limit |
"true" | "false" |
Filter to workflows that do / do not count toward the concurrent limit. |
cursor |
string | Opaque cursor from a prior response's pagination.cursor for the next page. |
limit |
integer | Page size. Min 1, max 100, default 20. |
q |
string | Full-text search on workflow title and task description. Max 200 characters. |
folder_id |
string | Filter by folder ID. Pass "unassigned" to return workflows not in any folder. |
Response (200):
{
"ok": true,
"request_id": "req_abc123...",
"credits_used": 0,
"credits_remaining": 9484,
"workflows": [
{
"workflow_id": "wr_2a4b6c8d...",
"status": "completed",
"task_type": "solution_design",
"current_step": 5,
"total_steps": 5,
"credits_charged": 530,
"created_at": "2026-05-08T12:00:00Z",
"started_at": "2026-05-08T12:00:01Z",
"completed_at": "2026-05-08T12:05:00Z"
}
],
"pagination": {
"cursor": "2026-05-08T12:00:00Z",
"has_more": false,
"total_count": 1,
"limit": 20
},
"concurrent_summary": {
"active": 0,
"limit": 5,
"remaining": 5
}
}
Workflow summary fields: workflow_id, status, task_type, current_step, total_steps, credits_charged, created_at, started_at, completed_at. When present: error, halt_reason, halted_at_step, approval_status.
concurrent_summary.limit is null on Enterprise plans (unlimited); otherwise it is the tier's integer cap. concurrent_summary.remaining is null when limit is null.
Errors:
401 unauthorized429 rate_limit_exceeded500 internal_errorcurl (Windows):
curl "https://plith.ai/api/rigor/workflows?status=executing,queued&limit=10" -H "x-api-key: YOUR_KEY"
Auth: Required (x-api-key)
Cost: Free
Rate limit: 30 requests/minute (separate from the main 120 req/min limit)
Bulk workflow action endpoint. Currently supports action: "cancel". Operates in two modes:
confirm: false) — shows what would happen without making changes.confirm: true) — applies the cancellations.Request body:
| Field | Type | Required | Description |
|---|---|---|---|
action |
"cancel" |
Yes | Action to perform. Only "cancel" is currently supported. |
workflow_ids |
string[] | Yes | IDs of workflows to act on. Max 20 per request. Deduplicated automatically. |
confirm |
boolean | No | false (default) = preview only. true = execute. |
force |
boolean | No | true = force-cancel workflows in executing or step_executing status. Without this, those statuses are skipped. |
Workflows not owned by the caller's org are silently skipped.
Response (200) — preview (confirm: false):
{
"ok": true,
"request_id": "req_abc123...",
"preview": true,
"affected": [
{ "workflow_id": "wr_aaa...", "current_status": "queued", "action": "cancel" },
{ "workflow_id": "wr_bbb...", "current_status": "executing", "action": "force_cancel" }
],
"would_skip": [
{ "workflow_id": "wr_ccc...", "reason": "Status 'completed' cannot be cancelled" }
],
"message": "2 workflow(s) would be cancelled. Send confirm: true to execute."
}
Response (200) — execute (confirm: true):
{
"ok": true,
"request_id": "req_abc123...",
"credits_used": 0,
"credits_remaining": 9484,
"results": {
"succeeded": [
{ "workflow_id": "wr_aaa...", "previous_status": "queued", "new_status": "cancelled" }
],
"failed": [],
"skipped": [
{ "workflow_id": "wr_ccc...", "reason": "Status 'completed' cannot be cancelled" }
]
},
"message": "1 cancelled, 1 skipped, 0 failed."
}
Errors:
401 unauthorized400 invalid_body400 invalid_action — only "cancel" is accepted.400 no_workflow_ids — workflow_ids missing or empty.400 bulk_limit_exceeded — workflow_ids exceeds 20.429 rate_limit_exceeded (30 req/min)500 internal_errorcurl (Windows) — preview:
curl -X POST https://plith.ai/api/rigor/workflows/actions -H "x-api-key: YOUR_KEY" -H "Content-Type: application/json" -d "{\"action\": \"cancel\", \"workflow_ids\": [\"wr_aaa...\"]}"
curl (Windows) — execute:
curl -X POST https://plith.ai/api/rigor/workflows/actions -H "x-api-key: YOUR_KEY" -H "Content-Type: application/json" -d "{\"action\": \"cancel\", \"workflow_ids\": [\"wr_aaa...\"], \"confirm\": true}"
Auth: Required (x-api-key) — minimum role: viewer
Cost: Free
Walks the prior_workflow_id chain backward from the given workflow and returns the full lineage, ordered oldest-first. Capped at 10 hops.
Response (200):
{
"ok": true,
"chain": [
{
"workflow_id": "wr_oldest...",
"status": "completed",
"task_type": "solution_design",
"total_steps": 4,
"created_at": "2026-05-01T10:00:00Z"
},
{
"workflow_id": "wr_2a4b6c8d...",
"status": "completed",
"task_type": "code_review",
"total_steps": 3,
"created_at": "2026-05-08T12:00:00Z"
}
],
"total_steps": 7,
"chain_depth": 2
}
Errors:
401 unauthorized400 missing_field — workflowId required in path.404 not_found — workflow not found or belongs to another org.422 chain_depth_exceeded — chain exceeds 10 hops.429 rate_limit_exceededcurl (Windows):
curl https://plith.ai/api/rigor/lineage/wr_2a4b6c8d... -H "x-api-key: YOUR_KEY"
Auth: None — public endpoint. Cost: Free
Returns deliverable templates — 43 types across 8 categories — that describe how Rigor structures its output for each task type. Templates can be used to preview the deliverable shape before running a workflow.
Query parameters:
| Parameter | Type | Description |
|---|---|---|
task_type |
string | Exact match filter on task type. |
category |
string | Exact match filter on category. |
Response (200):
{
"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.",
"example_filled": "...",
"display_order": 1,
"version": 1,
"category": "Engineering"
}
]
}
curl (Windows):
curl "https://plith.ai/api/rigor/templates?category=Engineering"
Auth: Not required for deterministic tier. Required (x-api-key) for llm tier.
Cost: Free for deterministic. 270 credits for llm.
Scores and optionally enhances a task description before submitting it to Rigor. Use this to check task quality and get improvement suggestions before spending credits on execution.
Request body:
| Field | Type | Required | Description |
|---|---|---|---|
task_description |
string | Yes | Task description to score. Max 10,000 characters. |
task_type |
string | No | Hint for more accurate scoring. |
tier |
"deterministic" | "llm" |
No | Default "deterministic". "llm" uses an LLM for richer suggestions. |
Response (200):
{
"ok": true,
"request_id": "req_abc123...",
"score": 72,
"suggestions": [
"Specify the target programming language or framework.",
"Mention any performance or scale constraints."
],
"enhanced": "Design a Redis-backed caching layer for the REST API gateway, targeting sub-10ms p99 cache hit latency and 99.9% availability. The cache should handle 50k req/s at peak..."
}
enhanced is only present when tier: "llm".
Errors:
400 invalid_body400 missing_field — task_description required.400 input_too_long — task_description exceeds 10,000 characters.500 enhance_errorcurl (Windows) — deterministic (no auth):
curl -X POST https://plith.ai/api/rigor/enhance -H "Content-Type: application/json" -d "{\"task_description\": \"Design a caching layer\"}"
curl (Windows) — LLM tier:
curl -X POST https://plith.ai/api/rigor/enhance -H "x-api-key: YOUR_KEY" -H "Content-Type: application/json" -d "{\"task_description\": \"Design a caching layer\", \"tier\": \"llm\"}"
MCP connections let your organization register external MCP servers as data sources. Once registered and tested, connected servers provide read-only external context to Rigor workflow executions.
All connection endpoints require x-api-key and return 501 if the MCP gateway is not enabled.
Auth: Required (x-api-key) — minimum role: admin
Cost: Free
Registers a new MCP server connection. The server URL is validated for SSRF safety at registration time. Credentials are encrypted at rest immediately and never returned in any response.
Request body:
| Field | Type | Required | Description |
|---|---|---|---|
display_name |
string | Yes | Slug-safe identifier (1-32 chars, [a-z0-9_-]+). Used in tool namespacing as mcp__{display_name}__{tool}. Immutable after first tool call. |
scope |
"org" | "workspace" |
Yes | org = visible to all workspaces. workspace = visible to one workspace only. |
workspace_id |
string (UUID) | Conditional | Required when scope is "workspace". Must be a valid workspace in the org. |
label |
string | No | Alias for display_name. If both are provided, display_name takes precedence. |
server_url |
string | Yes | HTTPS URL of the MCP server. |
auth_type |
string | Yes | One of: 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. |
Credential shapes by auth_type:
auth_type |
auth_credential shape |
|---|---|
bearer_token |
{ "token": "your-bearer-token" } |
api_key |
{ "key": "your-api-key", "header": "x-api-key" } |
oauth |
{ "access_token": "your-access-token" } |
none |
Omit auth_credential or pass {} |
Response (201):
{
"connection": {
"id": "conn_a1b2c3d4-...",
"display_name": "my-mcp-server",
"label": "My MCP Server",
"scope": "org",
"workspace_id": null,
"server_url": "https://mcp.example.com/sse",
"transport": "sse",
"auth_type": "bearer_token",
"status": "pending",
"created_at": "2026-04-25T12:00:00Z"
}
}
status starts as pending. Call POST /api/rigor/connections/{id}/test to promote it to active.
Errors:
400 — invalid or HTTPS-only violation on server_url.400 — missing required fields (display_name, server_url, auth_type, scope).400 — display_name does not match [a-z0-9_-]+ or exceeds 32 characters.409 — duplicate server_url within the same scope; existing_id returned. The same URL can exist once at org scope and once per workspace.409 — maximum 10 connections per organization.501 feature_not_enabledcurl (Windows):
curl -X POST https://plith.ai/api/rigor/connections -H "x-api-key: YOUR_KEY" -H "Content-Type: application/json" -d "{\"display_name\": \"my-mcp-server\", \"scope\": \"org\", \"server_url\": \"https://mcp.example.com/sse\", \"auth_type\": \"bearer_token\", \"auth_credential\": {\"token\": \"my-token\"}, \"transport\": \"sse\"}"
Auth: Required (x-api-key)
Cost: Free
Returns all connections visible to your organization and workspace, plus providers available for new connections. Credentials are never included in the response.
Response (200):
{
"connections": [
{
"id": "conn_a1b2c3d4-...",
"display_name": "my-mcp-server",
"label": "My MCP Server",
"scope": "org",
"workspace_id": null,
"server_url": "https://mcp.example.com/sse",
"transport": "sse",
"auth_type": "bearer_token",
"capabilities": null,
"status": "active",
"last_tested_at": "2026-04-25T12:05:00Z",
"created_at": "2026-04-25T12:00:00Z",
"updated_at": "2026-04-25T12:05:00Z"
}
],
"count": 1,
"available_providers": [
{
"id": "slack",
"name": "Slack",
"description": "Read messages, channels, and files from Slack workspaces.",
"icon": "slack",
"tier": "standard"
}
]
}
Connection object fields:
| Field | Type | Description |
|---|---|---|
display_name |
string | Slug-safe identifier used in tool namespacing (mcp__{display_name}__{tool}). |
scope |
"org" | "workspace" |
Visibility scope. Org-scoped connections are visible to all workspaces. |
workspace_id |
string | null | UUID of the owning workspace for workspace-scoped connections; null for org-scoped. |
Top-level response fields:
| Field | Type | Description |
|---|---|---|
connections |
object[] | All connections visible to this org/workspace. |
count |
number | Length of the connections array. |
available_providers |
object[] | Providers available for OAuth connection but not yet connected. Each: { id, name, description, icon, tier }. |
Connection status values:
status |
Meaning |
|---|---|
pending |
Registered but not yet tested. |
active |
Last test passed — safe to use in workflows. |
error |
Last test failed (DNS check failed or connection refused). |
revoked |
Deleted — credentials cleared, no longer usable. |
capabilities is null until a connection test completes. After testing it may contain the server's declared MCP capability set.
curl (Windows):
curl https://plith.ai/api/rigor/connections -H "x-api-key: YOUR_KEY"
Auth: Required (x-api-key) — minimum role: admin
Cost: Free
Updates a registered connection. Use to update credentials, label, transport, or permission mode.
When updating permission_mode to "read_write" for an OAuth provider that requires additional scopes, the response may indicate requires_reauth: true and require you to re-initiate the OAuth flow.
Request body:
Send only the fields you want to change. All fields are optional.
| Field | Type | Description |
|---|---|---|
display_name |
string | New slug-safe identifier. Cannot be changed after the connection's first tool call in a workflow execution. |
label |
string | New display name. |
auth_credential |
object | Updated credential (same shapes as POST). |
permission_mode |
"read" | "read_write" |
Change the permission scope. |
Response (200): Updated connection object (same shape as POST response).
Response (200) — re-auth required:
{
"requires_reauth": true,
"reauth_provider": "github",
"message": "GitHub requires re-authorization to grant write permissions."
}
Errors:
401 unauthorized403 forbidden — requires admin role.400 invalid_body404 not_found501 feature_not_enabledcurl (Windows):
curl -X PATCH https://plith.ai/api/rigor/connections/conn_a1b2c3d4-... -H "x-api-key: YOUR_KEY" -H "Content-Type: application/json" -d "{\"label\": \"Updated MCP Server\"}"
Auth: Required (x-api-key)
Cost: Free
Tests connectivity to the registered MCP server. Performs a DNS-level SSRF check — validates that the hostname does not resolve to a private or reserved IP. On success, the connection's status is updated to active and last_tested_at is recorded.
Call this once after registration and again whenever you suspect the server URL's destination may have changed.
Request: No body. Pass the connection ID in the URL path.
Response (200 — test passed):
{
"status": "active",
"connection_id": "conn_a1b2c3d4-...",
"resolved_ips": ["93.184.216.34"],
"message": "DNS validation passed."
}
Response (400 — SSRF check failed):
{
"error": "SSRF check failed: mcp.example.internal resolves to blocked IP: IP 10.0.0.1 is in blocked range: private"
}
When an SSRF check fails, the connection's status is also updated to error.
Errors:
401 unauthorized400 — SSRF check failed.404 — connection not found.501 feature_not_enabledcurl (Windows):
curl -X POST https://plith.ai/api/rigor/connections/conn_a1b2c3d4-.../test -H "x-api-key: YOUR_KEY"
Auth: Required (x-api-key) — minimum role: admin
Cost: Free
Revokes a connection. This is a soft delete: the record is retained for audit purposes, status is set to revoked, and all stored credentials are cleared. A revoked connection cannot be used in workflow executions.
Request: No body. Pass the connection ID in the URL path.
Response (200):
{
"connection": {
"id": "conn_a1b2c3d4-...",
"status": "revoked"
},
"message": "Connection revoked"
}
Errors:
401 unauthorized404 — connection not found.501 feature_not_enabledcurl (Windows):
curl -X DELETE https://plith.ai/api/rigor/connections/conn_a1b2c3d4-... -H "x-api-key: YOUR_KEY"
| Control | What it does |
|---|---|
| Credential encryption | Stored with AES-256-GCM. Credentials are never returned in any API response. |
| SSRF protection — registration | Server URL validated at registration: HTTPS only, no private IPs, no cloud metadata endpoints, no internal hostnames. |
| SSRF protection — test | DNS resolved at test time: all A/AAAA records checked against a blocklist before the connection is marked active. |
| Read-only enforcement | Only resources/read and resources/list MCP operations are ever executed. tools/call, prompts/*, sampling/*, and all write operations are blocked regardless of server capabilities. |
| Org isolation | Every request is filtered by your organization's ID, derived from your API key. You cannot access another organization's connections. |
| Scope isolation | Org-scoped connections are visible to all workspaces. Workspace-scoped connections are visible only to their designated workspace. |
| Content sanitization | External MCP content is wrapped in containment framing and scanned for prompt-injection patterns before being passed to workflow execution. |
Limits:
| Limit | Value |
|---|---|
| Max connections per organization | 10 |
| Duplicate server URLs | One per scope (same URL allowed once at org scope + once per workspace) |
| Accepted protocols | HTTPS only |
Connect to Plith via MCP at:
/api/mcp/rigorThese tools appear in tools/list and can be autodiscovered by MCP clients:
| 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. |
These tools are 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 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: Review plan, then 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 status is "completed"
GET /api/rigor/status/wr_...
→ { workflow: { status: "completed", deliverable: { ... } } }
Receive results pushed to your endpoint instead of polling.
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: "..." }
Store webhook_secret and use it to verify incoming payloads via HMAC-SHA256.
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 with a partial deliverable
POST /api/rigor/execute
{ "workflow_id": "wr_...", "delivery": { "method": "polling" } }
# Poll until status === "pending_approval"
GET /api/rigor/status/wr_...
# Approve or reject
POST /api/rigor/approve
{ "workflow_id": "wr_...", "decision": "approved", "notes": "Looks good" }
# workflow automatically resumes after approval
Drive execution one step at a time.
# Execute in interactive mode
POST /api/rigor/execute
{ "task_description": "...", "mode": "interactive" }
→ { status: "interactive_waiting", sequence: [...] }
# Drive each step
POST /api/rigor/step
{ "workflow_id": "wr_...", "action": "next" }
→ { status: "step_executing", poll_url: "..." }
# Poll until step completes (status returns to "interactive_waiting")
GET /api/rigor/status/wr_...
# Skip a step
POST /api/rigor/step
{ "workflow_id": "wr_...", "action": "skip" }
# Abort early
POST /api/rigor/step
{ "workflow_id": "wr_...", "action": "abort" }
Interactive sessions expire after 30 minutes of inactivity.
Inherit context from a completed workflow into a new one.
# First workflow completes
GET /api/rigor/status/wr_parent...
→ { status: "completed" }
# Chain a new workflow that inherits prior context
POST /api/rigor/execute
{
"task_description": "Implement the solution from the prior design",
"prior_workflow_id": "wr_parent...",
"context_fields": ["deliverable", "quality_review"]
}
Chain depth limit: 10 links.
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\"}"
Response:
{
"ok": true,
"plan": {
"workflow_id": "wr_2a4b6c8d1f3e5a7b9c0d1e2f",
"classification": {
"task_type": "solution_design",
"value_class": "operational"
},
"sequence": [
{ "step": 1, "name": "Requirements Analysis", "estimated_credits": 80, "priority_class": "must" },
{ "step": 2, "name": "Research", "estimated_credits": 120, "priority_class": "should" },
{ "step": 3, "name": "Solution Design", "estimated_credits": 240, "priority_class": "must" },
{ "step": 4, "name": "Review (Sonnet)", "estimated_credits": 120, "priority_class": "must" }
],
"cost": {
"credits": 560,
"usd": 0.56,
"range": { "estimated_min": 420, "estimated_max": 700 }
},
"alternatives": {
"quick": {
"steps": 3,
"cost": { "credits": 280, "range": { "estimated_min": 210, "estimated_max": 350 } },
"skips": ["Research"],
"review_model": "Haiku",
"includes_verification": false,
"tradeoff": "Fewer steps, skips verification."
},
"thorough": {
"steps": 6,
"cost": { "credits": 920, "range": { "estimated_min": 690, "estimated_max": 1150 } },
"adds": ["Assumption Verification", "Gap Analysis"],
"review_model": "Sonnet + Opus",
"includes_verification": true,
"tradeoff": "More verification steps and multi-model review."
}
},
"info": ["Review: Haiku + Sonnet"],
"warnings": [],
"rigor_level": "standard"
},
"generated_title": "Caching Layer Design — API Gateway"
}
curl -X POST https://plith.ai/api/rigor/execute -H "x-api-key: YOUR_KEY" -H "Content-Type: application/json" -d "{\"workflow_id\": \"wr_2a4b6c8d1f3e5a7b9c0d1e2f\", \"delivery\": {\"method\": \"polling\"}}"
Response:
{
"ok": true,
"workflow_id": "wr_2a4b6c8d1f3e5a7b9c0d1e2f",
"status": "queued",
"task_type": "solution_design",
"delivery_mode": "polling",
"poll_url": "/api/rigor/status/wr_2a4b6c8d1f3e5a7b9c0d1e2f"
}
curl https://plith.ai/api/rigor/status/wr_2a4b6c8d1f3e5a7b9c0d1e2f -H "x-api-key: YOUR_KEY"
Response when complete:
{
"ok": true,
"workflow": {
"workflow_id": "wr_2a4b6c8d1f3e5a7b9c0d1e2f",
"status": "completed",
"task_type": "solution_design",
"value_class": "operational",
"current_step": 4,
"total_steps": 4,
"actual_steps_executed": 4,
"completed_step_summaries": [
{ "step": 1, "tool": "Requirements Analysis", "summary": "Captured 7 functional and 4 non-functional requirements." },
{ "step": 2, "tool": "Research", "summary": "Compared Redis, Memcached, Hazelcast for the gateway use case." },
{ "step": 3, "tool": "Solution Design", "summary": "Two-tier cache with Redis primary and in-process LRU." },
{ "step": 4, "tool": "Review (Sonnet)", "summary": "Score 92 — minor gaps on observability." }
],
"quality_score": 92,
"quality_review": {
"score": 92,
"summary": "Strong design with clear trade-offs. Add concrete latency targets and observability hooks to reach 100.",
"path_to_100": [
"Add p50/p95/p99 latency targets per cache tier.",
"Specify observability metrics emitted by the Solution Design hook points."
]
},
"deliverable": {
"title": "Solution Design — Rigor Workflow Output",
"sections": [
{ "heading": "Requirements Analysis", "content": "..." },
{ "heading": "Research", "content": "..." },
{ "heading": "Solution Design", "content": "..." },
{ "heading": "Review (Sonnet)", "content": "..." }
]
},
"credits_used": 545,
"error": null
}
}
By default, Rigor executes each plan step as a separate LLM call. Intermediate outputs are available via SSE events and GET /api/rigor/status as execution progresses.
Set preferences.execution: "direct" to request single-pass execution: all plan steps are composed into a single LLM call and executed together.
Direct mode:
execution_fallback: true appears in the plan response.require_approval: true or interactive mode (mode: "interactive"). See below.Incompatibility with approval gates: Direct execution cannot be combined with require_approval: true or interactive mode (mode: "interactive"). If both are requested, the system honors the approval preference and falls back to default multi-call execution. The response will include execution_fallback: true.
This is because direct mode executes all framework steps in a single LLM call with no intermediate outputs — there are no steps to pause between for approval.
When allowed_modes in the plan response does not contain "direct", the workflow is not eligible for direct mode regardless of the preferences.execution field.
All endpoints return errors in this shape:
{
"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 — use it when contacting support.
| Code | HTTP | Message (summary) | Fix |
|---|---|---|---|
missing_api_key |
401 | Provide your Plith API key via the x-api-key header. |
POST /api/keys |
invalid_api_key |
401 | API key not found or revoked. | GET /api/keys/verify |
unauthorized |
401 | Authentication required. | POST /api/keys |
forbidden |
403 | This resource belongs to a different organization. | — |
| Code | HTTP | Message (summary) | Fix |
|---|---|---|---|
invalid_body |
400 | Request body must be valid JSON. | — |
missing_field |
400 | <field> is required. |
— |
invalid_field |
400 | Invalid value for <field>: <reason>. |
— |
invalid_task_description |
400 | Must contain at least 2 words. | Provide a clear description. |
vague_task_description |
400 | Could not confidently classify this task. | Add more detail about the specific task, system, or deliverable. |
input_too_long |
400 | Input exceeds maximum length. | — |
| Code | HTTP | Message (summary) | Fix |
|---|---|---|---|
rate_limit_exceeded |
429 | Rate limit exceeded. Retry in <n> seconds. |
Wait <n> seconds; check Retry-After header. |
concurrent_limit_exceeded |
429 | You have N active workflows (limit: N). | Wait for a workflow to complete; poll GET /api/rigor/status/{id}. |
| Code | HTTP | Message (summary) | Fix |
|---|---|---|---|
not_found |
404 | Workflow not found. | — |
no_plan |
400 | Workflow has no plan. | POST /api/rigor/plan first. |
insufficient_credits |
402 | Insufficient credits. | POST /api/billing/checkout. |
workspace_budget_exceeded |
402 | Workflow cost would exceed workspace budget. | Adjust budget via PUT /api/workspaces/{id}/budget or reduce plan. |
workflow_has_progress |
409 | Workflow has completed steps; use /resume. |
POST /api/rigor/resume. |
invalid_status |
409 | Cannot perform this action on workflow with status "<current>". |
— |
duplicate_task |
409 | Identical task submitted in the last 5 minutes. | GET /api/rigor/status/<existing_workflow_id>. |
already_complete |
409 | All steps have results; nothing to resume. | — |
resume_conflict |
409 | Another resume is already in progress. | Wait and check status. |
pending_human_review |
409 | Workflow is pending human review. | POST /api/rigor/approve. |
| Code | HTTP | Message (summary) | Fix |
|---|---|---|---|
prior_not_found |
404 | Prior workflow not found. | — |
prior_not_terminal |
409 | Prior workflow must be in a terminal state. | Wait for it to complete. |
chain_depth_exceeded |
422 | Maximum workflow chain depth (10) reached. | — |
context_too_large |
422 | Inherited context exceeds maximum allowed size. | Use context_fields to restrict inheritance. |
| Code | HTTP | Message (summary) | Fix |
|---|---|---|---|
webhook_url_required |
400 | webhook_url is required for webhook delivery. |
— |
webhook_url_https |
400 | webhook_url must use HTTPS. |
— |
webhook_url_invalid |
400 | webhook_url is not a valid URL. |
— |
| Code | HTTP | Message (summary) | Fix |
|---|---|---|---|
session_expired |
410 | Interactive session expired (30-min TTL). | POST /api/rigor/resume to restart. |
concurrent_step |
409 | Another step execution is in progress. | Retry in 2–3 seconds. |
invalid_action |
400 | action must be next, skip, or abort. |
— |
| Code | HTTP | Message (summary) | Fix |
|---|---|---|---|
feature_not_enabled |
501 | MCP connections feature is not enabled. | — |
| Code | HTTP | Message (summary) | Fix |
|---|---|---|---|
no_workflow_ids |
400 | workflow_ids array is required and must not be empty. |
Provide an array of workflow ID strings. |
bulk_limit_exceeded |
400 | workflow_ids exceeds 20. |
Split into batches of 20. |
| Code | HTTP | Message (summary) | Fix |
|---|---|---|---|
plan_timeout |
504 | Planning exceeded the time limit. | Simplify or split the task description. |
| Code | HTTP | Message (summary) | Fix |
|---|---|---|---|
provider_exhausted |
503 | All configured LLM providers failed for this step. | Check provider status dashboards; contact support if persistent. |
fallback_triggered |
200 | Primary LLM provider failed; step completed using fallback provider. | Informational — no action needed. |
| Code | HTTP | Message (summary) | Fix |
|---|---|---|---|
deletion_already_pending |
409 | A deletion request is already pending for this org. | — |
deletion_rate_limited |
429 | Deletion requests are limited to 5 per org per day. | Retry after midnight UTC. |
invalid_deletion_request |
404 | Deletion request not found, already processed, or confirmation_token does not match. |
— |
deletion_failed |
500 | Deletion partially executed. | Contact support with your request_id. |
| Code | HTTP | Message (summary) | Fix |
|---|---|---|---|
internal_error |
500 | An internal error occurred. | Retry; include request_id when contacting support. |
queue_failed |
500 | Failed to queue workflow for execution. | Retry. |
approve_failed |
500 | Failed to process approval decision. | Retry. |
Every successful response includes these metadata fields:
| Field | Type | Description |
|---|---|---|
request_id |
string | Unique request identifier — always req_ followed by 24 hex characters. Include this when contacting support. |
credits_used |
number | Credits deducted by this request. 0 for free endpoints. |
credits_remaining |
number | Your organization's balance after this request. |
fallback_behavior |
string | What the system did when an upstream dependency was degraded — e.g. "none" when everything worked normally. |
Note:
GET /api/keys/verifyalways reportscredits_remaining: 0— it does not query the live balance. UseGET /api/billing/balancefor the actual balance.
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.
Authentication, API keys, billing, workspaces, notifications, and GDPR endpoints.
Base URL:
https://plith.aiRigor workflow endpoints are documented in EXTERNAL-RIGOR-API-REFERENCE.md.
Plith supports two authentication mechanisms:
x-api-key header) — for programmatic access and all Rigor endpoints.Most API consumers use API keys. Session-based endpoints exist for signup, login, and OAuth flows.
Do NOT use Authorization: Bearer format — that is not supported for API authentication.
Auth: None Cost: Free
Creates a user account and returns a session. Sets a session cookie on success.
Request body:
| Field | Type | Required | Description |
|---|---|---|---|
email |
string | Yes | User email |
password |
string | Yes | Minimum 8 characters |
name |
string | No | Display name |
Response (201):
{
"user_id": "uuid",
"email": "user@example.com",
"session": {
"access_token": "...",
"refresh_token": "...",
"expires_at": 1234567890
}
}
Errors:
400 invalid_body — missing required fields.409 email_already_registered — email already exists.500 signup_failedAuth: None Cost: Free
Authenticates with email and password. Sets a session cookie.
Request body:
| Field | Type | Required | Description |
|---|---|---|---|
email |
string | Yes | Registered email |
password |
string | Yes | Account password |
Response (200):
{
"user_id": "uuid",
"email": "user@example.com",
"session": {
"access_token": "...",
"refresh_token": "...",
"expires_at": 1234567890
}
}
Errors:
401 invalid_credentials — email/password mismatch.400 invalid_body — missing required fields.Auth: Session cookie Cost: Free
Signs out the current user and clears the session cookie.
Response (200): { "success": true }
Auth: None (OAuth / magic-link callback) Cost: Free
Exchanges an authorization code from an OAuth or magic-link flow for a session. Redirects to the app on success.
Query parameters:
| Parameter | Description |
|---|---|
code |
Auth code |
next |
Optional redirect path after login (default: /dashboard) |
Response: HTTP 302 redirect.
Errors:
400 — missing or invalid code.Auth: Required (x-api-key) — minimum role: admin
Cost: Free
Starts an OAuth flow for an MCP provider (e.g. GitHub). Returns an authorization URL to redirect the user to.
Request body:
| Field | Type | Required | Description |
|---|---|---|---|
provider |
string | Yes | Provider identifier (e.g. "github"). |
permission_mode |
"read" | "read_write" |
No | Scope of access. Default "read". |
Response (200):
{
"authorization_url": "https://github.com/login/oauth/authorize?..."
}
Errors:
401 unauthorized403 forbidden — requires admin role.400 — provider is required or not available for OAuth.400 — invalid permission_mode.curl (Windows):
curl -X POST https://plith.ai/api/auth/mcp/initiate -H "x-api-key: YOUR_KEY" -H "Content-Type: application/json" -d "{\"provider\": \"github\", \"permission_mode\": \"read\"}"
Auth: None (public — server-side redirect) Cost: Free
OAuth callback for MCP provider authentication. After the user authorizes the provider, this endpoint receives the code, stores the token, and redirects the user back to the app. You do not call this directly.
Auth: None — public endpoint. Cost: Free
Returns service availability and product health. Use this endpoint to check whether the platform is operational before making API calls.
Response (200):
{
"status": "ok",
"timestamp": "2026-05-18T12:00:00.000Z",
"services": {
"database": "ok",
"cache": "ok"
},
"products": {
"dedupq": "ok",
"burnrate": "ok",
"qualitygate": "ok",
"guardrail": "ok",
"pitfalldb": "ok",
"rigor": "ok"
},
"version": "2026-05-18"
}
curl (Windows):
curl https://plith.ai/api/health
Auth: None — public endpoint. Cost: Free Rate limit: 3 keys per IP per 24 hours.
Creates a new organization and API key pair. The raw key is returned once and never stored in plaintext.
Request body:
| Field | Type | Required | Description |
|---|---|---|---|
email |
string | No | Contact email for the org |
name |
string | No | Display name (defaults to email, then "Unnamed org") |
Response (201):
{
"org_id": "a1b2c3d4-...",
"key": "plth_live_xxxxxxxxxxxx...",
"prefix": "plth_live_xxx",
"message": "Store this key securely — it will not be shown again.",
"quickstart": "https://plith.ai/docs/quickstart",
"docs": "https://plith.ai/docs/authentication"
}
Errors:
429 rate_limited — IP exceeded 3 keys per 24 hours. Response includes retry_after_seconds: 86400.409 duplicate_email — org with that email already exists.500 db_errorcurl (Windows):
curl -X POST https://plith.ai/api/keys -H "Content-Type: application/json" -d "{\"email\": \"you@example.com\", \"name\": \"My Org\"}"
Auth: Required (x-api-key)
Cost: Free
Confirms an API key is valid and returns the associated org context.
Response (200):
{
"org_id": "a1b2c3d4-...",
"is_paid": true,
"request_id": "req_abc123def456...",
"credits_used": 0,
"credits_remaining": 0,
"fallback_behavior": "none"
}
credits_remainingis always0on this endpoint — it does not query the live balance. UseGET /api/billing/balancefor your actual balance.
Errors:
401 unauthorized — missing or invalid key.curl (Windows):
curl https://plith.ai/api/keys/verify -H "x-api-key: YOUR_KEY"
Auth: Session cookie or x-api-key
Cost: Free
Returns the authenticated user's profile and workspace memberships.
Response (200):
{
"user_id": "uuid",
"email": "user@example.com",
"display_name": "Alice",
"memberships": [
{ "workspace_id": "uuid", "workspace_name": "Acme Corp", "role": "admin" }
]
}
Errors:
401 unauthorized — no valid session or API key.curl (Windows):
curl https://plith.ai/api/user/me -H "x-api-key: YOUR_KEY"
Auth: Required (x-api-key)
Cost: Free
Returns the current credit balance for the authenticated organization.
Response (200):
{
"balance": 9484,
"org_id": "a1b2c3d4-...",
"request_id": "req_abc123def456...",
"credits_used": 0,
"credits_remaining": 9484,
"fallback_behavior": "none"
}
Errors:
401 unauthorized500 internal_errorcurl (Windows):
curl https://plith.ai/api/billing/balance -H "x-api-key: YOUR_KEY"
Auth: Required (x-api-key)
Cost: Free
Creates a Stripe Checkout session for purchasing credits. Exactly one of pack or custom_amount must be provided.
Preset pack request:
| Field | Type | Required | Description |
|---|---|---|---|
pack |
number | Yes | One of 10, 25, 100, 250 (USD price). |
Custom amount request:
| Field | Type | Required | Description |
|---|---|---|---|
custom_amount |
integer | Yes | USD amount, integer between 5 and 10,000 inclusive. |
Both options award 1,000 credits per dollar.
Response (200):
{
"url": "https://checkout.stripe.com/pay/cs_live_..."
}
Redirect the user to this URL to complete payment.
Errors:
400 invalid_body — both or neither of pack / custom_amount provided.400 invalid_pack — not in [10, 25, 100, 250]. Response includes available.400 invalid_custom_amount — not an integer or outside [5, 10000]. Response includes min and max.401 missing_api_key503 billing_unavailable — Stripe not configured.curl (Windows) — preset pack:
curl -X POST https://plith.ai/api/billing/checkout -H "x-api-key: YOUR_KEY" -H "Content-Type: application/json" -d "{\"pack\": 25}"
curl (Windows) — custom amount:
curl -X POST https://plith.ai/api/billing/checkout -H "x-api-key: YOUR_KEY" -H "Content-Type: application/json" -d "{\"custom_amount\": 50}"
Auth: x-api-key or session
Cost: Free
Creates a Stripe Checkout session for a recurring subscription.
Request body:
| Field | Type | Required | Description |
|---|---|---|---|
tier |
string | Yes | One of pro, team, enterprise. |
interval |
string | No | monthly (default) or annual. |
Response (200): { "url": "https://checkout.stripe.com/pay/cs_live_..." }
Errors:
400 invalid_tier400 invalid_interval409 already_subscribed — org already has an active subscription; use POST /api/billing/portal to manage it.503 billing_unavailablecurl (Windows):
curl -X POST https://plith.ai/api/billing/subscribe -H "x-api-key: YOUR_KEY" -H "Content-Type: application/json" -d "{\"tier\": \"team\", \"interval\": \"monthly\"}"
Auth: x-api-key or session
Cost: Free
Creates a Stripe Customer Portal session to manage your subscription (cancel, upgrade, update payment method).
Response (200): { "url": "https://billing.stripe.com/session/..." }
Redirect the user to this URL.
Errors:
400 no_billing_account — no billing account found; purchase credits or a subscription first.401 unauthorized503 billing_unavailablecurl (Windows):
curl -X POST https://plith.ai/api/billing/portal -H "x-api-key: YOUR_KEY"
Auth: Required (x-api-key)
Cost: Free
Returns the current auto-reload configuration for your organization.
Response (200):
{
"enabled": true,
"threshold": 500,
"amount": 5000,
"monthly_limit": 100,
"monthly_spent": 45,
"reset_at": "2026-06-01T00:00:00Z"
}
| Field | Description |
|---|---|
enabled |
Whether auto-reload is active. |
threshold |
Credit balance that triggers a reload. null if not set. |
amount |
Credits added per reload. null if not set. |
monthly_limit |
Monthly USD cap on auto-reloads. null if not set. |
monthly_spent |
USD spent on auto-reloads this month. |
reset_at |
When monthly_spent resets. null if not set. |
curl (Windows):
curl https://plith.ai/api/billing/auto-reload -H "x-api-key: YOUR_KEY"
Auth: Required (x-api-key) — minimum role: admin
Cost: Free
Configures automatic credit reloads. When your balance drops below threshold, a Stripe charge for amount credits is triggered automatically. Requires a saved payment method from a prior purchase.
Request body:
| Field | Type | Required | Description |
|---|---|---|---|
enabled |
boolean | Yes | Enable or disable auto-reload. |
confirm |
boolean | When enabling | true required to acknowledge the automatic charge authorization. |
threshold |
integer | When enabling | Balance floor that triggers a reload. Range: 100–100,000 credits. |
amount |
integer | When enabling | Credits to add per reload. One of: 5,000 / 25,000 / 100,000 / 250,000. |
monthly_limit |
integer | No | Monthly USD cap on auto-reloads. Range: 5–10,000. |
Response (200): Updated auto-reload configuration (same shape as GET).
Errors:
401 unauthorized403 forbidden — requires admin role.400 invalid_body — enabled=true with missing or invalid fields.curl (Windows) — enable:
curl -X PUT https://plith.ai/api/billing/auto-reload -H "x-api-key: YOUR_KEY" -H "Content-Type: application/json" -d "{\"enabled\": true, \"confirm\": true, \"threshold\": 500, \"amount\": 5000}"
curl (Windows) — disable:
curl -X PUT https://plith.ai/api/billing/auto-reload -H "x-api-key: YOUR_KEY" -H "Content-Type: application/json" -d "{\"enabled\": false}"
Auth: Stripe-Signature header (do not call this endpoint directly) Cost: Free
Stripe webhook receiver. Called by Stripe to notify your organization of subscription lifecycle events. This endpoint verifies the Stripe signature and updates your subscription status.
Handled events:
| Event | Action |
|---|---|
customer.subscription.created |
Activates subscription, sets tier. |
customer.subscription.updated |
Updates tier and status. |
customer.subscription.deleted |
Sets status to cancelled, reverts tier to free. |
invoice.paid |
Confirms subscription active. |
invoice.payment_failed |
Sets status to past_due. |
Response (200): { "received": true }
Errors:
400 invalid_signature — HMAC verification failed.All workspace endpoints require the caller to be a member of the specified workspace. Role requirements are noted per endpoint.
Roles (from lowest to highest): viewer → member → admin → owner.
Auth: Required (x-api-key or session)
Cost: Free
Lists all workspaces for your organization.
Query parameters:
| Parameter | Description |
|---|---|
include_archived |
true to include archived workspaces. Default false. |
Response (200):
{
"workspaces": [
{
"id": "uuid",
"name": "Default",
"slug": "default",
"is_default": true,
"archived_at": null,
"created_at": "2026-05-01T00:00:00Z"
}
]
}
curl (Windows):
curl https://plith.ai/api/workspaces -H "x-api-key: YOUR_KEY"
Auth: Required (x-api-key) — Team or Enterprise only.
Cost: Free
Creates a new workspace.
Request body:
| Field | Type | Required | Description |
|---|---|---|---|
name |
string | Yes | Workspace display name. |
Response (201): Full workspace object.
Errors:
403 tier_required — Team or Enterprise subscription required.403 workspace_limit_reached — tier workspace limit reached.400 missing_field — name required.curl (Windows):
curl -X POST https://plith.ai/api/workspaces -H "x-api-key: YOUR_KEY" -H "Content-Type: application/json" -d "{\"name\": \"My New Workspace\"}"
Auth: Required (x-api-key or session, any role)
Cost: Free
Returns workspace details including LLM policy status and budget information (when configured).
Response (200):
{
"id": "uuid",
"name": "Default",
"slug": "default",
"is_default": true,
"archived_at": null,
"created_at": "2026-05-01T00:00:00Z",
"llm_policy": null,
"enforcement_status": null,
"account_tier": "team",
"budget": {
"monthly_credit_budget": 50000,
"monthly_credits_used": 12400,
"budget_remaining": 37600,
"budget_reset_at": "2026-06-01T00:00:00Z"
}
}
budget is only present when a budget is configured and the tier supports it. llm_policy is null when no policy is set.
Errors:
401 unauthorized404 not_foundcurl (Windows):
curl https://plith.ai/api/workspaces/uuid -H "x-api-key: YOUR_KEY"
Auth: Required (x-api-key or session) — minimum role: admin
Cost: Free
Updates workspace settings.
Request body: Send only the fields you want to change.
| Field | Type | Description |
|---|---|---|
name |
string | New workspace display name. |
Response (200): Updated workspace object.
Errors:
401 unauthorized403 forbidden — requires admin role.404 not_foundcurl (Windows):
curl -X PUT https://plith.ai/api/workspaces/uuid -H "x-api-key: YOUR_KEY" -H "Content-Type: application/json" -d "{\"name\": \"Renamed Workspace\"}"
Auth: Required (x-api-key or session) — minimum role: admin
Cost: Free
Archives the workspace (soft delete). Archived workspaces are not deleted; they can be retrieved with include_archived=true.
Response (200): { "success": true }
Errors:
401 unauthorized403 forbidden — requires admin role.404 not_foundcurl (Windows):
curl -X DELETE https://plith.ai/api/workspaces/uuid -H "x-api-key: YOUR_KEY"
Auth: x-api-key or session (any role)
Cost: Free
Lists all members of the workspace, including API key members.
Response (200):
{
"members": [
{
"id": "uuid",
"type": "user",
"email": "alice@example.com",
"name": "Alice",
"role": "owner",
"created_at": "2026-05-01T00:00:00Z"
},
{
"id": "uuid",
"type": "api_key",
"key_prefix": "plth_live_xxx",
"role": "member",
"created_at": "2026-05-02T00:00:00Z"
}
]
}
curl (Windows):
curl https://plith.ai/api/workspaces/uuid/members -H "x-api-key: YOUR_KEY"
Auth: x-api-key or session — requires admin or owner
Cost: Free
Invites a user to the workspace. If the user exists, they are added directly. For new emails, an invite token is created and an email is sent.
Request body:
| Field | Type | Required | Description |
|---|---|---|---|
email |
string | Yes | Email of person to invite. |
role |
string | Yes | One of viewer, member, admin. |
Response (201): { "invite_id": "...", "email": "...", "role": "...", "expires_at": "..." }
Errors:
403 forbidden — caller is not admin or owner.409 already_member — user is already a member.402 member_limit_reached — tier member limit reached.curl (Windows):
curl -X POST https://plith.ai/api/workspaces/uuid/members -H "x-api-key: YOUR_KEY" -H "Content-Type: application/json" -d "{\"email\": \"bob@example.com\", \"role\": \"member\"}"
Auth: x-api-key or session — requires owner
Cost: Free
Updates a workspace member's role. Owners cannot demote their own role.
Request body:
| Field | Type | Required | Description |
|---|---|---|---|
role |
string | Yes | New role: viewer, member, or admin. |
Response (200): { "member_id": "...", "role": "..." }
Errors:
403 forbidden — requires owner role.404 not_found400 cannot_demote_selfcurl (Windows):
curl -X PATCH https://plith.ai/api/workspaces/uuid/members/member-uuid -H "x-api-key: YOUR_KEY" -H "Content-Type: application/json" -d "{\"role\": \"viewer\"}"
Auth: x-api-key or session — requires admin or owner
Cost: Free
Removes a member from the workspace.
Response (200): { "success": true }
Errors:
403 forbidden404 not_found400 cannot_remove_last_owner — workspace must retain at least one owner.curl (Windows):
curl -X DELETE https://plith.ai/api/workspaces/uuid/members/member-uuid -H "x-api-key: YOUR_KEY"
Auth: Session cookie (must be logged in) Cost: Free
Accepts a workspace invite by token. The token is included in the invite email.
Request body:
| Field | Type | Required | Description |
|---|---|---|---|
token |
string | Yes | Invite token from the invite email. |
Response (200): { "workspace_id": "...", "workspace_name": "...", "role": "..." }
Errors:
404 invite_not_found — token not found or already used.410 invite_expired — invite has expired (default TTL: 7 days).401 unauthorized — must be logged in to accept.Auth: x-api-key or session — requires member or above
Cost: Free
Lists all API keys scoped to the workspace. Raw keys are never returned after creation — only prefixes.
Response (200):
{
"api_keys": [
{
"key_id": "uuid",
"prefix": "plth_live_xxx",
"label": "Production Key",
"created_at": "2026-05-01T00:00:00Z",
"last_used_at": "2026-05-10T00:00:00Z",
"created_by": "uuid"
}
]
}
curl (Windows):
curl https://plith.ai/api/workspaces/uuid/api-keys -H "x-api-key: YOUR_KEY"
Auth: x-api-key or session — requires admin or owner
Cost: Free
Creates a new workspace-scoped API key. The raw key is returned once.
Request body:
| Field | Type | Required | Description |
|---|---|---|---|
label |
string | No | Human-readable label for the key. |
Response (201): { "key_id": "...", "key": "plth_live_...", "prefix": "...", "label": "...", "created_at": "..." }
Errors:
403 forbidden — requires admin or owner.402 key_limit_reached — tier key limit reached.curl (Windows):
curl -X POST https://plith.ai/api/workspaces/uuid/api-keys -H "x-api-key: YOUR_KEY" -H "Content-Type: application/json" -d "{\"label\": \"Production Key\"}"
Auth: x-api-key or session — requires admin or owner
Cost: Free
Revokes a workspace API key. The key is invalidated immediately.
Response (200): { "success": true }
Errors:
403 forbidden404 not_foundcurl (Windows):
curl -X DELETE https://plith.ai/api/workspaces/uuid/api-keys/key-uuid -H "x-api-key: YOUR_KEY"
Auth: Required — Team/Enterprise only. GET requires any role; PUT requires admin or above.
Cost: Free
Get or update the workspace's allowed-model LLM policy. This controls which AI models Rigor workflows in this workspace may use.
GET response (200):
{
"workspace_id": "uuid",
"allowed_models": ["claude-sonnet-4-6", "claude-haiku-4-5"],
"enforcement_status": "active",
"updated_at": "2026-05-01T00:00:00Z"
}
PUT request body:
| Field | Type | Required | Description |
|---|---|---|---|
allowed_models |
string[] | Yes | List of allowed model identifiers. Pass null to remove the policy. |
PUT response (200): Updated policy object.
Errors:
403 tier_required — Team or Enterprise subscription required.403 forbidden — PUT requires admin role.404 not_foundcurl (Windows) — GET:
curl https://plith.ai/api/workspaces/uuid/llm-policy -H "x-api-key: YOUR_KEY"
curl (Windows) — PUT:
curl -X PUT https://plith.ai/api/workspaces/uuid/llm-policy -H "x-api-key: YOUR_KEY" -H "Content-Type: application/json" -d "{\"allowed_models\": [\"claude-sonnet-4-6\"]}"
Auth: x-api-key or session (any role)
Cost: Free
Returns the workspace's monthly credit budget and current-month spend.
Response (200):
{
"workspace_id": "uuid",
"monthly_credit_budget": 50000,
"current_month_spend": 12400,
"budget_utilization_pct": 24.8,
"reset_at": "2026-06-01T00:00:00Z"
}
monthly_credit_budget: null means no budget limit is set.
curl (Windows):
curl https://plith.ai/api/workspaces/uuid/budget -H "x-api-key: YOUR_KEY"
Auth: x-api-key or session — requires admin or owner
Cost: Free
Tier: Team or Enterprise only.
Sets the workspace's monthly credit budget soft limit. When a workflow's estimated cost would exceed the remaining budget, the workflow is blocked with 402 workspace_budget_exceeded.
Request body:
| Field | Type | Required | Description |
|---|---|---|---|
monthly_credit_budget |
integer | null | Yes | New budget in credits, or null to remove the limit. |
Response (200): { "workspace_id": "...", "monthly_credit_budget": 50000 }
Errors:
403 forbidden — requires admin or owner.403 tier_required — Team or Enterprise subscription required.400 invalid_body — budget must be a positive integer or null.curl (Windows):
curl -X PUT https://plith.ai/api/workspaces/uuid/budget -H "x-api-key: YOUR_KEY" -H "Content-Type: application/json" -d "{\"monthly_credit_budget\": 50000}"
Auth: Required (x-api-key or session, any role)
Cost: Free
Lists workflow folders for the workspace.
Response (200):
{
"folders": [
{ "id": "uuid", "name": "Engineering", "created_at": "2026-05-01T00:00:00Z" }
]
}
curl (Windows):
curl https://plith.ai/api/workspaces/uuid/folders -H "x-api-key: YOUR_KEY"
Auth: Required (x-api-key or session) — minimum role: admin
Cost: Free
Returns the audit log for the workspace — a record of administrative actions (key creation, member changes, policy updates, etc.).
Response (200):
{
"events": [
{
"id": "uuid",
"action": "workspace.member.added",
"actor": "alice@example.com",
"resource_type": "member",
"resource_id": "uuid",
"metadata": { "role": "admin" },
"created_at": "2026-05-01T00:00:00Z"
}
]
}
Errors:
401 unauthorized403 forbidden — requires admin role.curl (Windows):
curl https://plith.ai/api/workspaces/uuid/audit-log -H "x-api-key: YOUR_KEY"
Auth: Required (x-api-key or session) — minimum role: member
Cost: Free
Tier: Pro or above.
Lists workflow event webhooks registered for this workspace.
Response (200):
{
"webhooks": [
{
"id": "uuid",
"url": "https://your-server.example.com/hook",
"description": "Production notifier",
"events": ["workflow_completed", "workflow_failed"],
"active": true,
"created_at": "2026-05-01T00:00:00Z"
}
]
}
Auth: Required (x-api-key or session) — minimum role: admin
Cost: Free
Tier: Pro or above.
Registers a new webhook endpoint. The secret is shown once — store it securely for HMAC-SHA256 payload verification.
Request body:
| Field | Type | Required | Description |
|---|---|---|---|
url |
string (HTTPS) | Yes | Endpoint that will receive POST events. |
events |
string[] | Yes | One or more of: step_completed, step_failed, workflow_completed, workflow_failed, approval_requested, timed_out. |
description |
string | No | Human-readable label for this webhook. |
active |
boolean | No | Whether to enable immediately. Default true. |
Response (201):
{
"id": "uuid",
"url": "https://your-server.example.com/hook",
"description": "Production notifier",
"events": ["workflow_completed", "workflow_failed"],
"secret": "whsec_a3f7c2...",
"active": true,
"created_at": "2026-05-01T00:00:00Z"
}
Errors:
403 tier_required — Pro or above required.403 forbidden — requires admin role.400 invalid_body — missing url or events, or url is not HTTPS.curl (Windows):
curl -X POST https://plith.ai/api/workspaces/uuid/webhooks -H "x-api-key: YOUR_KEY" -H "Content-Type: application/json" -d "{\"url\": \"https://your-server.example.com/hook\", \"events\": [\"workflow_completed\"]}"
Auth: Required (x-api-key or session) — minimum role: admin
Cost: Free
Tier: Pro or above.
Updates a webhook's URL, events, description, or active state.
Request body: Send only fields to change (url, events, description, active).
Response (200): Updated webhook object (without secret).
Errors:
403 tier_required / 403 forbidden / 404 not_foundAuth: Required (x-api-key or session) — minimum role: admin
Cost: Free
Tier: Pro or above.
Permanently removes the webhook.
Response (200): { "ok": true }
Errors:
403 tier_required / 403 forbidden / 404 not_foundAuth: Required (x-api-key or session) — minimum role: admin
Cost: Free
Tier: Pro or above.
Sends a test payload to the webhook URL to verify connectivity.
Response (200): { "ok": true, "delivered": true, "status_code": 200 }
Errors:
403 tier_required / 403 forbidden / 404 not_foundAuth: Required (x-api-key or session) — minimum role: member
Cost: Free
Tier: Pro or above.
Verifies an incoming webhook payload signature. Pass the raw body and the X-Plith-Signature header value to confirm the payload was signed with the registered secret.
Request body:
| Field | Type | Required | Description |
|---|---|---|---|
payload |
string | Yes | Raw request body string as received. |
signature |
string | Yes | Value of the X-Plith-Signature header. |
Response (200): { "ok": true, "valid": true }
Auth: Required (x-api-key or session) — minimum role: admin
Cost: Free
Creates a shareable invite link. The recipient follows the link to join the workspace without needing a direct email.
Request body:
| Field | Type | Required | Description |
|---|---|---|---|
role |
"viewer" | "member" | "admin" |
Yes | Role to assign on accept. |
max_uses |
integer | No | How many times the link can be accepted. Default unlimited. |
expires_in_days |
integer | No | TTL in days. Default 7. |
Response (201):
{
"ok": true,
"invite_id": "uuid",
"token": "inv_abc123...",
"url": "https://plith.ai/invite/inv_abc123...",
"role": "member",
"expires_at": "2026-05-25T00:00:00Z",
"max_uses": null
}
Errors:
403 forbidden — requires admin or owner.curl (Windows):
curl -X POST https://plith.ai/api/workspaces/uuid/invites -H "x-api-key: YOUR_KEY" -H "Content-Type: application/json" -d "{\"role\": \"member\"}"
Auth: Required (x-api-key or session) — minimum role: admin
Cost: Free
Lists all active invite links for the workspace.
Response (200): { "ok": true, "invites": [ { "invite_id", "token", "role", "expires_at", "max_uses", "uses_remaining", "created_at" } ] }
curl (Windows):
curl https://plith.ai/api/workspaces/uuid/invites -H "x-api-key: YOUR_KEY"
Auth: Required (x-api-key or session) — minimum role: admin
Cost: Free
Revokes an invite link immediately. Any further attempts to use the token will fail.
Response (200): { "ok": true }
Errors:
403 forbidden / 404 not_foundAuth: Required (x-api-key or session) — minimum role: owner
Cost: Free
Tier: Team or Enterprise only.
Restores an archived workspace. Cannot restore a workspace that has been hard-deleted (returns 410).
Response (200): { "ok": true, "workspace_id": "uuid", "status": "active" }
Errors:
403 forbidden — requires owner role.403 tier_required — Team or Enterprise subscription required.400 not_archived — workspace is not in an archived state.410 gone — workspace has been permanently deleted and cannot be restored.curl (Windows):
curl -X POST https://plith.ai/api/workspaces/uuid/restore -H "x-api-key: YOUR_KEY"
Auth: Required (x-api-key or session)
Cost: Free
Returns notifications for the authenticated user, with optional filtering.
Query parameters:
| Parameter | Description |
|---|---|
unread_only |
true to return only unread notifications. |
limit |
Page size. Range: 1–50. Default 20. |
cursor |
Opaque cursor for the next page. |
Response (200):
{
"ok": true,
"notifications": [
{
"id": "uuid",
"type": "workflow_completed",
"title": "Workflow completed",
"body": "Your Solution Design workflow finished with quality score 92.",
"workflow_id": "wr_2a4b6c8d...",
"action_url": "/workflows/wr_2a4b6c8d...",
"action_label": "View results",
"metadata": {},
"read_at": null,
"created_at": "2026-05-08T12:05:00Z"
}
],
"unread_count": 1,
"has_more": false
}
curl (Windows):
curl "https://plith.ai/api/notifications?unread_only=true" -H "x-api-key: YOUR_KEY"
Auth: Required (x-api-key or session)
Cost: Free
Returns the unread notification count only. Suitable for polling.
Response (200):
{
"ok": true,
"unread_count": 3,
"request_id": "req_abc123..."
}
curl (Windows):
curl https://plith.ai/api/notifications/count -H "x-api-key: YOUR_KEY"
Auth: Required (x-api-key or session)
Cost: Free
Marks notifications as read. Pass specific IDs or all: true to mark everything read.
Request body:
| Field | Type | Required | Description |
|---|---|---|---|
notification_ids |
string[] | Conditional | IDs to mark read. Required unless all: true. |
all |
boolean | Conditional | true to mark all notifications read. |
Response (200):
{
"ok": true,
"updated": 3,
"request_id": "req_abc123..."
}
curl (Windows) — specific IDs:
curl -X PATCH https://plith.ai/api/notifications/read -H "x-api-key: YOUR_KEY" -H "Content-Type: application/json" -d "{\"notification_ids\": [\"uuid1\", \"uuid2\"]}"
curl (Windows) — mark all read:
curl -X PATCH https://plith.ai/api/notifications/read -H "x-api-key: YOUR_KEY" -H "Content-Type: application/json" -d "{\"all\": true}"
These endpoints allow you to request and confirm permanent erasure of all data associated with your organization.
Warning: Data deletion is irreversible. All workflows, keys, billing history, and org data will be permanently removed.
Rate limit: 5 deletion requests per organization per day.
Auth: Required (x-api-key)
Cost: Free
Returns the deletion request history for the organization.
Response (200):
{
"requests": [
{
"id": "uuid",
"status": "pending",
"requested_at": "2026-05-08T12:00:00Z",
"soft_deleted_at": null,
"hard_deleted_at": null,
"completed_at": null,
"deletion_log": null,
"error": null
}
]
}
curl (Windows):
curl https://plith.ai/api/data/delete-request -H "x-api-key: YOUR_KEY"
Auth: Required (x-api-key)
Cost: Free
Starts a GDPR erasure request. Returns a confirmation_token required to execute the deletion. Idempotent within a short window — returns the existing pending request if one was recently created.
Response (202):
{
"request_id": "uuid",
"confirmation_token": "tok_...",
"status": "pending",
"requested_at": "2026-05-08T12:00:00Z",
"warning": "This will permanently delete all data for your organization.",
"affected_data": ["workflows", "organization", "request_log"],
"next_step": "Call DELETE /api/data/confirm-deletion with request_id and confirmation_token to execute."
}
Errors:
409 deletion_already_pending — a deletion request is already pending.429 deletion_rate_limited — limited to 5 per org per day.curl (Windows):
curl -X POST https://plith.ai/api/data/delete-request -H "x-api-key: YOUR_KEY"
Auth: Required (x-api-key)
Cost: Free
Executes the cascading deletion. This action is irreversible. All org data is permanently deleted. Request logs are anonymized rather than deleted.
Request body:
| Field | Type | Required | Description |
|---|---|---|---|
request_id |
string | Yes | UUID from POST /api/data/delete-request. |
confirmation_token |
string | Yes | Token from POST /api/data/delete-request. |
Response (200): { "ok": true, "message": "Organization data has been permanently deleted." }
Errors:
404 invalid_deletion_request — request not found, already processed, or token mismatch.500 deletion_failed — deletion partially executed; contact support with your request_id.curl (Windows):
curl -X DELETE https://plith.ai/api/data/confirm-deletion -H "x-api-key: YOUR_KEY" -H "Content-Type: application/json" -d "{\"request_id\": \"uuid\", \"confirmation_token\": \"tok_...\"}"