API overview
What the chat API serves, where it lives, and the two headers every call carries.
The chat API is the HTTP surface of the PlanCover assistant. Any PlanCover team or service with an API key can call it.
It is a server-to-server API. Your backend holds the API key and calls the chat API; browsers and mobile apps call your backend, never the chat API directly with a key.
Base URL
Every path in these docs is relative to the base URL of one environment:
| Environment | Base URL | API keys |
|---|---|---|
Production (prod) | https://chat.142.93.215.84.sslip.io | pcai_live_… |
Staging (staging) | https://chat-staging.142.93.215.84.sslip.io | pcai_test_… |
Interim hostnames
These sslip.io hostnames are interim, until PlanCover assigns the API a
domain of its own. Read the base URL from configuration, such as a
BASE_URL environment variable, rather than hard-coding it.
Keys are issued per environment, and a key works only in the environments its
prefix allows: pcai_live_ keys in prod, pcai_test_ keys everywhere else.
A staging key is refused in production, and a production key in staging. The
third environment, dev, runs locally and has no shared host. See
Authentication.
Endpoints
| Path | What it does | Needs a key |
|---|---|---|
POST /v1/chat/completions | Starts a turn, OpenAI Chat Completions style (Quickstart) | Yes |
POST /chat/stream | Starts a turn and streams the answer | Yes |
POST /chat/confirm | Resumes a turn paused on a confirmation | Yes |
POST /chat/resume | Resumes a turn paused for sign-in | Yes |
POST /auth/session | Mints a member session | Yes |
POST /auth/login, POST /auth/verify | Signs a member in with an OTP | Yes |
GET /auth/me, POST /auth/logout | Reads or ends a member's sign-in | Yes |
GET /documents/{handle} | Streams a document the assistant cited | Yes |
GET /health/live, GET /health/ready | Liveness and readiness probes | No |
The /chat/* endpoints speak the Native SSE API: eleven
named events over text/event-stream.
POST /v1/chat/completions speaks OpenAI's Chat Completions protocol, so any
OpenAI-style SDK can call the assistant. Start with the
Quickstart.
Building with Claude Code, Codex or another coding agent? See Use with AI coding agents.
Every call carries two principals
| Header | Identifies | Required |
|---|---|---|
Authorization: Bearer pcai_… | Your integration: the team or service calling | On every route except /health/* |
X-PlanCover-Session: <session id> | The member the chat acts for | For continuity and member data |
The key says who is calling. The session says who the conversation belongs to. They are separate on purpose: one integration talks for many members, and a member's session is never a key. Authentication covers both.
Errors
A refused call is a JSON response with a real HTTP status and the
error envelope. The key, the session and
the limits are all checked before a stream opens, so a refused chat turn is an
HTTP status, not an error event on a 200. Only a failure after the stream
has started arrives as an error event.