Skip to Content

HTTP API

The native HTTP API served by plico serve.

plico serve exposes a native API over the runtime and event store. This page covers the access rules; Threads and Artifacts describe their endpoints.

Access and safety

The server has no authentication, so it listens on loopback only and refuses requests a web page could send:

RequestResponse
Foreign Host header (DNS rebinding)421
Cross-origin Origin header403
Body that is not application/json415

Clients that send no Origin, such as curl and the CLI, are unaffected.

plico serve --allow-remote exposes the server on another host and skips the Host check; the Origin check still applies. A host app that serves the API behind its own authentication passes allowRemote: true to servePlico and owns authentication. Origin must match in scheme, host and port, so behind a TLS proxy the host app also passes publicOrigin: "https://its.host".

Approval signals sent to POST /v1/runs/<run-id>/signal have any context dropped, so a client cannot say who decided. See Threads.

Cancel a run

Cancel an active persisted run with POST /v1/runs/<run-id>/cancel or plico cancel <run-id> <project>. Pressing Ctrl+C during plico run also cancels that run.

Scripted runs

A script in a run request replays steps the caller picks and skips the model. The API answers 403 run.script_disabled unless the server starts with plico serve --allow-script (allowScript: true for servePlico).

Scripted runs over HTTP never run tool handlers. Each tool.call step needs a result, which is recorded as the tool's output, the same way evals mock tools. A step without result or with "live": true is refused with 400 request.invalid_body before the run starts. Approval policies still apply, so a step for a tool that needs approval waits for a decision first, unless an approval.decide step follows it (see Evals). plico run --dry --script on the command line, and plico.runs.start({ script }) in your backend, still run the real handlers.

With plico serve --inspect (inspect: true for servePlico) the server only reads: it recovers nothing, GET /health returns { "ok": true, "inspection": true }, and every request other than GET or HEAD is refused with 403 inspection.read_only. See Use Plico from your backend.

Routes

Every route is under /v1. Errors are JSON with an error.code and error.message.

RoutePurpose
GET /healthLiveness.
GET /v1/projectThe loaded project: its root and config.
GET /v1/instructionsThe composed instructions.
GET /v1/readinessReadiness diagnostics.
POST /v1/runsStart a run. Returns 202 and the runId.
POST /v1/runs/dryRun the project without a model and return the result.
GET /v1/runsList runs.
GET /v1/runs/<run-id>One run.
GET /v1/runs/<run-id>/eventsThe run's events. ?after=<sequence> returns only newer ones. See Events.
GET /v1/runs/<run-id>/events/streamThe same events as Server-Sent Events.
POST /v1/runs/<run-id>/cancelCancel a run.
POST /v1/runs/<run-id>/signalSend a signal, such as an approval decision.
GET /v1/runs/<run-id>/workspaceThe run's artifact files and versions. Sub-routes /files/<path>, /diffs, /exports and /snapshots/<id> are in Artifacts.
POST /v1/threads, GET /v1/threadsCreate and list threads.
GET /v1/threads/<id>, PATCH /v1/threads/<id>Read or rename a thread.
GET /v1/threads/<id>/messages, POST /v1/threads/<id>/messagesRead history and send a message. See Threads.
GET /v1/threads/<id>/workspaceThe thread's shared artifact workspace.
POST /v1/eval, GET /v1/eval, GET /v1/eval/<name>Run evals and read the latest report or one case.

/v1/project, /v1/instructions, /v1/readiness, /v1/eval and /v1/runs/dry are not served on a mounted, scoped API.

On this page