---
version: 1.4.0
updated: 2026-04-26
---

# Project Tracking (via curl)

This project is tracked in the **Project Tracker** app at `https://app.project-tracker.ai`. Use it to populate and update the backlog as work progresses.

You don't have an MCP server — talk to the API directly with `curl`.

## Staying current

On the first tracker call in a session, run:

```bash
curl -s "$PT_API/prompts/version" | jq -r .curl
```

Compare it against the `version:` line in this file's front matter. If the API reports a newer version, tell the user and offer to fetch the new prompt from `https://app.project-tracker.ai/docs/curl-prompt.md` so they can diff it against their CLAUDE.md and update what's relevant. The behaviour-change changelog is at `https://app.project-tracker.ai/docs/prompts-changelog.md`.

This file covers two modes:

- **Business / planning mode** — design, UX, business rules, content, research. Mostly stories that feed into engineering later.
- **Technical mode** — code, infra, deployments, test suites. Stories broken into tasks, worked on in a repo.

Both use the same API; the difference is which fields matter.

---

## Setup (both modes)

You need an API key. Ask the project owner for one, or create your own at `https://app.project-tracker.ai` under **Keys** (top right). Your key inherits your user's permissions.

**Per-project roles**: a project owner can add you as `viewer` (read-only), `commenter` (can append to changeLog only) or `editor` (full access). If a write returns 403 with `"Permission denied: requires X on project Y"`, your role on that project doesn't include capability X. Tell the user; suggest they ask the project owner for editor access. Hit `GET $PT_API/projects/:key/me/capabilities` to see your capability set on a project.

Export your key:

```bash
export PT_TOKEN=pt_your_key_here
export PT_API=https://app.project-tracker.ai/api
```

Test:

```bash
curl -s -H "Authorization: Bearer $PT_TOKEN" $PT_API/auth/me
```

Full API reference: `https://app.project-tracker.ai/api/docs` (markdown) or `/api/docs/json` (OpenAPI).

---

## Shared concepts

- **Project** — a workspace (short uppercase key like `PT`, `WEB`, `ONB`). Belongs to a person, optionally a team.
- **Item** — a **story** or **task** inside a project. Identified as `PT-12` etc.
- **Story** — a unit of work as seen by its customer/stakeholder. "As a [X], I want [Y] so that [Z]." Good for business, design, research work — or tech features.
- **Task** — a concrete implementation step. Almost always lives under a parent story via `parentKey`. Mostly used in technical mode.
- **Value / effort / desirability** — all 1-5 (XS → XL).
  - `value`: business impact
  - `effort`: complexity / time
  - `desirability`: how urgently needed
- **changeLog** — append-only audit of what was done (string updates; the API timestamps each append).
- **Labels** — free-text tags. Use them to carve up work: `design`, `ux`, `copy`, `backend`, `infra`, `research`, etc.

---

## Mode 1 — Business / planning work

You're capturing requirements, decisions, wireframes, research findings, rules. Most items are **stories** — often self-contained documents of thinking, not things anyone codes directly.

### Typical items

- User-facing features expressed as user stories
- UI decisions ("Checkout button placement", "Onboarding flow v2")
- Business rules ("Pricing for annual plans", "Refund policy")
- Content work ("Landing page copy", "Error messages audit")
- Research outputs ("Competitor analysis Q2", "User interview findings")
- Compliance / legal requirements

### When to populate the tracker

When asked to work on a planning project:

1. **Find or create the project.**

   ```bash
   curl -s -H "Authorization: Bearer $PT_TOKEN" $PT_API/projects | jq
   ```

   Create if missing — usually no `repoPath` / `claudeMdPath` needed for planning-only projects:

   ```bash
   curl -s -X POST -H "Authorization: Bearer $PT_TOKEN" -H "Content-Type: application/json" \
     -d '{"key":"ONB","name":"Onboarding Redesign","notes":"Cross-functional project, primary stakeholder: Sarah"}' \
     $PT_API/projects
   ```

