REST API

Pilot5 over HTTPS.

Launch a deliberation with an API key, receive the result on a signed webhook — from any backend, workflow tool, or non-MCP client. If you’re wiring Pilot5 into Claude, Cursor, Perplexity, or another MCP-compatible client, the MCP connector is the simpler path.

Submit a question, get a deliberated answer from a 5-persona AI panel via webhook callback. Same backend that powers mcp.pilot5.ai/mcp — exposed as plain HTTPS for direct integration.

Base URL: https://api.pilot5.ai (production) · https://staging-api.pilot5.ai (staging)


1. Quickstart

1. Create an API key in the dashboard at pilot5.ai/settings?tab=api. Live keys start with pk_live_, test keys with pk_test_. The plaintext key is shown once on creation — store it securely.

2. Submit a deliberation:

curl -X POST https://api.pilot5.ai/v1/api/deliberations \
  -H "Authorization: Bearer pk_live_xxx" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{
    "question": "Should we price our SaaS per-seat or usage-based for mid-market?",
    "mode": "deliberation1",
    "use_case": "pricing",
    "webhook_url": "https://example.com/hooks/pilot5",
    "metadata": {"order_id": "ord_123"}
  }'

Response (202 Accepted):

{
  "deliberation_id": "del_01HXYZ...",
  "status": "queued",
  "result_url": "https://api.pilot5.ai/v1/api/deliberations/del_01HXYZ.../result",
  "estimated_credits": 4.2
}

3. Receive the webhook (typically 1-3 min for smartroute1, 2-6 min for deliberation1) at https://example.com/hooks/pilot5. Verify the signature, then 4. fetch the full result from result_url.


2. Authentication

All requests authenticate with an API key — not a dashboard login session or Clerk JWT:

Authorization: Bearer pk_live_xxx
Key prefixCreated onStored inUse against
pk_live_...app.pilot5.aiProduction databaseapi.pilot5.ai
pk_test_...staging dashboardStaging databasestaging-api.pilot5.ai

The prefix is a label for the key's environment of origin. Real isolation comes from which database the key was created in: production keys (pk_live_*) live in the production database and only authenticate against api.pilot5.ai; staging keys (pk_test_*) live in the staging database and only authenticate against staging-api.pilot5.ai. A pk_test_* key sent to api.pilot5.ai returns 401 invalid_api_key because the production database has no record of it.

Both prefixes debit real credits in their respective environments. Use staging credits for development and integration testing — they're separate from your production balance. Never commit keys to source; rotate immediately via the dashboard if exposed.

Keys carry scopes (deliberations:write, deliberations:read). A write key implicitly grants read.


3. POST /v1/api/deliberations

Submit a question for deliberation.

Request body

FieldTypeRequiredNotes
questionstringyes10–2000 chars
modestringnodeliberation1 (5 personas, ~4 cr — default) or smartroute1 (single LLM, ~0.5 cr)
use_casestringnogeneral (default) · pricing · code · strategy · marketing · logistics
webhook_urlstringnoHTTPS URL that receives the signed terminal-state callback. Private/loopback IPs are rejected. Omit it to poll instead (see §4)
metadataobjectnoOpaque JSON, ≤4 KB. Echoed verbatim in the webhook payload
difficultystringnoEASY · MEDIUM (default) · HARD. Sizes the credit reservation only

deliberation2 (Dream Team with HITL checkpoints) is not available via the API — it requires interactive pauses incompatible with async webhook delivery. Use the MCP server or web UI for HITL flows.

Headers

  • Idempotency-Key (recommended): any string ≤255 chars. Replaying the same key + body within 24h returns the original deliberation_id instead of creating a duplicate. Same key with a different body returns 409 Conflict.

Responses

202 Accepted:

{
  "deliberation_id": "del_01HXYZ...",
  "status": "queued",
  "result_url": "https://api.pilot5.ai/v1/api/deliberations/del_01HXYZ.../result",
  "estimated_credits": 4.2
}

estimated_credits is the amount reserved at submit time. The final charge is computed from actual token usage and may be lower; any excess is refunded automatically (see §8).

StatusCodeMeaning
401invalid_api_keyMissing, malformed, revoked, or wrong-environment key
402insufficient_creditsWorkspace balance < estimated_credits
403content_blockedQuestion failed safety guardrails (no charge)
409idempotency_conflictSame Idempotency-Key reused with a different body
422validation_errorSchema violation; see error.message
429rate_limitedSee Retry-After header

4. GET /v1/api/deliberations/{id} · poll-only (no webhook needed)

