Docs

REST API reference

The documented api.plori.ai/v1 surface: create agents, submit and track runs, schedules, credits, and the public rate cards. OpenAPI 3.1 spec included.

Everything the dashboard and the MCP tools do runs over one REST API at https://api.plori.ai/v1. If you are writing code rather than operating an MCP client, call it directly. The machine-readable contract is the OpenAPI 3.1 document at https://plori.ai/openapi.json; this page is the human version of the same surface.

Authentication

Account endpoints take a bearer credential:

Authorization: Bearer plori_sk_...

Either a plori API key (plori_sk_…, minted in the dashboard under Settings → API keys, or by plori login for the CLI) or an OAuth 2.1 access token from the same flow MCP clients use. The full credential walkthrough, including how an agent can obtain a key non-interactively, is at plori.ai/auth.md.

GitHub Actions use a separate workload-identity path: a job mints a GitHub OIDC JWT for audience plori.ai, exchanges it at POST /v1/github/oidc/exchange, and receives a short-lived capability for one repository agent run. No Plori secret or customer account is created by that exchange. The claim URL combines the verified repository capability with a normal signed-in payer when the free quota ends or the repository is private.

The main path: create, invoke, read

Create an agent (get-or-create by name, so reruns are safe):

curl -s https://api.plori.ai/v1/agents \
  -H "Authorization: Bearer $PLORI_KEY" \
  -d '{"name": "my-agent", "if_exists": "return"}'

Send it work. The response is always 202 with the run's ids; the endpoint submits and returns, it does not block:

curl -s https://api.plori.ai/v1/agents/$AGENT_ID/runs \
  -H "Authorization: Bearer $PLORI_KEY" \
  -H "Idempotency-Key: hello-1" \
  -d '{"message": "Say hello and tell me what tools you have."}'
# -> {"run_id": "...", "session_id": "..."}

Poll the run until it finishes:

curl -s https://api.plori.ai/v1/agents/$AGENT_ID/runs/$RUN_ID \
  -H "Authorization: Bearer $PLORI_KEY"

The run object carries status and accounting (credits, tokens, stop reason), not the reply text. Read the reply where the live surfaces read it: the session WebSocket (GET /v1/ws, the browser's transport), the CLI (plori result), or the MCP invoke_agent / get_run_result tools, which can wait for the answer and return it in one call. If you want request/response simplicity end to end, MCP is the better integration surface; the REST API is the submit-and-track layer underneath.

Run accounting

The run's monetary integers are in micro-US-dollars, so 1000000 equals $1. credits is the net charge after any automatic refund. It is null when usage attribution is unavailable. gross_micro_usd records consumption before refunds, and refunded_micro_usd records the amount returned. refund_reason records why the run qualified for an automatic refund. refund_withheld_reason records why Plori did not refund the run. The gross and refund fields are absent when their values are zero or empty.

usage_by_role splits gross usage among executor, advisor, and reviewer. Each role includes gross credits, tokens, and the number of model calls. The role credits sum to gross_micro_usd, not the net credits value. This field is absent when usage attribution is unavailable.

The advisor is the stronger model that the Plori Router can consult during a turn. The advisor object reports calls_used, calls_allowed, and spend_micro_usd. Its spend is gross and matches usage_by_role.advisor.credits. spend_cap_micro_usd is present only when the run has a cap. By default, a run has no advisor spend cap. To set a cap for an agent, send advisor_max_spend_micro_usd in PATCH /v1/agents/{agentID}. The value is the maximum advisor spend for each turn. 0 stops advisor calls, and null removes the cap.

Retries, idempotency, and concurrency

A run is nondeterministic and billable, so never retry blindly. Send an Idempotency-Key header (or idempotency_key in the body): a retry with the same key within 24 hours returns the original run (X-Plori-Idempotent-Replay: true) instead of starting a second one. The same key with a different payload is a 422; a retry while the first request is still in flight is a 409 with Retry-After.

How many runs an account may have in flight at once is set by its plan (1 Free, 2 Pro, 5 Power, counted across all its agents); past that a run is a 429 with Retry-After. Running out of credits is a 402 whose body carries "code": "insufficient_credits".

Endpoints

Method and path What it does
POST /v1/agents Create an agent; if_exists: "return" makes it get-or-create by name
GET /v1/agents List your agents
GET /v1/agents/{agentID} One agent, including its warm/sleeping status
POST /v1/agents/{agentID}/runs Submit a run (202 {run_id, session_id}; Idempotency-Key, max_turn_tokens)
GET /v1/agents/{agentID}/runs Run history
GET /v1/agents/{agentID}/runs/{runID} One run's status and accounting receipt
POST /v1/agents/{agentID}/runs/{runID}/cancel Cancel an in-flight run (cascades to delegated child runs)
POST /v1/agents/{agentID}/schedules Defer a one-shot run (fire_at RFC3339 or delay_seconds)
GET /v1/agents/{agentID}/schedules List schedules
GET /v1/agents/{agentID}/schedules/{scheduleID} One schedule
DELETE /v1/agents/{agentID}/schedules/{scheduleID} Cancel a pending schedule (returns the updated schedule)
GET /v1/users/{userID}/credits Balance, plan, and purchase options (your own account only)
POST /v1/github/oidc/exchange Exchange a GitHub Actions OIDC JWT for one repository-scoped run capability
POST /v1/github/claim Attach a verified repository and its existing agent workspace to the signed-in account
GET /v1/github/runs/{runID} Poll one GitHub-triggered run's durable AG-UI events (narrow capability or owning API key)
GET /v1/pricing/models Public model rate card (no auth)
GET /v1/pricing/tools Public tool and workflow rate card (no auth)

Errors share one shape, {"error": "<message>"}, with the two exceptions noted above (402 adds code and audience). A 500 always answers {"error": "internal error", "request_id": "<id>"}: the message is fixed (never parse it), and request_id is the handle to quote at [email protected] so we can find the failure in our logs.

Endpoints not in this table (files, memory, workflows, billing checkout, …) exist but are internal: the dashboard's private surface, subject to change without notice. If you need one of them in the documented API, tell us: [email protected].

How this page stays accurate

The OpenAPI document is drift-checked in CI against the live route table, so every path and method above is served exactly as written. Pricing numbers live on the rate cards and in the two pricing endpoints, never hand-copied here.