2. **Review what's already there**:

   ```bash
   curl -s -H "Authorization: Bearer $PT_TOKEN" "$PT_API/items?project=ONB" | jq
   ```

3. **Create stories.** Use `type=story`, leave `parentKey` empty unless linking to a bigger epic-style story. Put the richness in `description` (markdown is fine — gets rendered in the UI):

   ```bash
   curl -s -X POST -H "Authorization: Bearer $PT_TOKEN" -H "Content-Type: application/json" \
     -d '{
       "type":"story",
       "title":"Signup flow: require company name for B2B accounts",
       "description":"## Context\nEnterprise sales asked that B2B signups capture company up front...\n\n## Proposal\n- Add **Company** field after email\n- Validate via Clearbit if available\n\n## Open questions\n- Do we also ask role?",
       "project":"ONB",
       "priority":"high",
       "labels":["design","signup","b2b"],
       "value":4,
       "effort":2,
       "desirability":5
     }' \
     $PT_API/items
   ```

   Useful `labels` for planning work: `design`, `ux`, `copy`, `research`, `pricing`, `legal`, `content`, `decision`.

4. **Tracking decisions and refinement.** When a story is refined or a decision lands, append to its `changeLog`:

   ```bash
   curl -s -X PATCH -H "Authorization: Bearer $PT_TOKEN" -H "Content-Type: application/json" \
     -d '{"changeLog":"Decision 2026-04-22: we will NOT ask role at signup — too much friction. Revisit after 30 days of data."}' \
     $PT_API/items/ONB-3
   ```

5. **Moving stories through status** (meaning in a planning context):

   - `backlog` → idea, not committed
   - `todo` → agreed to do, not started
   - `blocked` → can't progress; record what's blocking it (see below)
   - `in_progress` → actively being worked on / designed / researched
   - `review` → draft ready for stakeholder sign-off
   - `done` → signed off, ready for engineering to pick up (or shipped)

   When moving an item to `blocked`, set either `blockedBy: ["PT-12"]` (for in-tracker dependencies) or `blockedReason: "..."` (free text — useful for external blockers like "waiting on legal review", "vendor SDK doesn't support X yet"). The blocked column is meant to make stuck work visible — don't leave items in_progress when they're actually waiting on something.

### Linking design to engineering

Two shapes that both work:

- **Single project** — design stories and tech tasks live in the same project key. Use labels (`design` vs `backend`) to separate.
- **Two projects** — e.g. `ONB` for product work, `WEB` for the engineering. Reference each other in the `description` or `notes` field: "Implements ONB-3".

I'd suggest one project per cross-functional initiative unless the engineering is clearly a separate codebase. Fewer projects, easier to find things.

---

## Mode 2 — Technical work

You're in a codebase. You read the code, you write the code. Items point at repo paths and you work in them.

### When populating the tracker

When asked to analyze a technical project:

1. **Find or create the project** — this time set `repoPath` and `claudeMdPath`:

   ```bash
   curl -s -X POST -H "Authorization: Bearer $PT_TOKEN" -H "Content-Type: application/json" \
     -d '{
       "key":"WEB",
       "name":"Marketing Site",
       "repoPath":"'"$PWD"'",
       "claudeMdPath":"'"$PWD"'/CLAUDE.md",
       "branch":"main"
     }' \
     $PT_API/projects
   ```

2. **Analyze the codebase thoroughly.** Things to find:

   - Testing gaps (no coverage? flaky tests?)
   - Performance issues (slow queries, N+1, bundle size)
   - UX rough edges
   - Security concerns (auth, input validation, secrets in code)
   - Tech debt (TODO comments, commented-out code, deprecated deps)
   - Documentation gaps
   - Deployment / ops concerns
   - Accessibility

