automation

Turn multiple inboxes into one actionable CRM workflow with Notion, Supabase, and Slack.

An inbound CRM automation skill that polls multiple email inboxes, syncs routing logic from Notion, persists contacts and activity in Supabase, and only alerts Slack when there is a real lead or task to handle.

This is the operating system behind my inbound funnel: multiple inboxes, one Notion playbook, Supabase as the record, and Slack only when there is a lead worth acting on.

OpenClaw
Claude Code logoClaude Code
Claude CoWork
Codex logoCodex
Cursor logoCursor
Qualify leadsRoute inbound emailDraft responsescrm

Walkthrough

Watch the skill in action

Watch on YouTube

This is the operating system behind my inbound funnel: multiple inboxes, one Notion playbook, Supabase as the record, and Slack only when there is a lead worth acting on.


How it works

CRM Inbound Orchestrator

Use this skill for hourly polling CRM workflows across:

  • info@yourdomain.com
  • sales@yourdomain.com
  • support@yourdomain.com

The source-of-truth SOP is synced from Notion page CRM_SOP_PAGE_ID every run.


How to use it

1

Download the skill package

Hit the Download button in the sidebar (desktop) or the button pinned at the bottom (mobile). You'll get a ZIP.

2

Add it to your agent

Extract and drop the folder into your agent's plugins directory. For Claude Code / OpenClaw, that's .claude/plugins/ in your project root.

3

Start prompting

Your agent now knows the skill. Describe the task in plain English — it handles the rest.


Developer & Source DetailsRaw SKILL.md and repository files

Skill Definition (SKILL.md)

The raw instruction document consumed by your AI agent.

CRM Inbound Orchestrator

Use this skill for hourly polling CRM workflows across:

  • info@yourdomain.com
  • sales@yourdomain.com
  • support@yourdomain.com

The source-of-truth SOP is synced from Notion page CRM_SOP_PAGE_ID every run.

Runtime Env Contract

Required:

  • NOTION_API_KEY
  • CRM_SOP_PAGE_ID (default: 31288fb313488013924ade7bf704ab6f)
  • CRM_MONITORED_EMAILS (comma-separated)
  • CRM_POLL_QUERY (default: in:inbox is:unread -in:spam -in:trash -category:promotions -category:social -category:updates -category:forums)
  • CRM_POLL_OVERLAP_MINUTES (default: 120)
  • SUPABASE_URL
  • SUPABASE_SECRET_KEY

Optional:

  • CRM_POLL_MAX_RESULTS (default: 200)
  • CRM_POLL_MAX_AGE_HOURS (default: 36)
  • CRM_SOP_CACHE_FILE (default: /tmp/crm-inbound-sop-cache.json)
  • CRM_POLL_STATE_TABLE (default: crm_poll_state)
  • CRM_CONTACTS_TABLE (default: crm_contacts)
  • CRM_ACTIVITIES_TABLE (default: crm_activities)
  • CRM_DRAFTS_TABLE (default: crm_drafts)
  • CRM_ACCOUNTING_TABLE (default: accounting_entries)
  • CRM_JOB_RUNS_TABLE (default: crm_job_runs)
  • GOG_ACCOUNT (fallback sender account for approvals)
  • CRM_OUTSTANDING_LOOKBACK_DAYS (default: 7)
  • CRM_OUTSTANDING_STALE_HOURS (default: 24)
  • CRM_OUTSTANDING_NOTIFY_EMPTY (default: false)
  • CRM_CLASSIFIER_MODEL (default: gpt-5-nano)
  • CRM_REPLY_MODEL (default: gpt-5.2)
  • CRM_USE_MODEL_CLASSIFIER (default: true)
  • CRM_USE_MODEL_REPLY_WRITER (default: true)
  • OPENAI_API_KEY (required to use model classifier/reply writer)
  • CRM_GMAIL_LABEL_APPLY (default: true)
  • CRM_GMAIL_LABEL_LEAD (default: CRM/Lead)

Deterministic Command Surface

1) Fetch Notion SOP

tsx {baseDir}/scripts/fetch-sop.ts fetch_sop

Optional flags:

  • --page-id <id>
  • --cache-file <path>
  • --output <path>

2) Poll Inboxes Hourly

tsx {baseDir}/scripts/poll-inboxes.ts poll_inboxes

Optional flags:

  • --accounts <csv>
  • --query <gmail-query>
  • --overlap-minutes <n>
  • --max-age-hours <n>
  • --output <path>

3) Classify + Route + Persist

tsx {baseDir}/scripts/process-inbound.ts process_inbound \
  --poll-file /tmp/crm-poll.json

