Plith Rigor — API Reference

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.


Table of Contents


Authentication

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.

Anonymous mode (MCP only)

When connecting via MCP (Smithery, Claude Desktop, Cursor, etc.) without an API key, rigor_plan is available for evaluation against a demo org. Anonymous calls are rate-limited to 30 requests/minute and 500 requests/day per IP.


Quick Start

1. Get a key

curl -X POST https://plith.ai/api/keys -H "Content-Type: application/json" -d "{\"email\": \"you@example.com\", \"name\": \"My Org\"}"

Response (store 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."
}

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.

Credit packs

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.

How credits work

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.


Rate Limits

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.


Rigor Endpoints

Workflow Lifecycle

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

POST /api/rigor/plan

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"
}

Plan response shape

{
  "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 sequence and carries priority_class: "must". Its estimated cost (530 credits) is included in cost.credits.

Multi-intent response

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 unauthorized
  • 400 invalid_body
  • 400 missing_fieldtask_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_error

curl (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\"}"

POST /api/rigor/execute

Auth: Required (x-api-key) Cost: Variable — equals the plan's cost.credits.

Executes a workflow end-to-end. Two entry points:

  1. With 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).
  2. Without 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.

Response — polling or webhook delivery

{
  "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.

Response — SSE delivery (default)

Content-Type: text/event-stream, Cache-Control: no-cache, no-transform, X-Accel-Buffering: no.

Event sequence:

  1. workflow_started — workflow metadata + steps preview.
  2. step_started / step_completed / step_skipped — per step.
  3. quality_review — score + summary, after execution.
  4. Terminal event: workflow_completed, workflow_pending_approval, or workflow_error.
  5. processing — heartbeat every 15 s while waiting.
  6. 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.

Response — interactive mode

{
  "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_languagelanguage is not a valid BCP 47 code.
  • 400 no_planworkflow_id row has no plan.
  • 400 webhook_url_required / 400 webhook_url_https / 400 webhook_url_invalid
  • 402 insufficient_creditsdetails.required_credits, details.balance.
  • 402 workspace_budget_exceeded — workflow estimate exceeds the workspace's monthly credit budget.
  • 403 forbiddenworkflow_id belongs to a different org.
  • 403 tier_required — feature requires a higher subscription tier.
  • 404 not_foundworkflow_id not found.
  • 404 prior_not_found
  • 409 prior_not_terminal
  • 409 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_exceededprior_workflow_id chain exceeds 10 links.
  • 422 context_too_largedetails.estimated_tokens, details.max_tokens.
  • 429 concurrent_limit_exceededdetails.active_count, details.limit, details.active_workflows.
  • 500 queue_failed

curl (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...\"}"

GET /api/rigor/status/{workflow_id}

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 unauthorized
  • 400 missing_fieldworkflow_id required.
  • 404 not_found — also returned when the workflow belongs to a different org.
  • 429 rate_limit_exceeded

curl (Windows):

curl https://plith.ai/api/rigor/status/wr_2a4b6c8d... -H "x-api-key: YOUR_KEY"

POST /api/rigor/approve

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 unauthorized
  • 400 invalid_body
  • 400 missing_fieldworkflow_id, decision.
  • 404 not_found
  • 409 invalid_status — workflow is not at pending_approval.
  • 429 rate_limit_exceeded
  • 500 approve_failed

curl (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\"}"

POST /api/rigor/resume

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 unauthorized
  • 400 invalid_body
  • 400 missing_fieldworkflow_id required.
  • 404 not_found
  • 409 invalid_statusdetails.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_exceeded

curl (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...\"}"

POST /api/rigor/step

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 unauthorized
  • 400 invalid_body
  • 400 missing_fieldworkflow_id required.
  • 400 invalid_action — must be next, skip, or abort.
  • 400 invalid_field — workflow must have been created with mode: "interactive".
  • 400 no_plan
  • 403 tier_required — feature requires a higher subscription tier.
  • 404 not_found
  • 409 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_exceeded
  • 500 step_error

curl (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\"}"

GET /api/rigor/workflows

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 unauthorized
  • 429 rate_limit_exceeded
  • 500 internal_error

curl (Windows):

curl "https://plith.ai/api/rigor/workflows?status=executing,queued&limit=10" -H "x-api-key: YOUR_KEY"

POST /api/rigor/workflows/actions

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:

  • Preview (default, confirm: false) — shows what would happen without making changes.
  • Execute (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 unauthorized
  • 400 invalid_body
  • 400 invalid_action — only "cancel" is accepted.
  • 400 no_workflow_idsworkflow_ids missing or empty.
  • 400 bulk_limit_exceededworkflow_ids exceeds 20.
  • 429 rate_limit_exceeded (30 req/min)
  • 500 internal_error

curl (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}"

GET /api/rigor/lineage/{workflowId}

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 unauthorized
  • 400 missing_fieldworkflowId 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_exceeded

curl (Windows):

curl https://plith.ai/api/rigor/lineage/wr_2a4b6c8d... -H "x-api-key: YOUR_KEY"

GET /api/rigor/templates

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"

POST /api/rigor/enhance

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_body
  • 400 missing_fieldtask_description required.
  • 400 input_too_longtask_description exceeds 10,000 characters.
  • 500 enhance_error

curl (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

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.

POST /api/rigor/connections

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).
  • 400display_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_enabled

curl (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\"}"

GET /api/rigor/connections

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"

PATCH /api/rigor/connections/{id}

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 unauthorized
  • 403 forbidden — requires admin role.
  • 400 invalid_body
  • 404 not_found
  • 501 feature_not_enabled

curl (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\"}"

POST /api/rigor/connections/{id}/test

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 unauthorized
  • 400 — SSRF check failed.
  • 404 — connection not found.
  • 501 feature_not_enabled

curl (Windows):

curl -X POST https://plith.ai/api/rigor/connections/conn_a1b2c3d4-.../test -H "x-api-key: YOUR_KEY"

DELETE /api/rigor/connections/{id}

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 unauthorized
  • 404 — connection not found.
  • 501 feature_not_enabled

curl (Windows):

curl -X DELETE https://plith.ai/api/rigor/connections/conn_a1b2c3d4-... -H "x-api-key: YOUR_KEY"

Connection Security

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

MCP Tools

Connect to Plith via MCP at:

  • Rigor: /api/mcp/rigor

Discoverable tools

These 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.

Hidden tools

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.

Integration Patterns

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: 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: { ... } } }

Webhook Delivery

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.


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 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

Interactive Mode (Mode C)

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.


Workflow Chaining

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.


Worked Example

A complete plan → execute → poll cycle for a Solution Design.

1. Plan

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"
}

2. Execute (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_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"
}

3. Poll status

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
  }
}

Execution Modes

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:

  • Requires Pro+ tier.
  • Is subject to workflow size limits. When the workflow exceeds the composition limit, execution falls back to default multi-call mode automatically — execution_fallback: true appears in the plan response.
  • Is not compatible with require_approval: true or interactive mode (mode: "interactive"). See below.
  • Does not support SSE streaming — use polling delivery.

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.


Error Reference

Error envelope

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.

Error codes by category

Auth

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.

Validation

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.

Rate limiting

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}.

Workflow state

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.

Workflow chaining / lineage

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.

Webhook

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.

Interactive / step

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.

Connections

Code HTTP Message (summary) Fix
feature_not_enabled 501 MCP connections feature is not enabled.

Bulk workflow management

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.

Planning

Code HTTP Message (summary) Fix
plan_timeout 504 Planning exceeded the time limit. Simplify or split the task description.

LLM routing

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.

GDPR / data deletion

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.

Server errors

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.

Response Conventions

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/verify always reports credits_remaining: 0 — it does not query the live balance. Use GET /api/billing/balance for 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.


Plith — Platform API Reference

Authentication, API keys, billing, workspaces, notifications, and GDPR endpoints.

Base URL: https://plith.ai

Rigor workflow endpoints are documented in EXTERNAL-RIGOR-API-REFERENCE.md.


Table of Contents


Authentication

Plith supports two authentication mechanisms:

  1. API key (x-api-key header) — for programmatic access and all Rigor endpoints.
  2. Session cookie (Supabase Auth) — for browser-based flows and the dashboard.

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.


POST /api/auth/signup

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_failed

POST /api/auth/login

Auth: 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.

POST /api/auth/logout

Auth: Session cookie Cost: Free

Signs out the current user and clears the session cookie.

Response (200): { "success": true }


GET /api/auth/callback

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.

POST /api/auth/mcp/initiate

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 unauthorized
  • 403 forbidden — requires admin role.
  • 400provider 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\"}"

GET /api/auth/mcp/callback

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.


Health

GET /api/health

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

API Keys

POST /api/keys

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_error

curl (Windows):

curl -X POST https://plith.ai/api/keys -H "Content-Type: application/json" -d "{\"email\": \"you@example.com\", \"name\": \"My Org\"}"

GET /api/keys/verify

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_remaining is always 0 on this endpoint — it does not query the live balance. Use GET /api/billing/balance for 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"

User

GET /api/user/me

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"

Billing

GET /api/billing/balance

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 unauthorized
  • 500 internal_error

curl (Windows):

curl https://plith.ai/api/billing/balance -H "x-api-key: YOUR_KEY"

POST /api/billing/checkout

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_key
  • 503 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}"

POST /api/billing/subscribe

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_tier
  • 400 invalid_interval
  • 409 already_subscribed — org already has an active subscription; use POST /api/billing/portal to manage it.
  • 503 billing_unavailable

curl (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\"}"

POST /api/billing/portal

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 unauthorized
  • 503 billing_unavailable

curl (Windows):

curl -X POST https://plith.ai/api/billing/portal -H "x-api-key: YOUR_KEY"

GET /api/billing/auto-reload

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"

PUT /api/billing/auto-reload

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 unauthorized
  • 403 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}"

POST /api/billing/webhook

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.

Workspaces

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): viewermemberadminowner.


GET /api/workspaces

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"

POST /api/workspaces

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_fieldname 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\"}"

GET /api/workspaces/{id}

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 unauthorized
  • 404 not_found

curl (Windows):

curl https://plith.ai/api/workspaces/uuid -H "x-api-key: YOUR_KEY"

PUT /api/workspaces/{id}

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 unauthorized
  • 403 forbidden — requires admin role.
  • 404 not_found

curl (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\"}"

DELETE /api/workspaces/{id}

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 unauthorized
  • 403 forbidden — requires admin role.
  • 404 not_found

curl (Windows):

curl -X DELETE https://plith.ai/api/workspaces/uuid -H "x-api-key: YOUR_KEY"

GET /api/workspaces/{id}/members

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"

POST /api/workspaces/{id}/members

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\"}"

PATCH /api/workspaces/{id}/members/{memberId}

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_found
  • 400 cannot_demote_self

curl (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\"}"

DELETE /api/workspaces/{id}/members/{memberId}

Auth: x-api-key or session — requires admin or owner Cost: Free

Removes a member from the workspace.

Response (200): { "success": true }

Errors:

  • 403 forbidden
  • 404 not_found
  • 400 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"

POST /api/invites/accept

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.

GET /api/workspaces/{id}/api-keys

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"

POST /api/workspaces/{id}/api-keys

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\"}"

DELETE /api/workspaces/{id}/api-keys/{keyId}

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 forbidden
  • 404 not_found

curl (Windows):

curl -X DELETE https://plith.ai/api/workspaces/uuid/api-keys/key-uuid -H "x-api-key: YOUR_KEY"

GET/PUT /api/workspaces/{id}/llm-policy

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 forbiddenPUT requires admin role.
  • 404 not_found

curl (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\"]}"

GET /api/workspaces/{id}/budget

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"

PUT /api/workspaces/{id}/budget

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}"

GET /api/workspaces/{id}/folders

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"

GET /api/workspaces/{id}/audit-log

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 unauthorized
  • 403 forbidden — requires admin role.

curl (Windows):

curl https://plith.ai/api/workspaces/uuid/audit-log -H "x-api-key: YOUR_KEY"

GET /api/workspaces/{id}/webhooks

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"
    }
  ]
}

POST /api/workspaces/{id}/webhooks

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\"]}"

PATCH /api/workspaces/{id}/webhooks/{webhookId}

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_found

DELETE /api/workspaces/{id}/webhooks/{webhookId}

Auth: 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_found

POST /api/workspaces/{id}/webhooks/{webhookId}/test

Auth: 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_found

POST /api/workspaces/{id}/webhooks/{webhookId}/verify

Auth: 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 }


Workspace Invites

POST /api/workspaces/{id}/invites

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\"}"

GET /api/workspaces/{id}/invites

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"

DELETE /api/workspaces/{id}/invites/{inviteId}

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_found

POST /api/workspaces/{id}/restore

Auth: 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"

Notifications

GET /api/notifications

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"

GET /api/notifications/count

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"

PATCH /api/notifications/read

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}"

Data Deletion (GDPR)

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.


GET /api/data/delete-request

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"

POST /api/data/delete-request

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"

DELETE /api/data/confirm-deletion

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_...\"}"