You don't have to run a webhook receiver. If you omit webhook_url on the POST, just poll this endpoint until the status is terminal — the result is available here whether or not a webhook is configured. This is the simplest integration when you can't host a public HTTPS callback (serverless, no-code, behind a firewall):

1. POST /v1/api/deliberations  (no webhook_url)  → { deliberation_id, result_url }
2. GET  result_url every ~10s   → 202 while running, 200 with `synthesis` when done

The shape is status-only while running, full payload on terminal states — pollers don't need a second round-trip to /result.

curl https://api.pilot5.ai/v1/api/deliberations/del_01HXYZ... \
  -H "Authorization: Bearer pk_live_xxx"

While running (status: queued or running):

{
  "id": "del_01HXYZ...",
  "status": "running",
  "mode": "deliberation1",
  "use_case": "pricing",
  "created_at": "2026-04-29T10:00:00Z",
  "updated_at": "2026-04-29T10:01:32Z"
}

On completed:

{
  "id": "del_01HXYZ...",
  "status": "completed",
  "synthesis": "...full arbiter synthesis markdown...",
  "summary": "...one-paragraph TL;DR...",
  "confidence_index": 7.2,
  "mode": "deliberation1",
  "use_case": "pricing",
  "credits_charged": 3.87,
  "error": null,
  "created_at": "2026-04-29T10:00:00Z",
  "updated_at": "2026-04-29T10:04:12Z",
  "completed_at": "2026-04-29T10:04:12Z"
}

On failed:

{
  "id": "del_01HXYZ...",
  "status": "failed",
  "synthesis": null,
  "summary": null,
  "confidence_index": null,
  "mode": "deliberation1",
  "use_case": "pricing",
  "credits_charged": 0,
  "error": {
    "code": "processing_timeout",
    "message": "The deliberation exceeded its processing time and was halted before completing. This is usually transient — retry the request."
  },
  "created_at": "2026-04-29T10:00:00Z",
  "updated_at": "2026-04-29T10:01:00Z",
  "completed_at": "2026-04-29T10:01:00Z"
}

status is one of queued · running · completed · failed. On failed, the deliberation creation cost is fully refunded — credits_charged reflects the actual debit (0 for full refunds).

The error.code on a failed deliberation is one of:

error.codeMeaningWhat to do
processing_timeoutThe run exceeded its processing budget and was halted. Usually transient.Retry the request
processing_failedThe run failed during processing for another reason.Retry, or contact support with the deliberation id

GET /v1/api/deliberations/{id}/result returns the same payload shape as the completed case above, so partners can use either endpoint depending on whether they want to keep polling or fetch once after a webhook arrives.

Poll at most every 10s. Webhook delivery is faster and cheaper.


5. Webhook delivery

When a deliberation reaches a terminal state, Pilot5 sends:

POST {webhook_url}
X-Pilot5-Event: deliberation.completed
X-Pilot5-Timestamp: 1745923452
X-Pilot5-Signature: t=1745923452,v1=5257a869e7ec...
X-Pilot5-Event-Id: evt_8a4c12...
X-Pilot5-Delivery-Id: del_01HV4...
X-Pilot5-Attempt: 1
User-Agent: Pilot5-Webhook/1.0
Content-Type: application/json
{
  "id": "evt_8a4c12...",
  "event": "deliberation.completed",
  "created_at": "2026-04-29T10:04:12Z",
  "data": {
    "deliberation_id": "del_01HXYZ...",
    "status": "completed",
    "mode": "deliberation1",
    "use_case": "pricing",
    "result_url": "https://api.pilot5.ai/v1/api/deliberations/del_01HXYZ.../result",
    "completed_at": "2026-04-29T10:04:12Z",
    "credits_charged": 3.87,
    "metadata": {"order_id": "ord_123"}
  }
}

Other events: deliberation.failed — same shape with status: "failed". The webhook itself carries no report and no error detail; GET result_url returns the error block (processing_timeout / processing_failed) and the run is fully refunded.

The webhook never contains the synthesis itself. Fetch result_url with your API key — this keeps payloads small and ensures only authenticated callers see the deliberation content.

Delivery semantics

  • We expect a 2xx response within 10 seconds.
  • Retries on 5xx, network error, or 408 / 429: +1 min, +5 min, +30 min, +2 h, +12 h. After 5 failures the delivery is marked dead and surfaced in the dashboard for manual replay.
  • 4xx other than 408 / 429 is not retried — fix your handler and replay from the dashboard.
  • After 20 consecutive failures the endpoint is auto-disabled. Re-enable from the dashboard.

Signature verification

X-Pilot5-Signature is t={timestamp},v1={hex_hmac}. The signed payload is:

