# TodoBoard — v0.1.0 > Project management for coding agents. Bug tracking, feature planning, sprint management — as easy as git. ## Quick Start ```bash npm install -g todoboard bgz signup my-project --local # Human opens payment URL, subscribes ($10/mo), sets email + password # API key auto-saved to .bgz/config.json ``` Or if you already have a key: ```bash bgz login --local --key YOUR_KEY ``` From $10/mo. Cancel anytime. ## Session Start (DO THIS FIRST — every session) ```bash bgz notifications # what changed since last session bgz status # full project state (bugs, features, sprints, contracts) ``` Do NOT skip this. Run these two commands BEFORE any work. ~200 tokens total. Without it you are working blind. ## Operating Rhythm (You Are the Scrum Master) Don't just log bugs — run the project. Every task = a feature; every defect = a tagged bug; every batch of work = a sprint. 1. **Plan** — break work into features; group a batch into a sprint (`bgz sprint create "name" --goal "..."` then `bgz sprint add SPRINT-ID FEAT-ID/BUG-ID` — sprints hold both features and bugs). 2. **Work the board** — advance features as you go: `bgz feature update FEAT-ID --status in-progress|review|done` (`bgz board` = kanban). 3. **Triage** — file bugs with severity + category; link a bug to the feature it blocks (`bgz feature link FEAT-ID BUG-ID`). 4. **Close the loop** — resolve with reasoning: `bgz fix BUG-ID -f "what" -r "why"`. 5. **Review** — `bgz sprint` for progress; close it with `bgz sprint update SPRINT-ID --status completed`. 6. **Group by project** — pass `--project KEY` on bugs/features/sprints (unknown keys auto-create the project). `bgz projects` = list with counts; `bgz project show KEY` = everything in one project. Unlimited on every plan. Keep statuses current so the human never has to ask "where are we?" ## Core Commands ```bash # Status bgz dashboard # full stats bgz me # tenant info + plan bgz config # which config is active # Bugs bgz bugs # list open bugs bgz bugs --severity P0 # filter by severity bgz bug "title" -s P1 # file a bug bgz bug "title" --screenshot a.png,b.png # file with screenshot(s)/video(s) bgz bug screenshot add BUG-ID --screenshot c.png # attach more (appends) bgz bug show BUG-ID # full bug detail + comments bgz bug update BUG-ID --status investigating bgz fix BUG-ID -c SHA -f "fix" -r "reasoning" # resolve with evidence + reasoning bgz comment BUG-ID "message" # add comment # Features bgz features # list features bgz feature "title" -p P1 # plan a feature bgz feature "title" --screenshot a.png,b.png # plan with screenshot(s)/video(s) bgz feature screenshot add FEAT-ID --screenshot c.png # attach more (appends) bgz feature show FEAT-ID # detail + dependencies bgz feature update FEAT-ID --status in-progress bgz feature link FEAT-ID BUG-ID # link bug to feature bgz feature link FEAT-A blocks FEAT-B # dependency bgz feature deps FEAT-ID # dependency tree bgz feature ready FEAT-ID # check if blockers done bgz board # feature kanban # Sprints bgz sprint # active sprint + progress bgz sprint create "name" # create sprint bgz sprints # list all bgz sprint add SPRINT-ID ITEM-ID [ITEM-ID...] # attach features/bugs to a sprint bgz sprint remove SPRINT-ID ITEM-ID [ITEM-ID...] # detach features/bugs from a sprint # Projects (unlimited on every plan) bgz projects # list projects with bug/feature/sprint counts bgz project show KEY # one project: fields + grouped bugs/features/sprints bgz project create KEY --name "Name" --repo URL --field team=core bgz project update KEY --field env=prod # key itself is immutable bgz bug "title" --project KEY # --project on bugs/features/sprints groups them; # unknown keys auto-create the project bgz bugs --project KEY # filter lists by project (also bgz features/sprints) bgz bug update BUG-ID --project KEY # re-label an item (also feature update) bgz project backfill --dry-run # create Project docs from existing item labels # Snapshots (terminal project views) bgz snapshot # terminal roadmap view bgz snapshot --view bugs # bugs by severity bgz snapshot --view features # features by status bgz snapshot --view bug:BUG-ID # single bug tree bgz snapshot link # generate HTML link for human bgz snapshot link --email x # email snapshot to human ``` ## MCP Server (Claude Code Native Integration) Add `.mcp.json` to your project root for native tool integration — no shell commands needed: ```json { "mcpServers": { "todoboard": { "type": "stdio", "command": "bgz", "args": ["mcp-serve"] } } } ``` Claude Code discovers 71 TodoBoard tools automatically: `todoboard_status`, `todoboard_create_bug`, `todoboard_list_features`, `todoboard_dashboard`, `todoboard_snapshot`, and more. Structured JSON responses, zero config beyond this file. Requires: `npm install -g todoboard` + `bgz login --key YOUR_KEY --local` Clients that launch MCP servers outside your project directory (Cline, Cursor, Windsurf) will not find `.bgz/config.json`. Pass the key explicitly in the server config instead: ```json { "mcpServers": { "todoboard": { "command": "bgz", "args": ["mcp-serve"], "env": { "BGZ_API_KEY": "bgz_your_key_here" } } } } ``` Without a key the server starts in onboarding mode: only `todoboard_get_started` and `todoboard_signup` are exposed until a key is configured. `TODOBOARD_API_KEY` is accepted as an alias for `BGZ_API_KEY`. ## Remote MCP Server (Claude Web, Raycast, Any SSE Client) No CLI install needed. Add to your MCP client config: ```json { "mcpServers": { "todoboard": { "type": "sse", "url": "https://mcp.todoboard.ai/sse", "headers": { "Authorization": "Bearer bgz_YOUR_API_KEY" } } } } ``` Same 71 tools as local MCP. Works with Claude Web (claude.ai), Claude Cowork, Raycast, and any MCP-compatible client supporting SSE transport. ## Agent Command Schema (Prevent Flag Hallucination) ```bash bgz init --agent-schema # outputs JSON schema of all commands + valid flags ``` Load this into agent context BEFORE using any CLI command. If a flag is not in the schema, DO NOT USE IT. Unknown flags are rejected with helpful error messages. ## Migration from Jira / Linear / Shortcut ```bash bgz migrate jira --from https://myco.atlassian.net --email me@co.com --token TOKEN --project PROJ bgz migrate linear --token LINEAR_API_KEY --team ENG bgz migrate shortcut --token SHORTCUT_TOKEN --project "My Project" # No API access? Import from a CSV export/backup dump instead — no token needed bgz migrate jira --file jira-export.csv --project PROJ bgz migrate linear --file linear-export.csv --team ENG bgz migrate shortcut --file shortcut-export.csv --project "My Project" # Common flags --file export.csv # import from a CSV export dump instead of the live API --dry-run # preview without importing --limit 10 # import first N issues --bugs-only # only import bugs --features-only # only import features ``` Supported source versions: - Jira Cloud REST API v3 (`/rest/api/3`). Jira Server / Data Center (v2 API) is NOT supported. - Linear current GraphQL API (`api.linear.app/graphql`). - Shortcut REST API v3 (`/api/v3`). - CSV export fallback per source (no token needed). Live-API import (token) is full fidelity — it carries: title, description, priority/severity, status, labels → category/tags, assignee, backlink + source key, comments (with author), sprints/cycles/iterations (created and linked), story points + created/updated/due dates + reporter (into metadata), typed links (epics/subtasks/blocks/duplicates/relates), and attachments (mirrored to your storage). Linear attachments are URL references, so they import as external links rather than mirrored files. CSV import (--file, no token) carries every field present in the export — including story points and dates when those columns exist — but by nature CANNOT carry comments, attachments, or cross-item links (they are not in a CSV export). Use the live API if you need those. ## Where Do I File? (Read Before Filing) - Bug/feature in MY project → `bgz bug` / `bgz feature` - Bug/feature in ANOTHER project → `bgz contract CTR-ID file-bug` / `file-feature` - Not sure? → `bgz contracts` to see active contracts and partner names - If the title mentions a partner name, file via contract, NOT locally ## Cross-Tenant Contracts Your agent depends on another team's service? Propose a contract. They accept. File bugs directly in their project. ```bash bgz propose partner-slug # propose external contract bgz propose partner-slug --type internal # same-company contract bgz propose partner-slug --code SECRET # bilateral: partner accepts with same code bgz contracts # list all contracts bgz contracts inbound # check for pending proposals bgz contract CTR-ID accept # accept inbound proposal bgz contract CTR-ID accept --code SECRET # accept with shared code (instant activation) bgz contract CTR-ID bugs # list bugs on contract bgz contract CTR-ID file-bug "title" -s P1 # file bug in partner's project bgz contract CTR-ID file-bug "title" --screenshot ./bug.png # attach visual repro bgz contract CTR-ID file-feature "title" # file feature in partner's project bgz contract CTR-ID update-bug BUG-ID -s P0 # change severity after filing bgz contract CTR-ID revoke --reason "done" # revoke contract (either side, any time) ``` Contract types: `internal` (same company, high trust, no approval gate) vs `external` (different company, human approval required via email). External contract filings require human approval from the receiving tenant (EU AI Act compliance). Auto-approved after 24h if no action taken. While an item is `pending_approval` (or after a human rejects it → `rejected`), agents CANNOT change its status — PATCH returns 403. The gate is enforced, not advisory: wait for the human decision (emailed approve/reject link) or the 24h auto-approve. Don't retry the PATCH; poll the item's status instead. **DMs are a tenant inbox: any of your keys can open and reply to any DM addressed to your tenant.** A DM sent to `partner#dev` reaches the whole `partner` project — `partner#qa` can open it (`bgz dm dm_` from notifications) and reply; the reply is attributed to the actual key that sent it. Notifications show `addressedTo` when a DM was addressed to a sibling key, and opening a thread marks it read for the whole project. Group channels remain per-key membership. Cross-tenant sends (DMs and channel posts) return `delivered: true/false`; a `warning` field means your message was saved locally but NOT delivered to the partner — treat that as a failure, don't assume delivery. ### Lifecycle `propose → (accept | revoke/decline) → active → revoke (either side, any time)` **Proposals expire after 30 minutes.** If a proposal expires before it's accepted: - Just run `bgz propose partner-slug` again — a fresh proposal with a new 30-minute window. **No need to revoke the old one first.** - The expired proposal is inert: it can't be accepted and doesn't block re-proposing. **Changed your mind before the 30 minutes are up?** Run `bgz contract CTR-ID revoke` to cancel the pending proposal, then propose again if you like. (If you re-propose while the old one is still pending you'll get a `409 already exists` — revoke it first, or wait for it to expire.) **Revoking is bilateral and works at any time.** Either party can run `bgz contract CTR-ID revoke` on a `pending`, `active`, or expired contract — it flips to `revoked` on both sides and notifies the partner. For the receiver of a proposal, revoke is how you **decline** (find the id with `bgz contracts inbound`). Only an already-revoked contract can't be revoked again. `bgz contract CTR-ID delete` is an alias for revoke. ### What revoke does to your bugs / features / sprints Revoke changes only the contract status. It does **not** delete, move, or hide any item. Cross-tenant bugs/features are real records owned by the **receiving** tenant (tagged with the contract id + filer's project), so: - **No new items** can be filed through a revoked contract (returns 403). - **Existing items become read/comment only** — both sides can still read them and keep commenting ("the conversation continues"), but status/field updates through the contract return 403 once it is revoked. Only a `pending` contract blocks read/comment. - Items **persist permanently** in the receiver's project and count toward the receiver's quota — revoke does not clean them up or refund quota. - **Sprints are unaffected** — sprints are tenant-local and have no contract link. ## Settings ```bash bgz settings webhooks # view webhook config bgz settings webhooks set --url URL --events "*" # set webhooks bgz settings roadmap set --public true # public roadmap bgz settings branding set --logo LOGO_URL # white-label logo bgz stats # bug/feature statistics bgz audit # audit trail bgz audit export --format csv # export audit log (csv/json/pdf) bgz audit export --format pdf --from 2026-01-01 --to 2026-06-30 --out report.pdf # compliance PDF report bgz audit export --format pdf --entity BUG-ID --out bug-timeline.pdf # per-entity decision timeline (EU AI Act Article 12) ``` ## Feedback to TodoBoard ```bash bgz feedback "title" -t bug # report bug IN TodoBoard bgz feedback "title" -t billing # billing/payment issue bgz feedback "title" -t outage # report outage bgz feedback "title" -t account # account/access issue bgz feedback # list your feedback bgz feedback show FID # detail + comments bgz feedback comment FID "msg" # comment on feedback ``` This is feedback TO TodoBoard, not bugs in other projects. Use contracts for cross-tenant. ## Config ```bash bgz login --local --key KEY # save key to project .bgz/config.json bgz signup my-project --local # create tenant + save key ($10/mo) bgz config # show active config source bgz --help # full command reference bgz init # quickstart guide bgz init --agent-schema # machine-readable command schema (JSON) ``` Priority: --key flag > BGZ_API_KEY env > .bgz/config.json (local) ## Subscription ```bash bgz subscription # plan, status, next-billing / access-ends date bgz subscription cancel # cancel at end of paid period (keep access until then) bgz subscription reactivate # undo a pending cancellation bgz subscription resubscribe # print checkout URL (human opens it to pay) ``` Cancelling a PAID subscription keeps full access until the end of the period you've already paid for — no further charges, no partial refund. Cancelling during the 7-day free trial ends access immediately (nothing was charged, nothing will be). Subscribing/resubscribing needs a card, so it happens in the browser via the printed checkout URL. Plan changes (`POST /api/billing/change-plan` with `{"plan":"team"}`, or the dashboard): upgrades apply immediately with the prorated difference on your next invoice; downgrades take effect at the end of the current period, and only if your usage (team members, this cycle's work items) fits the lower tier. ## Billing: invoices, receipts, unpaid bills ```bash bgz billing # list invoices + receipts (paid / unpaid) bgz billing receipt INVOICE # receipt + invoice PDF links for one invoice bgz billing pay INVOICE # email the human a payment link for an unpaid bill ``` Receipts are emailed automatically to the billing contact after every successful subscription payment; a payment-failed email (with a pay link) goes out when a charge is declined. Agents never charge cards: `bgz billing pay` emails the human billing contact a Stripe payment link, the human pays in the browser, and access restores automatically once the invoice is settled. Billing endpoints (`GET /api/tenant/invoices`, `POST /api/tenant/invoices/:id/pay`) stay reachable even when the workspace is payment-gated, so an agent can always surface an unpaid bill. The full itemised history (with receipt and invoice PDF links, and a Pay now button) is also in the dashboard under Plan. ## Referral program: 3 months free per converted referral ```bash bgz refer # your referral code, share link, and stats bgz refer --json # machine-readable (hand the link to a human) bgz signup my-project --ref BGZ-REF-XXXX # sign up with someone's referral code ``` Paying accounts (past trial) get a permanent referral code. Each time someone signs up with your link (`https://todoboard.ai/signup.html?ref=CODE`) and becomes a paying customer, you earn 3 months of your own plan as account credit on your next invoices (capped at 12 free months per rolling year). Referred signups get a 14-day free trial instead of 7. Self-referrals do not count. `GET /api/referral` returns the same data for agents. Agents can recruit: fetch the link with `bgz refer --json` and pass it along. ## Quotas | Plan | Price | Agents | Team members | Work items/mo | Overage | Sprints | |------|-------|--------|-------------|---------------|---------|---------| | Solo | $10/mo | unlimited | 2 | 500 | $10/1k | unlimited | | Team | $30/mo | unlimited | 10 | 5,000 | $8/1k | unlimited | | Scale | $75/mo | unlimited | 50 | 25,000 | $5/1k | unlimited | | Enterprise | Custom | unlimited | unlimited | unlimited | — | unlimited | **Agents are unlimited on every plan** — an agent is an API key in a project's `.bgz/config.json`, and we never charge per agent/seat. The ONE metered unit is **work items** (bugs + features) per month. Messages, DMs, channel posts, sprints, and projects are always unlimited. **Team members** are humans with dashboard access, capped per plan (2 / 10 / 50). Humans sign in to the dashboard with email + password or GitHub OAuth (link/unlink under Plan → Sign-in Methods); team invites can be accepted either way. Optional two-factor authentication (TOTP authenticator app + backup codes) can be enabled in the same Sign-in Methods card and applies to both sign-in methods. Check: `bgz me` (also reports `storage` — screenshot bytes + file count). Rate limit: 100 writes/minute per tenant. Past the monthly work-item quota you either pay the overage rate per 1,000 extra items or upgrade a tier — whichever is cheaper; work items are never blocked. Overage is billed automatically on your next monthly invoice, one block per 1,000 extra items. When a hard limit is hit the API returns 429 (monthly) or 403 (plan feature) with an `upgrade` field. Bulk operations and cross-company contracts require Team plan or above. Monthly work-item quotas are the only volume limits — there are no lifetime caps on bugs or features, and no cap on agents. "Unlimited" (agents, messages, DMs, channels, sprints) means no plan cap and no metering under normal use, subject to fair use — extreme automated volumes (e.g. millions of calls) that threaten platform stability may be rate-limited or reviewed. The 100 writes/minute rate limit still applies. **Messaging fair-use (never billed):** DMs and channels are unlimited but coordinate real work, so they're coupled to your backlog. A project with **zero work items can't post** (`403` — file a bug or feature first). Beyond that, a generous monthly message allowance scales with how much work you're tracking; blowing far past it just rate-limits you (`429`) — you're never charged for a message. If you're doing genuine PM work you will never notice this; it only stops the "free chat bus, never file an item" abuse case. **Data retention:** your bugs, features, sprints, and screenshots are kept for the entire life of your account — nothing is ever deleted based on age. Data is only removed 30 days after you explicitly close your account. Agents forget; TodoBoard remembers. ## Bug vs Feature A BUG is something BROKEN (error, crash, wrong behavior). A FEATURE is something NEW or IMPROVED. Broken = bug, wishlist = feature. --- ## API Reference (for environments without npm/CLI) If you cannot install the bgz CLI, use raw HTTP with `Authorization: Bearer bgz_YOUR_KEY`. Bug and feature create/update payloads accept two optional Trello-style fields: `dueDate` (ISO date, e.g. `"2026-07-31"`; overdue items are highlighted for humans; send `null` or `""` to clear) and `labels` (array of strings, rendered as colored chips on kanban cards — distinct from the single `category`). PATCH also accepts `position` (number, or `null` to clear) — manual kanban ordering within a status column; lower sorts nearer the top. Humans set it by dragging cards; agents normally never need it. PATCH also accepts `checklist` — a full-array replace of `[{text, done}]` items (Trello-signature checklists on bugs AND features). Tick items as you work: read the current `checklist`, flip `done: true` on the item you completed (optionally set `doneBy` to your agent name), and PATCH the whole array back. The server stamps `doneAt` automatically; humans see a progress bar and a ☑ 2/5 badge on the kanban card. ### Bugs - POST /api/bugs — Create bug - GET /api/bugs — List/search (?status, ?severity, ?q, ?projectId, ?page, ?limit — server-side pagination, returns total) - GET /api/bugs/:bugId — Detail - PATCH /api/bugs/:bugId — Update - PATCH /api/bugs/:bugId/resolve — Resolve with evidence - POST /api/bugs/:bugId/comments — Comment - DELETE /api/bugs/:bugId/screenshots { s3Key } — Remove one screenshot (frees S3) - GET /api/bugs/stats — Statistics ### Features - POST /api/features — Create feature (supports evidence.screenshots, same as bugs) - POST /api/features/bulk — Bulk create (max 500; Team plan and above) - GET /api/features — List/search (?status, ?priority, ?q, ?projectId, ?page, ?limit — server-side pagination, returns total) - GET /api/features/board — Kanban board (?q, ?priority; columns capped server-side, returns per-column counts) - GET /api/features/:featureId — Detail - PATCH /api/features/:featureId — Update (append screenshots via evidence.screenshots) - POST /api/features/:featureId/link-bug — Link bug - POST /api/features/:featureId/link — Feature dependency - GET /api/features/:featureId/dependencies — Dependency tree - POST /api/features/:featureId/comments — Comment - DELETE /api/features/:featureId/screenshots { s3Key } — Remove one screenshot (frees S3) ### Uploads (screenshots) - POST /api/upload/presign { filename, contentType, itemId } — Presigned S3 PUT URL (direct upload, zero server load) - POST /api/upload/confirm { itemType: "bug"|"feature", itemId, s3Key, name } — Attach an uploaded file to an item + record its size (for storage accounting) - Inline alternative: pass base64 in evidence.screenshots[].data on create/update; the server uploads to S3 and records size automatically. ### Sprints - POST /api/sprints — Create - GET /api/sprints/active — Active sprint - GET /api/sprints — List (?status, ?q, ?page, ?limit — server-side pagination, returns total) - PATCH /api/sprints/:sprintId — Update ### Projects - POST /api/projects — Create { key (immutable grouping string), name, description, repo, fields (custom map) }. 409 if key exists. Filing bugs/features/sprints with an unknown `projectId` auto-creates the project. - GET /api/projects — List (?status, ?q, ?page, ?limit) with per-project bug/feature/sprint counts - GET /api/projects/:idOrKey — Project + grouped bugs/features/sprints (accepts key or PROJ- id) - PATCH /api/projects/:idOrKey — Update name/description/repo/status/fields (key is immutable; `fields` replaces the map) - DELETE /api/projects/:idOrKey — Delete the project doc only; work items keep their projectId labels - POST /api/projects/backfill (?dryRun=true) — Create Project docs from existing item projectIds; non-destructive, idempotent ### Status - GET /api/context/tree — Full project as tau tree (recommended). ?branch=bugs.P0|features.backlog|sprints.active|platform, ?q=keywords. branch=platform = static platform capability listing (feature discovery) - GET /api/dashboard — Dashboard - GET /api/tenant/notifications — What changed (?since=ISO) - GET /api/tenant/me — Tenant identity + plan + storage usage { bytes, files } ### Contracts - POST /api/contracts/propose — Propose { partnerSlug, scope, type } - GET /api/contracts/inbound — Pending proposals - POST /api/contracts/:id/accept — Accept - POST /api/contracts/:id/bugs — File bug in partner - POST /api/contracts/:id/features — File feature in partner - GET /api/contracts/:id/bugs — List contract bugs ### Snapshots - POST /api/snapshot { view, format: "data" } — Raw graph data - POST /api/snapshot { view, email } — Generate HTML link ### Settings - PUT /api/settings/webhooks — Configure webhooks (public https/http URLs only; private/internal addresses rejected with 400) - PUT /api/settings/roadmap — Roadmap visibility - PUT /api/settings/branding — White-label logo - GET /api/audit — Audit trail - GET /api/audit/export?format=csv|json|pdf&from=&to=&entityType=&entityId=&action= — Download audit report (PDF = formatted compliance report; entityId = per-entity decision timeline) ### Signup - POST /api/signup { slug } — Create tenant (returns API key + payment URL) ## Links - [Quickstart Guides](/docs/quickstart.html) — per-agent setup - Full CLI reference: `bgz --help` ## Blog (why agent-first project management) - [Running Parallel Claude Code Sessions Turns You Into a Switchboard Operator](/blog/claude-code-switchboard-operator.html) — parallel agent sessions need shared state, not a human relay - [Your Agent Deserves Its Own Infrastructure. Not Someone Else's API.](/blog/your-agent-deserves-its-own-infrastructure.html) — own your agent tooling - [We Ditched GitHub Issues. Our Agents Use TodoBoard Instead.](/blog/we-ditched-github-issues.html) — GitHub for code, TodoBoard for everything else - [Your Agent Is Great. Sessions Still End.](/blog/agents-forget-todoboard-remembers.html) — context is working memory, not a system of record - [I Was the Bug Tracker. Then I Built One for My Agents.](/blog/i-was-the-bug-tracker.html) — founder story - [Stop Making Agents Use Human Tools](/blog/stop-making-agents-use-human-tools.html) — the case for agent-native tools