3. **List existing items first** to avoid duplicates:

   ```bash
   curl -s -H "Authorization: Bearer $PT_TOKEN" "$PT_API/items?project=WEB" | jq '.[] | {key, title, status}'
   ```

4. **Create stories for feature-level work** and **tasks as concrete steps under them**:

   Story:
   ```bash
   curl -s -X POST -H "Authorization: Bearer $PT_TOKEN" -H "Content-Type: application/json" \
     -d '{
       "type":"story",
       "title":"Add a test suite",
       "description":"Currently no tests exist. Add unit tests for the API and component tests for the frontend.",
       "project":"WEB",
       "priority":"high",
       "labels":["testing","quality"],
       "value":5,
       "effort":3,
       "desirability":4
     }' \
     $PT_API/items
   ```

   Tasks under it:
   ```bash
   curl -s -X POST -H "Authorization: Bearer $PT_TOKEN" -H "Content-Type: application/json" \
     -d '{
       "type":"task",
       "title":"Set up Vitest for the API",
       "description":"Install vitest, supertest, write config, add npm script.",
       "project":"WEB",
       "parentKey":"WEB-5",
       "priority":"high",
       "labels":["testing","backend"],
       "value":4,
       "effort":1
     }' \
     $PT_API/items
   ```

### When working on a tracked item

This is the important flow. Follow it every time.

1. **Get the item first and check if someone else has it.** Multiple AIs can hit the tracker — don't start work on something that's already in flight.

   ```bash
   curl -s -H "Authorization: Bearer $PT_TOKEN" $PT_API/items/WEB-6 | jq '{status, assignee}'
   ```

   **Abort and pick a different item if:**
   - `status` is `in_progress` AND `assignee` is set to someone other than you
   - `status` is `review` (someone's awaiting feedback)

   It's OK to proceed if status is `backlog` / `todo` / `done` (and you're redoing), or if `in_progress` with no `assignee` (stale — likely an AI that crashed).

2. **Get the resolved context** — repo path, claude.md path, branch, notes. These come from the project defaults merged with any overrides on the item:

   ```bash
   curl -s -H "Authorization: Bearer $PT_TOKEN" $PT_API/items/WEB-6/context
   ```