{timestamp}.{raw_request_body}

HMAC-SHA256 with your key's webhook secret (returned once, alongside the key, on creation). Always use a timing-safe compare and reject if |now - timestamp| > 300s (replay protection).

Python (quick reference):

import hmac, hashlib, time

def verify(secret: str, body: bytes, sig_header: str, ts_header: str) -> bool:
    parts = dict(p.split("=", 1) for p in sig_header.split(","))
    if abs(time.time() - int(ts_header)) > 300:
        return False
    expected = hmac.new(
        secret.encode(),
        f"{ts_header}.".encode() + body,
        hashlib.sha256,
    ).hexdigest()
    return hmac.compare_digest(expected, parts["v1"])

body must be the raw bytes of the request — re-serializing parsed JSON will change whitespace and break the signature. Node and Go reference implementations are available on request — email support@pilot5.ai.


6. Rate limits

EndpointLimit
POST /v1/api/deliberations60 / hour per key
GET /v1/api/deliberations/{id}600 / min per key

Every POST response carries:

X-RateLimit-Limit: 60
X-RateLimit-Remaining: 47
X-RateLimit-Reset: 1745925600

429 responses include Retry-After (seconds). Email enterprise@pilot5.ai to lift caps.


7. Errors

All errors return JSON with the same shape:

{
  "error": {
    "code": "insufficient_credits",
    "message": "Workspace balance 1.20 < estimated 4.20",
    "request_id": "req_01HXYZ..."
  }
}
CodeHTTPWhen
invalid_api_key401Bad/missing/revoked key, or env mismatch
insufficient_credits402Balance below estimate
content_blocked403Guardrails rejected the question (no charge)
not_found404Unknown deliberation_id or wrong workspace
idempotency_conflict409Idempotency-Key reused with different body
validation_error422Schema/length/enum violation
rate_limited429Per-key cap hit
internal_error5xxOur fault — include request_id in support tickets

Always log request_id — it's the fastest path to a root cause from our side.


8. Pricing

Credits are reserved at submit (using estimated_credits) and finalized to actual cost on completion. Excess is refunded to your workspace balance automatically. Full refund on:

  • Guardrail block (no LLM call made)
  • Creation failure (queue / DB error before pipeline starts)
  • Pipeline failure (status: "failed")

Per-mode estimate ranges and the live credit table: pilot5.ai/pricing.


9. OpenAPI spec

A machine-readable OpenAPI 3.1 spec for this surface is published at pilot5.ai/docs/api/openapi.json. Point a Vertex AI Extension, a Gemini function-calling config, Postman, or any standard OpenAPI client at it. Both servers are listed (production + staging); authenticate with Authorization: Bearer pk_live_… (or pk_test_… on staging).


10. SDKs

No official SDKs — the API is small enough that hand-written HTTP is the right tool. If you're integrating from an AI-tool context (Claude Desktop/Cowork, agents, IDE extensions), use the MCP server at https://mcp.pilot5.ai/mcp instead — same backend, same credits, native tool-call ergonomics.


Happy-path trace

# 1. Submit
$ curl -X POST https://api.pilot5.ai/v1/api/deliberations \
    -H "Authorization: Bearer pk_live_xxx" \
    -H "Idempotency-Key: 7c1d..." \
    -H "Content-Type: application/json" \
    -d '{"question":"...","mode":"deliberation1","use_case":"pricing",
         "webhook_url":"https://example.com/hooks/pilot5"}'

HTTP/1.1 202 Accepted
{"deliberation_id":"del_01HXYZ","status":"queued","estimated_credits":4.2,...}

# 2. ~4 min later, your endpoint receives:
POST https://example.com/hooks/pilot5
X-Pilot5-Event: deliberation.completed
X-Pilot5-Signature: t=1745923452,v1=5257a869...
{"event":"deliberation.completed","data":{"deliberation_id":"del_01HXYZ",
 "result_url":"https://api.pilot5.ai/v1/api/deliberations/del_01HXYZ/result",...}}

# 3. Verify signature, then fetch:
$ curl https://api.pilot5.ai/v1/api/deliberations/del_01HXYZ/result \
    -H "Authorization: Bearer pk_live_xxx"

HTTP/1.1 200 OK
{"id":"del_01HXYZ","status":"completed","synthesis":"...","summary":"...",
 "credits_charged":3.87,"completed_at":"2026-04-29T10:04:12Z"}

Questions: support@pilot5.ai

Questions, raised rate limits, or enterprise DPAs? Contact enterprise@pilot5.ai. See also the Privacy Policy, Terms of Service, and Data Processing Agreement.