Meeting notes → Linear tasks
Turn Notion meeting notes into Linear issues, then confirm them in Slack.
Action items decided in a meeting evaporate unless someone files them the same day. This workflow watches a Notion "Meeting Notes" database for pages marked Ready, extracts the action items with owners and due dates, creates one Linear issue each, writes the issue links back into the note, and posts a confirmation in Slack so the room can catch a misread item within the hour.
How it flows
- 01
Note marked "Ready to process"
A human flips the Status select once the notes are actually finished.
- 02
Poller picks it up within 10 minutes
Filtered database query returns pages that are ready and recent.
- 03
Action items extracted
Owner, verb phrase and any stated due date. Items without a clear owner are flagged rather than assigned.
- 04
One Linear issue per item
issueCreate with team, assignee, label and the meeting-note backlink.
- 05
Note updated with issue links
Status → Processed. This write is the idempotency guard for the whole workflow.
- 06
Slack confirmation posted
One message listing the filed issues so the room can correct a misread item.
Set up each app
Work through these in order — later apps usually need a token or an id from an earlier one.
Notion
Source of the notes and the processed-state flag
- 01
Set up the "Meeting Notes" database
Properties: Name (Title), Date (Date), Attendees (Person), Status (Select: Draft / Ready to process / Processed), Linear Issues (Text), Team (Select). "Ready to process" is set by a human — never auto-process a Draft, or you will file half-written thoughts as tickets.
- 02
Poll for ready notes every 10 minutes
Retrieve the database once to get its data source id, then POST /v1/data_sources/{data_source_id}/query with the filter below and Notion-Version: 2026-03-11 — the version header is mandatory on every Notion request. The older /v1/databases/{database_id}/query is deprecated as of 2025-09-03 and now only answers if you pin a pre-split version and the database has exactly one data source. Polling is the simplest thing that works; if you want sub-poll latency, subscribe to the page.properties_updated webhook, which fires on the Status change itself (events are aggregated and usually land inside a minute).
Data source query filter{ "filter": { "and": [ { "property": "Status", "select": { "equals": "Ready to process" } }, { "property": "Date", "date": { "past_week": {} } } ] }, "sorts": [{ "property": "Date", "direction": "descending" }], "page_size": 25 } - 03
Write the results back
After the issues exist, PATCH the page: set Status to "Processed" and put the issue identifiers in Linear Issues. That write is the idempotency guard — if it fails, the next poll re-processes the note, so do it last and check the response.
Linear
Receives one issue per action item
- 01
Create a personal API key
Linear → Settings → Security & access → Personal API keys → New API key (linear.app/settings/account/security). Send it as the raw Authorization header value (no "Bearer " prefix — that is the single most common 400 here; Bearer is for OAuth tokens).
- 02
Look up team and state ids
Everything in the Linear GraphQL API is addressed by id. Run this once and store the ids you need in config.
Teams and workflow statesquery Bootstrap { teams { nodes { id key name states { nodes { id name type } } } } } - 03
Create the issue
One mutation per action item. Put the meeting page URL in the description so the ticket carries its own context — use the url the Notion API returned for that page rather than hand-building a notion.so link, which now only survives as a redirect — and set dueDate only when the note actually stated one.
issueCreate mutation + variablesmutation CreateIssue($input: IssueCreateInput!) { issueCreate(input: $input) { success issue { id identifier url } } } # variables { "input": { "teamId": "a1b2c3d4-5e6f-4a7b-8c9d-0e1f2a3b4c5d", "title": "Cut the onboarding email from 5 steps to 3", "description": "From [Growth sync — 2026-01-28](https://app.notion.com/p/2f91c4a70b8d4e5f9a1c3b6d8e0f2a4b)", "assigneeId": "9f8e7d6c-5b4a-4392-8172-6f5e4d3c2b1a", "labelIds": ["c7d8e9f0-1a2b-4c3d-9e8f-7a6b5c4d3e2f"], "dueDate": "2026-02-04", "priority": 2 } }
Slack
Confirmation so mistakes get caught fast
- 01
Add an incoming webhook
api.slack.com/apps → your app → Incoming Webhooks → Activate → Add New Webhook to Workspace → pick the team channel. Store the URL as a secret; anyone holding it can post as your app.
- 02
Post the created issues as a list
Keep it to one message per meeting, not one per issue, or the channel becomes unusable after a busy Monday. The owner names below are plain text labels, not pings — to actually notify someone you need their Slack member id and the <@U01ABCDEF> form, which means keeping a Linear-user-to-Slack-member map.
Slack Block Kit payload{ "text": "3 action items filed from Growth sync", "blocks": [ { "type": "section", "text": { "type": "mrkdwn", "text": "*Growth sync — 28 Jan* → 3 issues in Linear" } }, { "type": "section", "text": { "type": "mrkdwn", "text": "• <https://linear.app/acme/issue/GRW-412|GRW-412> Cut onboarding email to 3 steps — @dana\n• <https://linear.app/acme/issue/GRW-413|GRW-413> Pricing page A/B test — @sam\n• <https://linear.app/acme/issue/GRW-414|GRW-414> Churn survey copy — @rae" } }, { "type": "context", "elements": [{ "type": "mrkdwn", "text": "Wrong owner or a hallucinated item? Edit in Linear — the note stays untouched." }] } ] }