Write a PRD in Notion backed by call quotes and usage data
When to use it: a problem is framed, the user stories are filed, and the team now needs one document that lets engineering start, design mock and sales announce. Readable in ten minutes, two to four pages. A good PRD isn't complete, it's decisive: every claim traces to a customer's words, a measured number or an issue in the tracker.
Natural language input in Claude
"Write the PRD for bulk CSV export. The framing and the stories are done."
│
▼
┌─────────────────────────────────────────────────┐
│ 1 · Check the prerequisites │ notion_search
│ Find the framing page and the filed stories │ linear_project
│ before a single section is written. │
└────────────────────────┬────────────────────────┘
├───────────────▶ ▪ not ready no framing or no stories yet
▼ framing and stories found
┌───────┴─────────────────────────────┐
▼ ▼
┌──────────────────────────────────┐ ┌──────────────────────────────────┐
│ 2 · Pull the customer's words │ │ 3 · Measure the baseline │ posthog_insight
│ Exact quotes from recent │ │ The number the success metric │ posthog_query
│ Granola call transcripts. │ │ moves, from a saved chart. │
└────────────────┬─────────────────┘ └────────────────┬─────────────────┘
└───────┬─────────────────────────────┘
▼ evidence in hand, or its absence noted
┌─────────────────────────────────────────────────┐
│ 4 · Check scope against the tracker │ linear_issue
│ What is already built, in flight or owned │
│ by another project gets named, not moved. │
└────────────────────────┬────────────────────────┘
▼
┌─────────────────────────────────────────────────┐
│ 5 · Draft the seven sections, then cut │
│ One line up top, a success metric at the end, │
│ cut down to two to four pages. │
└────────────────────────┬────────────────────────┘
▼
╔═════════════════════════════════════════════════╗
║ 6 · Settle the open questions ║
║ The PM decides or assigns each one before ║
║ the draft becomes a page. ║
╚════════════════════════╤════════════════════════╝
▼ no open question without an owner
┌─────────────────────────────────────────────────┐
│ 7 · Write the PRD page │ notion_create_page
│ A draft page under your specs parent, │ notion_append_blocks
│ linked from the project it specifies. │ linear_project
└─────────────────────────────────────────────────┘
▪ terminal — the run stops there and no PRD is drafted
1. Check the prerequisites
notion_search— find the framing page (the problem statement) and any story pages by the feature's name,sort: "last_edited_time". The search only sees pages shared with the integration, so an empty result means "not shared, or not written": ask which before concluding. Read the framing in full withnotion_get_blocksrecursive: true— toggles and nested lists hide half a framing at the first level. The tool reads one page of 100 blocks per level and doesn't followhas_more: if the response hashas_more: true, or a nested block comes back with exactly 100 children, the framing is longer than it can read, so say so and don't write the PRD from a partial framing.linear_project— find the project the stories were filed under (op: "list", thenop: "get"), and list its issues withlinear_issueop: "list",project_id. The stories are those issues; their acceptance criteria are what section 5 links to.- If the framing or the stories are missing, stop and say which. A PRD written without a framing invents its problem; one without stories has an empty middle. Both are their own processes, don't improvise them here.
- Note the three to five product areas the framing says are affected. The PRD cites them; it doesn't rediscover them.
2. Pull the customer's words
granola_contentop: "list_notes"withcreated_afterset to [your evidence window, e.g. the last 90 days], narrowed withfolder_idif your customer calls live in a folder (op: "list_folders"to find it). There is no text search on notes, and the list carries little more than ids, titles and dates: shortlist by title and date, thenop: "get_note"on each shortlisted call to read its summary and attendees before keeping it. Drop calls that don't touch the feature and calls whose attendees are all internal.- A note's summary is a paraphrase, not a quote. For the two or three lines that go into "Why now", pull the exact wording with
op: "get_transcript"(paginated,page_sizeup to 100).op: "get_note"withinclude: "transcript"fails withTRANSCRIPT_TOO_LARGEon long meetings, so go straight to the paginated call for long meetings. - Keep each quote with the account and the call date. If no call in the window mentions the problem, write that down: a PRD with no customer voice should say so rather than borrow one from the framing.
3. Measure the baseline
posthog_insightop: "list"withsearchon the feature's terms, thenop: "run"on the chart the team already watches. It replays the insight's own definition, so the baseline matches the number people see on their dashboard. Prefer it to any query you would write yourself.posthog_queryonly when no saved chart exists, or whenrunreports the chart is saved in PostHog's legacyfiltersformat and can't be replayed. Confirm the event is tracked first withposthog_schemaop: "events". Usequerywith aFunnelsQueryorRetentionQueryfor conversion and retention (funnel semantics don't survive a SQL rewrite), and HogQL only for plain counts, counting people withuniq(person_id), notdistinct_id, which counts devices. For a legacy chart, rebuild its exact definition (same events, steps and window), not a new one of your own.- No event, no baseline. Write "not measured today" and make instrumenting the event part of the success criterion. Never estimate a baseline to fill the section.
- Record the window and the exact definition next to the number, so the post-launch reading uses the same one.
4. Check scope against the tracker
linear_issueop: "search"on each in-scope item, two or three phrasings each, every phrasing one to three key terms ("csv export", "bulk download"), never a sentence: the search matches the query as a substring of titles and descriptions, so a full sentence finds nothing. A customer's wording and an engineer's issue title often describe the same thing in different words.- Found and done → it already exists: move it to "out of scope" or reframe the item as fixing or surfacing what's there.
- Found and in progress in another project → a dependency, named in section 6 with its project.
- Found in someone else's backlog → a merge candidate, flagged for the PM.
linear_projectop: "list"— an active project that overlaps the scope is a dependency or a conflict, and the PRD says which.- This step only reads. The PRD names what it found; it never re-parents, merges or closes an issue.
5. Draft the seven sections, then cut
- In one line, readable by someone outside the product team.
- Why now: the quotes from step 2, the baseline from step 3, the accounts or revenue affected as [your impact measure], and the prioritization context from the framing.
- Audience: the target persona, who is explicitly not targeted, and the accounts that asked.
- Scope: what's in, and what's explicitly out, including what step 4 found already built.
- User stories and acceptance criteria: linked to the issues, not pasted in.
- Dependencies and risks: from step 4, each with its owner.
- Success criterion: the metric from step 3, its baseline, the target [your target], and when it will be read.
Then cut to two to four pages: enough for dev to start, design to mock, sales to announce, no more. Link rather than copy. Every "TBD" becomes either a decision or an open question listed at the top.
6. Settle the open questions
Show the draft with its open questions first, then what step 7 will write: the parent, the page title, and the link line to be added to the project in the tracker. The PM decides each question or assigns it to a named owner; nothing unowned survives into the page. A draft in the conversation costs nothing to rewrite, a page people have already read and quoted does.
7. Write the PRD page
notion_searchthe exact title first. If a PRD for this feature already exists, stop and ask whether this replaces it; never create a second copy.notion_create_pageunder [your specs parent page or database], title "PRD: [feature name]". Under a database, the call writes the title into a column namedName, and the database schema call doesn't return columns, so read a few rows withnotion_query_databasepage_size: 5first: the title column is the property whosetypeistitle. If it isn't namedName, or the database is empty so no row shows it, the create would be refused, so the page goes under a parent page instead. Set a status property to Draft where one exists; sharing with the wider team is a separate step.- Notion takes at most 100 blocks per request and 2,000 characters per text run. Pass the first 100 blocks in
content, thennotion_append_blocksthe rest in batches of 100, in order, splitting long paragraphs. Keep the page id from the create call: if a batch fails, resume appending to that page rather than creating it again. linear_projectop: "update"to link the PRD from the project's description. The update replaces the description whole, soop: "get"first and append the link line to the existing text. That field is Linear's short project summary, capped at 255 characters, not the project document: if the appended text would pass the cap, leave the description as it is and report the link instead. A failed update never blocks the run; the PRD page already exists.
Output
The PRD page link, whether the project in the tracker now links to it (or the link to add by hand), the open questions with their owners, which evidence was missing (no call in the window, no tracked event), and the scope overlaps found in the tracker.