# HTTP API Base URL: your Pickpost host. All bodies are JSON. The API is not versioned yet; `/api/v1/...` paths come with the OpenAPI document. ## Authentication Send a personal API token as a Bearer token: ```http Authorization: Bearer pp_… ``` Create tokens under **API & MCP** in the app, or with `POST /api/auth/tokens` while signed in. The token is returned once and stored only as a hash. A token acts as the user who created it, in the workspace it was created in (the one open in the app at the time), and stops working if that user leaves the workspace. A personal API token reaches the actions below (as an agent), [`/mcp`](/reference/mcp/) and media uploads, nothing else: it cannot approve, invite, manage tokens, billing or the account. Anything else answers `403`. Do those in the app. Access tokens that MCP clients get from the sign-in service are not accepted here: their audience is `/mcp`. See [MCP server](/reference/mcp/#authentication). ## Actions | Method and path | Does | |---|---| | `GET /api/agent/tools` | The action catalog: `name`, `description`, `policy` (`Allowed` or `NeedsApproval`), `inputSchema`. | | `POST /api/agent/tools/{action}` | Runs one action, for example `posts.create`. Body: the action's input. Response: its output. See [Tools](/reference/tools/). | `GET /api/agent/tools` also lists `ui.setContext`, which the web app uses to report which page is open. Agents do not need it. Actions with policy `NeedsApproval` answer `403 ApprovalRequired` here. So do edits (`posts.update`, `posts.setVersionText`, `posts.setOption`) of a post that is already scheduled: someone approved that post, not the change. Unschedule it in the app, or ask the user to make the change. ## Tokens | Method and path | Does | |---|---| | `GET /api/auth/tokens` | Your tokens: `id`, `name`, `createdAt`. Never the secret. Signed in only. | | `POST /api/auth/tokens` | Body `{"name": "..."}`. Response includes `token`, shown this once. Signed in only. | Revoke a token in the app under **API & MCP**. ## Errors ```json {"code": "NotFound", "message": "Post 123 not found.", "issues": []} ``` | `code` | HTTP | |---|---| | `BadInput`, `Invalid` | 400 | | `Unauthorized` | 401 | | `PlanLimit` | 402 | | `ApprovalRequired`, `Forbidden` | 403 | | `NotFound`, `UnknownAction` | 404 | | `Conflict` | 409 | | `TooManyRequests` | 429 | | `Internal` | 500 | `issues` is filled for `Invalid`: one entry per broken network limit. `PlanLimit` means the workspace's plan does not cover the request, or its monthly allowance is used up: a network outside the plan, one account too many, the month's scheduled posts or posts on X. The message says which, and what lifts it: an upgrade, or, while paid plans are not on sale, the next month or the plan coming soon. Retrying does not help. `Forbidden` means the user's role in the workspace does not allow it. ## Request ids Every response carries `x-request-id`. Send your own `X-Request-Id` to have it used instead. Include it when you report a problem. ## Health - `GET /healthz` answers `ok` while the process runs. - `GET /readyz` answers `200` only when the database responds and the instance is not shutting down.