Developer docs
Automate Zapfeed with an organization API key.
One key unlocks both a REST API and a built-in MCP server. Wire up your app to file bugs automatically, script your triage, or let an AI agent read votes and move posts across the roadmap - all scoped to your org, all auditable.
Overview
The integrations surface lives under https://zapfeed.io/api/integrations for REST and https://zapfeed.io/api/mcp for MCP. Every request authenticates with a single bearer token - no OAuth dance, no per-host config. The org (and optionally a single board) is derived from the key, so a key can never touch another org’s data. Posts and comments created through the API are tagged source=API so your team can tell integrations apart from real users in the dashboard.
Get an API key
- 1Open
Dashboard → Settings → API keys(/admin/api-keys). API & MCP access is available on the Base plan and up. - 2Click New key, give it a name (e.g.
ci-bug-reporter), optionally pin it to one board, and set a rate limit (default 300/hour). - 3Copy the plaintext key - it looks like
zf_…and is shown once. Only a hash is stored; if you lose it, revoke and mint a new one. - 4Store it as a secret (env var, secret manager). Never commit it or expose it in client-side code.
Authentication
Send the key as a bearer token on every request:
Authorization: Bearer zf_live_xxxxxxxxxxxxxxxxxxxxxxxxA missing or malformed header returns 401. An invalid or revoked key also returns 401. If the org’s plan no longer includes API access, requests return 403 with code: "UPGRADE_REQUIRED".
Rate limits
Each key has its own hourly budget (default 300 req/hour, configurable when you mint it). When you exceed it, requests return 429 with these headers:
X-RateLimit-Limit: 300
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 2026-07-16T12:00:00.000Z
Retry-After: 1800Back off until Retry-After seconds have passed. Sustained abuse blocks the key for an hour.
REST endpoints
All paths are relative to https://zapfeed.io. All bodies are JSON.
List boards. Discover the boards this key can see. A board-pinned key returns only its board.
curl https://zapfeed.io/api/integrations/boards \
-H "Authorization: Bearer zf_live_xxxxxxxxxxxxxxxxxxxxxxxx"
# → { "boards": [ { "id", "name", "slug", "description",
# "color", "icon", "visibility",
# "_count": { "posts": 42 } } ] }List posts. Query bugs, features and feedback. Filters: board (slug or id), type, status (comma-separated), q (search), sort. Paginates via cursor.
curl "https://zapfeed.io/api/integrations/posts?type=BUG&status=NEW,UNDER_REVIEW&sort=most-voted&limit=25" \
-H "Authorization: Bearer zf_live_xxxxxxxxxxxxxxxxxxxxxxxx"
# → { "posts": [ ... ], "nextCursor": "clx..." | null }
# Follow pages with ?cursor=<nextCursor>Create a post. File a bug/feature/feedback item. If the key is pinned to a board, boardId is optional; otherwise pass a boardId in the org. Created posts are tagged source=API for auditing.
curl -X POST https://zapfeed.io/api/integrations/posts \
-H "Authorization: Bearer zf_live_xxxxxxxxxxxxxxxxxxxxxxxx" \
-H "Content-Type: application/json" \
-d '{
"title": "Checkout fails on Safari 17",
"description": "Stack trace + repro steps...",
"type": "BUG",
"anonymousEmail": "reporter@acme.com",
"browserInfo": { "ua": "Safari/17" }
}'
# → 201 Created - the full post objectGet a post. Fetch one post by id or by full dashboard URL (e.g. https://zapfeed.io/posts/<id>).
curl https://zapfeed.io/api/integrations/posts/clx123 \
-H "Authorization: Bearer zf_live_xxxxxxxxxxxxxxxxxxxxxxxx"Update status. Move a post through your workflow. A status change fires subscriber notification emails automatically.
curl -X PATCH https://zapfeed.io/api/integrations/posts/clx123 \
-H "Authorization: Bearer zf_live_xxxxxxxxxxxxxxxxxxxxxxxx" \
-H "Content-Type: application/json" \
-d '{ "status": "PLANNED" }'List comments. All comments on a post, oldest first.
curl https://zapfeed.io/api/integrations/posts/clx123/comments \
-H "Authorization: Bearer zf_live_xxxxxxxxxxxxxxxxxxxxxxxx"Add a comment. Comment as the key. It is recorded as "<key-name> (API)" so it is distinguishable from human replies.
curl -X POST https://zapfeed.io/api/integrations/posts/clx123/comments \
-H "Authorization: Bearer zf_live_xxxxxxxxxxxxxxxxxxxxxxxx" \
-H "Content-Type: application/json" \
-d '{ "content": "On the roadmap for Q3 - thanks!" }'MCP - for AI agents
The same key powers a Model Context Protocol server over Streamable HTTP at https://zapfeed.io/api/mcp. Point any MCP-capable client (Claude, editors, your own agents) at it and the tools below appear automatically - auth, scoping and rate limits are shared with the REST surface.
{
"mcpServers": {
"zapfeed": {
"type": "http",
"url": "https://zapfeed.io/api/mcp",
"headers": { "Authorization": "Bearer zf_live_xxxxxxxxxxxxxxxxxxxxxxxx" }
}
}
}| list_boards | Discover boards visible to the key. |
| list_posts | Query posts. Defaults to open posts only unless you pass status. |
| get_post | Fetch one post by id or URL. |
| list_comments | Read a post’s comment thread. |
| create_post | File a new bug/feature/feedback item. |
| add_comment | Reply on a post as the key. |
| update_post_status | Move a post to a new status. |
list_posts returns open posts (NEW, UNDER_REVIEW, PLANNED, IN_PROGRESS) by default - pass status explicitly to see completed or closed items.
Enum reference
BUG, FEATURE, FEEDBACKNEW, UNDER_REVIEW, PLANNED, IN_PROGRESS, COMPLETED, CLOSED, REJECTED, WONT_FIXField limits: title 1–300 chars, description 1–20,000 chars, comment content 1–10,000 chars. screenshotUrl must be an https:// URL.
Errors
| 400 | Bad request - invalid JSON, missing/invalid fields, or no board resolved. |
| 401 | Missing/malformed Authorization header, or invalid/revoked key. |
| 403 | Out of scope for the key, board locked, or plan lacks API access (UPGRADE_REQUIRED). |
| 404 | Post or board not found within the key’s scope. |
| 429 | Rate limit exceeded - honour Retry-After. |
Error bodies are JSON: { "error": "..." } (with an optional code).