Optional flags:

  • --sop-file <path>
  • --output <path>

4) Approval Actions

tsx {baseDir}/scripts/approval-action.ts approval_action \
  --action approve \
  --draft-id <draft_id> \
  --approved-by "U052337J8QH"

Also supported:

  • --action revise --notes "<feedback>"
  • --action reject --reason "<reason>"

5) Morning Outstanding Check (Actionable-Only Report)

tsx {baseDir}/scripts/check-outstanding.ts check_outstanding

Optional flags:

  • --lookback-days <n> (default: 7)
  • --stale-hours <n> (default: 24)
  • --output <path>

Slack Output Contract (Non-Technical Friendly)

For each actionable lead, post a simple Slack card containing only:

  • Mailbox
  • Subject
  • Message received
  • When it was sent
  • Suggested response

Approval/revision happens in the Slack thread, not via command strings in the main message.

Workflow Rules

  1. Poll hourly from all configured inboxes.
  2. Deduplicate by account_email:message_id.
  3. Classify with gpt-5-nano into receipt|sales|support|ignore (fallback to heuristics only if model call fails).
  4. Pull classification policy dynamically from Notion SOP sections (classification, lead, inbound, routing, qualification) and inject it into the classifier prompt.
  5. Deterministic lead override: expert-network, consulting, sponsorship, partnership, and creator-collaboration outreach is forced to sales when business ask is explicit.
  6. Apply Gmail label CRM/Lead (or CRM_GMAIL_LABEL_LEAD) to sales threads.
  7. Deterministic hard-ignore override: newsletter/digest/vendor-blast patterns (view in browser, unsubscribe, manage preferences, roundup-style blasts, Gmail promotional categories) are forced to ignore unless explicit lead criteria are met.
  8. Notification gate: only explicit business leads can create drafts and Slack notifications; model-only sales guesses are downgraded to ignore.
  9. Sales path:
    • upsert contact
    • log activity
    • apply SOP context from Notion snapshot
    • create draft only for human, direct business inquiries (consulting/sponsorship/partnership intent)
    • craft suggested response with gpt-5.2
  10. Accounting path:
  • parse vendor/date/amount/currency
  • upsert accounting entry
  1. No send side effects until manual approval in Slack thread.
  2. Slack reporting policy:
  • no hourly heartbeat/status spam
  • hourly posts only when actionable
  • morning 9:20 post provides outstanding summary (including "none" when empty)

Notion SOP Structure (Recommended)

Use clear headings in your Notion page so policy extraction stays deterministic:

  1. Business Context
    • what Prompt Circle does
    • ideal inbound opportunities
  2. Lead Classification Rules
    • Lead (sales): person/company reaching out for consulting, sponsorship, partnerships, affiliate opportunities, expert-network interviews, or any paid advisory call
    • Ignore: newsletters, product updates, system/vendor notifications, job alerts, hiring spam, social digests
    • Support: requests for help with your product/service
    • Receipt: invoices/payment confirmations
  3. Lead Qualification Checklist
    • commercial intent present
    • human sender (not no-reply/automated digest)
    • asks for a call/brief/proposal/timeline/budget or paid expertise
  4. Response Playbook
    • tone
    • what to ask for next (brief, timeline, deliverables, budget)
    • when to decline
  5. Out-of-Scope
    • examples you never want treated as leads

Reference template:

cat {baseDir}/references/notion-inbound-sop-template.md

Data Contract

Tables:

  • crm_contacts
  • crm_activities
  • crm_drafts
  • accounting_entries
  • crm_job_runs
  • crm_poll_state

Reference DDL:

cat {baseDir}/references/supabase-schema.sql

Hourly Cron Setup (No Hourly Announce Spam)

openclaw cron add \
  --name "CRM hourly polling" \
  --cron "0 * * * *" \
  --tz "America/New_York" \
  --session isolated \
  --message "Run crm-inbound-orchestrator hourly polling cycle. Use skill crm-inbound-orchestrator. Run fetch_sop, poll_inboxes, process_inbound. Only report actionable items."

Morning 9:20 Outstanding Sweep

openclaw cron add \
  --name "CRM morning outstanding check" \
  --cron "20 9 * * *" \
  --tz "America/Toronto" \
  --session isolated \
  --message "Run crm-inbound-orchestrator outstanding review. Use skill crm-inbound-orchestrator. Run check_outstanding for last 7 days and post a concise summary to Slack."

Safety

  • Do not log secrets or tokens.
  • If Notion fetch fails, use cached SOP and report degraded.
  • If one inbox fails, continue others and report partial failure.
  • Keep outbound email behind explicit approval action.