WorkflowsMCP

New hire onboarding kickoff: Notion → Todoist → Calendar → Slack

Hired in Notion fans out to a Todoist checklist, 30/60/90 calendar holds, one Slack post.

@workflowsmcpVerified hiringrecruitingatsonboardingscheduling

Slack's own "Onboard a new hire" template already ships a welcome canvas, an in-Slack checklist, and auto channel invites — this workflow skips all three. When a shared Candidates row hits Hired, it expands a department checklist into Todoist for the buddy, puts 30/60/90-day check-ins on the manager's calendar, and posts one #new-hires message linking the Notion page. Notion stays the source of truth. The Property-edited → Send webhook trigger needs a Plus, Business, or Enterprise plan; Free only gets button/Slack automations.

How it flows

  1. 01

    Stage flips to Hired

    A Candidates row moves Stage to Hired. On Plus, Business, or Enterprise, the Property-edited automation sends a webhook; on Free the scheduled poll of Stage = Hired is the trigger instead.

  2. 02

    Backend confirms Hired and resolves relations

    Webhook payloads carry properties only. Re-query the row to confirm Stage is Hired — required because Notion's docs disagree on whether Status supports "changed to a specific option" — then resolve Buddy / Department / Manager Email (extra GET if any are Relations).

  3. 03

    Buddy's checklist lands in Todoist

    Match the buddy's email to a collaborator id, then loop POST /api/v1/tasks over the department's fixed title array — an app-owned template, not a Todoist template API.

  4. 04

    30/60/90 check-ins land on the manager calendar

    Start Date + 30/60/90 becomes three independent events on the manager's calendar. The new hire is not an attendee until a company email exists.

  5. 05

    Buddy and manager resolve to Slack users (optional)

    If users:read.email is configured, lookupByEmail turns their emails into Slack ids for @-mentions in the broadcast.

  6. 06

    One welcome message posts to #new-hires

    A single chat.postMessage with hire, role, buddy, and the Notion page link. No canvas, no Slack checklist, no channel auto-invite — those stay in Slack's own onboarding template.

Set up each app

Work through these in order — later apps usually need a token or an id from an earlier one.

Notion

Candidates database — source of truth that fires the kickoff

  1. 01

    Confirm the workspace plan before building anything

    Check this first, not after the automation refuses to save: notion.com/pricing lists custom database automations — including a Property-edited trigger that fires only when a property changes to a specific value, paired with a Send webhook action — under Plus, Business, and Enterprise. Free workspaces only get button/Slack automations, which cannot watch Stage become Hired via Property-edited → Send webhook. If the workspace is on Free, stop here: the trigger this workflow needs does not exist on that plan. The fallback for Free is a scheduled poll of Candidates for Stage = Hired (same defensive query as the snippet below), not a different Notion automation you can click into existence.

  2. 02

    Reuse the shared Candidates database — add the onboarding fields

    This workflow reads the same "Candidates" database as recruiting-pipeline-gmail-notion-calendar: same Stage status property (Applied → Screen → Onsite → Offer → Hired / Rejected), same Name / Email / Role. Do not create a second candidate table. The shared schema has no onboarding fields yet, so add these four as additive properties — they do not change how the recruiting pipeline reads or writes existing columns: Start Date (Date), Buddy (Person, Email, or Relation — pick one shape and stick to it), Department (Select or Relation), Manager Email (Email). Then notion.so/my-integrations → New integration → Capabilities: "Read content" and "Insert content" (add "Update content" only if you want the optional write-back below), plus "User information with email addresses" so Person-typed buddy/manager emails resolve. Database → ••• → Connections → add the integration.

  3. 03

    Build the automation: Stage becomes Hired → Send webhook

    Database ••• → Automations → New automation. Trigger: Property edited → Stage → look for an "only when it changes to a specific option" condition and set it to Hired. Whether that condition is offered for a Status property is inconsistent across Notion's own help pages — test it live in the editor before shipping. If the condition is missing, the automation can only fire on any edit to Stage, so the backend must re-query the row after every webhook and confirm Stage really is Hired before doing anything else; treat that re-check as required either way. Action: Send webhook, pointed at your backend. Webhook payloads carry database properties only (no page body); if Buddy or Department are Relation properties, the payload gives related page ids, and a follow-up GET /v1/pages/{page_id} turns those into a name or email. Every Notion request needs Notion-Version: 2026-03-11; query via /v1/data_sources/{data_source_id}/query (retrieve the database once for its data source id — /v1/databases/{id}/query is deprecated). Prefer GET /v1/pages/{page_id} from the webhook's page id when you only need to confirm that one row's Stage; use the query filter when polling Plus/Free workspaces for newly Hired rows (drop page_size:1 and page through, or filter on Start Date / a processed checkbox so you do not re-kick the same hire).

    POST /v1/data_sources/{data_source_id}/query · Notion-Version: 2026-03-11 — defensive Hired re-check
    {
      "filter": {
        "and": [
          { "property": "Stage", "status": { "equals": "Hired" } },
          { "property": "Name", "title": { "equals": "Lee Okafor" } }
        ]
      },
      "page_size": 1
    }
  4. 04

    Optional: write Todoist and Calendar links back onto the row

    Once the Todoist project and the three check-in events exist, PATCH /v1/pages/{page_id} so HR can open the candidate row and see both without leaving Notion. Skip this if nobody on the HR side reads Candidates directly.

