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 prefix | Created on | Stored in | Use against |
|---|---|---|---|
pk_live_... | app.pilot5.ai | Production database | api.pilot5.ai |
pk_test_... | staging dashboard | Staging database | staging-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
| Field | Type | Required | Notes |
|---|---|---|---|
question | string | yes | 10–2000 chars |
mode | string | no | deliberation1 (5 personas, ~4 cr — default) or smartroute1 (single LLM, ~0.5 cr) |
use_case | string | no | general (default) · pricing · code · strategy · marketing · logistics |
webhook_url | string | no | HTTPS URL that receives the signed terminal-state callback. Private/loopback IPs are rejected. Omit it to poll instead (see §4) |
metadata | object | no | Opaque JSON, ≤4 KB. Echoed verbatim in the webhook payload |
difficulty | string | no | EASY · 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 originaldeliberation_idinstead of creating a duplicate. Same key with a different body returns409 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).
| Status | Code | Meaning |
|---|---|---|
| 401 | invalid_api_key | Missing, malformed, revoked, or wrong-environment key |
| 402 | insufficient_credits | Workspace balance < estimated_credits |
| 403 | content_blocked | Question failed safety guardrails (no charge) |
| 409 | idempotency_conflict | Same Idempotency-Key reused with a different body |
| 422 | validation_error | Schema violation; see error.message |
| 429 | rate_limited | See 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.code | Meaning | What to do |
|---|---|---|
processing_timeout | The run exceeded its processing budget and was halted. Usually transient. | Retry the request |
processing_failed | The 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
2xxresponse within 10 seconds. - Retries on
5xx, network error, or408/429: +1 min, +5 min, +30 min, +2 h, +12 h. After 5 failures the delivery is markeddeadand surfaced in the dashboard for manual replay. 4xxother than408/429is 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
| Endpoint | Limit |
|---|---|
POST /v1/api/deliberations | 60 / 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..."
}
}
| Code | HTTP | When |
|---|---|---|
invalid_api_key | 401 | Bad/missing/revoked key, or env mismatch |
insufficient_credits | 402 | Balance below estimate |
content_blocked | 403 | Guardrails rejected the question (no charge) |
not_found | 404 | Unknown deliberation_id or wrong workspace |
idempotency_conflict | 409 | Idempotency-Key reused with different body |
validation_error | 422 | Schema/length/enum violation |
rate_limited | 429 | Per-key cap hit |
internal_error | 5xx | Our 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.