3. **Claim the item** by setting `assignee` to your identifier (e.g. `claude-opus-codex`, `gpt4-bot`, or whatever name makes sense for you) and moving to `in_progress` in a single call:

   ```bash
   curl -s -X PATCH -H "Authorization: Bearer $PT_TOKEN" -H "Content-Type: application/json" \
     -d '{"status":"in_progress","assignee":"claude-opus-codex"}' \
     $PT_API/items/WEB-6

   curl -s -X PATCH -H "Authorization: Bearer $PT_TOKEN" -H "Content-Type: application/json" \
     -d '{"status":"in_progress","order":0}' \
     $PT_API/items/WEB-6/move
   ```

   (The first call claims; the second reorders the board column. If you don't care about ordering, skip it.)

4. **Read the project's `CLAUDE.md`** at the returned path before coding. It contains the project's conventions.

5. **Do the work** in the repo at the returned path, on the returned branch.

6. **When done, update the item.** Move to `review` (or `done` if trivial), record the changes with file paths. **Use `changeLog` for narrative** — don't overwrite `description` or `notes`, as concurrent edits clobber each other (changeLog safely appends):

   ```bash
   curl -s -X PATCH -H "Authorization: Bearer $PT_TOKEN" -H "Content-Type: application/json" \
     -d '{
       "status":"review",
       "changeLog":"Added Vitest + 30 tests covering API routes, models, and matrix logic. Files changed: api/src/__tests__/api.test.ts (new), api/package.json (test script+deps)"
     }' \
     $PT_API/items/WEB-6
   ```

   **Always include file paths in the changeLog.** Future you, other engineers, and other AI agents need to know what was touched.

---

## Cheat sheet

| Thing | Command |
|-------|---------|
| Check auth | `curl -H "Authorization: Bearer $PT_TOKEN" $PT_API/auth/me` |
| List projects | `curl -H "Authorization: Bearer $PT_TOKEN" $PT_API/projects` |
| List items | `curl -H "Authorization: Bearer $PT_TOKEN" "$PT_API/items?project=KEY"` |
| Get item context | `curl -H "Authorization: Bearer $PT_TOKEN" $PT_API/items/KEY-N/context` |
| Create item | `POST $PT_API/items` with `{type, title, project, ...}` |
| Update item | `PATCH $PT_API/items/KEY-N` |
| Move item | `PATCH $PT_API/items/KEY-N/move` with `{status, order}` |
| Board view | `"$PT_API/items/board?project=KEY"` |
| Matrix view | `"$PT_API/items/board/matrix?project=KEY"` |
| Search | `"$PT_API/items?search=auth"` |

## Picking the right item type

| You're doing... | type | parentKey? |
|-----------------|------|-----------|
| Specifying what to build / a decision / research | `story` | usually none |
| Breaking a big story into steps | `task` | yes → parent story |
| Writing code for a specific discrete thing | `task` | yes → parent story |
| Defining a business rule or policy | `story` | none |
| Quick fix with no stakeholder context | `task` | none OK |

When in doubt, use `story`. It's the right default.

## Multi-AI safety

Several AIs may be reading and writing the tracker at once. A few conventions prevent stepping on each other:

- **Always read before claim.** GET the item, check `status` + `assignee`, abort if someone else holds it.
- **Pass `claimedBy` when moving to `in_progress`.** Use a stable identifier for yourself (e.g. `claude-opus-codex`, `gpt4-bot`). Move call body: `{ "status": "in_progress", "claimedBy": "claude-opus-codex" }`. The server records it as the assignee and uses it to detect conflicts on subsequent moves. Treat `in_progress` with empty `assignee` as stale (probably an AI that died) — safe to take over.
- **Server enforces the soft-claim now.** A move to `in_progress` against an item already `in_progress` with a different `assignee` returns **HTTP 409** with `{ error, currentAssignee }`. Surface that to the user and back off, or send `"force": true` to take it over deliberately (and explain why in the next changeLog).
- **Use `expectedVersion` for risky field edits.** PATCH `/items/:key` accepts an optional `expectedVersion` matching the `__v` you saw in your last GET. If another writer raced you, the server returns **HTTP 409** with the latest item — re-merge and retry. Omitting it keeps last-write-wins behaviour. Use it whenever your edit depends on a field's current value (toggling a status, appending to a list you read, etc.).
- **Prefer `changeLog` over `description`/`notes` edits.** `changeLog` appends atomically (many AIs can append simultaneously, no loss) and is unaffected by version conflicts.
- **Don't reassign someone else's in-flight item** unless you're explicitly asked to take it over. If you do, say so in the changeLog.

## Gotchas

- **Item keys are scoped per project, not globally unique.** Two users may both have a project keyed `WEB`, and each will independently number its items `WEB-1, WEB-2, ...`. `GET $PT_API/items/WEB-1` returns the WEB-1 inside *your* accessible scope (owned project preferred). Don't assume an item key uniquely identifies one item across the whole instance.
- **Item keys are uppercase**: `MWA-3`, not `mwa-3`.
- **Keep API key out of git.** Use `~/.bashrc`, never commit.
- **value/effort/desirability are 1-5.** Leave them off for default 3.
- **`changeLog` accepts a string** — it appends to the history array with a timestamp. Don't send an array replacement.
- **Only projects you own or are a team member of are visible** to your key.
- **markdown is supported in description** — headings, lists, links all render in the UI.
- **Pre-write before creating.** Draft item titles/descriptions in your head (or a scratch note) before POSTing — editing is fine, but the audit log is cleaner if initial creation is well-formed.