Todoist

Buddy's checklist — where onboarding tasks actually get done

  1. 01

    Get a Personal API token

    app.todoist.com/app/settings/integrations/developer → copy the Personal API token. Auth header: Authorization: Bearer <token>.

  2. 02

    Create a shared project and add the buddy

    Create a project (per-hire, like "Onboarding — Lee Okafor", or one standing "New Hire Buddy Tasks" project) and Share project → invite the buddy by email. Assignment only works for collaborators on a shared project — an unshared project has nobody to assign to, and a buddy who has not accepted the invite will not appear in the collaborators list.

  3. 03

    Resolve the buddy's email to assignee_id, then create tasks

    Todoist assigns by assignee_id (an internal numeric id), never by email. GET /api/v1/projects/{project_id}/collaborators returns {id, email, full_name}; match the buddy's email and keep that id. Then POST /api/v1/tasks for each checklist item (api/v1 is the live path — rest/v2 and sync/v9 return 410 Gone).

    GET collaborators, then POST an assigned task
    curl -s "https://api.todoist.com/api/v1/projects/6X6WMMqgq2PWxjCX/collaborators" \
      -H "Authorization: Bearer $TODOIST_TOKEN"
    # -> [{ "id": "2671362", "email": "buddy@acme.com", "full_name": "Rae Chen" }, ...]
    
    curl -s -X POST "https://api.todoist.com/api/v1/tasks" \
      -H "Authorization: Bearer $TODOIST_TOKEN" \
      -H "Content-Type: application/json" \
      -d '{
        "content": "Set up dev laptop with new hire",
        "project_id": "6X6WMMqgq2PWxjCX",
        "assignee_id": "2671362",
        "due_string": "tomorrow"
      }'
  4. 04

    Expand the department checklist in your backend

    Todoist has no API for applying a template — its Templates feature is a manual CSV import or project copy, not a programmable object. Keep a fixed array of task titles per Department value (e.g. five Engineering titles, four Sales titles) in your backend, and loop POST /api/v1/tasks once per title with the resolved assignee_id when the webhook fires. That loop is the template system; do not imply Todoist ships one.

Google Calendar

30/60/90-day check-in holds on the manager's calendar

  1. 01

    Enable Calendar API with a narrow events scope

    Google Cloud Console → enable Google Calendar API → OAuth client (or a service account that can write the manager's calendar). Scope: https://www.googleapis.com/auth/calendar.events. This workflow only creates events on the manager's own calendar — it never reads free/busy across a panel and needs none of the domain-wide delegation a whole-team coverage workflow would.

  2. 02

    Compute Start Date + 30 / 60 / 90

    Read Start Date off the Notion candidate row and add 30, 60, and 90 days in your backend — plain date arithmetic, not a Calendar API call. Those three dates are the only inputs to the inserts below.

  3. 03

    Create three independent events on the manager's calendar

    Three separate POST /calendar/v3/calendars/{calendarId}/events calls (no batch insert). calendarId is the manager's work email from the Manager Email property. Title each event with the new hire's name (e.g. "30-day check-in — Lee Okafor"). Invite the buddy if useful; do not add the new hire as an attendee yet — before Day 0 they typically have no company email. Optionally PATCH the events later once a company email lands on the Notion row.

    POST /calendar/v3/calendars/manager@acme.com/events — Day 30
    {
      "summary": "30-day check-in — Lee Okafor",
      "description": "Onboarding checkpoint. Notion candidate page: https://notion.so/...",
      "start": { "date": "2026-09-08" },
      "end":   { "date": "2026-09-08" },
      "attendees": [{ "email": "buddy@acme.com" }]
    }

Slack

One broadcast post — not a rebuild of Slack's onboarding template

  1. 01

    Create the app and install it

    api.slack.com/apps → Create New App → OAuth & Permissions → Bot Token Scopes: chat:write (add users:read.email only if you want buddy/manager @-mentioned by Slack user id rather than named in plain text). Install to Workspace and copy the Bot User OAuth Token.

  2. 02

    Post the single broadcast — and stop there

    Invite the bot to #new-hires (or request chat:write.public so it can post without joining). This step is the entire Slack footprint: one chat.postMessage naming the hire, role, buddy, and Notion page link. Do not create Slack's Onboarding Guide Canvas, do not build or maintain a to-do list inside Slack, and do not run a workflow that auto-invites the hire into channels — those three are exactly what Slack's official "Onboard a new hire" template already does.

    POST https://slack.com/api/chat.postMessage
    {
      "channel": "#new-hires",
      "text": "Welcome Lee Okafor, joining Engineering on 2026-08-10! Buddy: <@U012ABC> · Onboarding tracker: https://notion.so/..."
    }