The API contract.
A channel is a URL. POST JSON to it — there is no SDK, and this page is the whole contract.
curl -X POST "https://<host>/events/<username>/<Channel%20Name>" \
-H "Content-Type: application/json" \
-d '{ "title": "Deploy complete", "message": "api-service deployed to production" }' Payload
{
"title": "Deploy failed", // required, ≤200 chars
"message": "api-service · main @ abc123", // required, ≤4000 chars
"priority": "Crit", // free-form, shown on the card
"nature": "error", // free-form, shown on the card
"dataJson": "{\"run\":\"4821\"}", // ≤64000 chars, previewed
"callback": [ // action buttons
{ "label": "Re-run pipeline",
"webhook": "https://ci.example.com/hooks/rerun" }
],
"ask": { // blocking question, ≤8 options
"options": [
{ "id": "approve", "label": "Approve", "tone": "positive" },
{ "id": "deny", "label": "Deny", "tone": "destructive" }
],
"timeoutSec": 300 // 5–86400
}
} Auth
Two ways to authorize a POST, bearer wins if both are present:
X-Paperplane-Endpoint-Key— per-channel, only if the channel has one set.Authorization: Bearer pplt_…— a personal access token. Works across every channel you belong to, no endpoint key needed.
Tokens are minted in the app under Settings → Developer tokens,
shown once, and stored server-side only as a SHA-256 hash. Six scopes
exist (events:write, channels:read, messages:read,
messages:write, tickets:read, tickets:write);
a token cannot create or delete channels, manage membership, or mint or
revoke another token.
Limits
| Field | Limit |
|---|---|
| title | 200 characters |
| message | 4,000 characters |
| dataJson | 64,000 characters |
| ask options | 1–8 |
| ask timeoutSec | 5–86,400 |
| WebSocket backlog | 1,000 messages per connection |
| Free plan | 1 channel, 1,000 events/day |
Asks — blocking questions
Add an ask object to an event and it renders as buttons on
the notification. Only a signed-in human, tapping in the app, can
answer — a personal access token cannot resolve the question it was
used to send, even holding every scope. See the agents section for the full model.
MCP
paperplane-mcp is an MCP server for Claude Code, Claude
Desktop, Cursor, or any MCP client, over stdio.
{
"mcpServers": {
"paperplane": {
"command": "npx",
"args": ["-y", "paperplane-mcp"],
"env": {
"PAPERPLANE_TOKEN": "pplt_...",
"PAPERPLANE_DEFAULT_CHANNEL": "Builds"
}
}
}
} | Tool | What it does |
|---|---|
paperplane_ask | Send a question with buttons; block until answered or timed out |
paperplane_check_ask | Recover an answer after the client's own tool-call timeout |
paperplane_send_event | Push a notification, with priority and buttons |
paperplane_list_channels | See which channels exist |
paperplane_list_messages | Read recent events and replies, with alert status |
paperplane_set_alert_state | Acknowledge or resolve an alert |
paperplane_reply | Leave context in the thread |
paperplane_list_tickets | Check what is already tracked |
paperplane_create_ticket | File a follow-up, linked back to the alert |