| name | consult-intake |
| description | Turn this week's inbound "AI 顧問服務" consulting inquiries (the website discovery-call emails from website@zynkr.ai) into tracked work: for each real lead it creates a CRM deal in Supabase, a numbered Google Drive project folder, and a kickoff doc inside that folder. Use this skill whenever Peter runs /consult-intake or says things like "process this week's consult inbounds", "處理本週顧問諮詢", "有沒有新的顧問諮詢", "check for new consulting leads", "turn the discovery-call inquiries into deals", "intake the consult requests", or otherwise asks to triage / log inbound AI-consulting requests — even if he doesn't name the CRM, the Drive folder, or the exact phrase. It is also the skill a scheduled weekly consult-intake run should invoke. When Peter mentions consult inbounds, discovery-call inquiries, or new 顧問 leads and wants them logged, prefer this skill. |
| category | sales-consultant |
| project | consult-intake |
| platform | claude |
| status | Done |
| author | Peter Tu |
| sheetId | 2.04 |
| input | Inbound AI-consulting discovery-call emails from website@zynkr.ai; optionally a forwarded inbound sales / proposal lead email |
| process | Parse → filter real leads → idempotently create a Supabase CRM deal + numbered Drive project folder + kickoff doc; optional inbound-sales mode also builds a Drive context doc, re-uploads attachments, and opens a Notion ticket in the Consultant service DB |
| output | Per real lead: a Supabase CRM deal, a numbered Drive project folder, and a kickoff doc; (optional) a Notion ticket + re-uploaded attachments + bidirectional Drive↔Notion links |
| synergy | [] |
Consult Intake
Peter gets inbound consulting requests through the Zynkr website's consult page.
Each one arrives as an email and, today, just sits in the inbox. This skill
clears that backlog once a week: it reads the week's inquiries, and for every
genuine one it lays down the three things the sales motion needs — a CRM deal
his team can track, a Drive project folder to hold the work, and a kickoff
doc so whoever picks it up starts with context, not a blank page.
It runs fully autonomously: parse → filter → create → report. No mid-run
confirmation. The whole point is that Peter runs it (or a cron does) and comes
back to a finished pipeline. Because it writes real CRM records and Drive files,
the safety comes from being idempotent — it skips tests and skips any lead that
already has a deal, so re-running the same week never creates duplicates.
The inbound email shape
Every real inquiry is plain text from website@zynkr.ai and looks like this:
Subject: New discovery call inquiry from Jane
From: website@zynkr.ai
New discovery call inquiry
Name: Jane
Email: inquirer@example.com
Company: 轉職計劃中
Interest: AI 顧問服務
Source page: consult
Brief context:
我希望透過諮詢...
The signal that an email is a consulting lead is the line Interest: AI 顧問服務.
The five fields you parse are Name, Email, Company, Interest, and the
free-text under Brief context:. (Company is often a real org, but sometimes a
personal phrase like "轉職計劃中" or blank — that's fine, carry it through as-is.)
Fixed facts (don't re-derive these)
- Supabase project_id:
uomieoqlkazknjgmfdda (the shared Zynkr project; CRM tables are crm_*)
- Google account for all Gmail/Drive tools:
peter_tu@zynkr.ai
- Drive parent folder (where numbered project folders go):
1hkXPX7OXPFOU0BcloPbJSFp8O0zArM8t
- CRM deal URL for the report/doc:
https://zynkr-crm.vercel.app/deals/{deal_id}
- Owner (
Peter Tu) and pipeline (銷售流程) are looked up live inside the SQL — don't hardcode their ids.
Workflow
1 · Pull the week's inquiries
Search Gmail for the consult inbounds from the last 7 days:
mcp__google-workspace__search_gmail_messages(
user_google_email = "peter_tu@zynkr.ai",
query = 'from:website@zynkr.ai "Interest: AI 顧問服務" newer_than:7d'
)
Then fetch the bodies in one batch with
mcp__google-workspace__get_gmail_messages_content_batch(message_ids=[...]).
A thread can contain several messages (auto-reply, follow-ups) but they share a
Thread ID — dedupe by Thread ID and keep only the original inquiry (the one
that actually carries the Name: / Email: block).
If nothing matches, say so plainly ("No new consult inbounds this week") and stop.
That's a perfectly good outcome, not a failure.
2 · Parse and filter out the noise
For each inquiry, extract Name, Email, Company, and Brief context.
Skip test / spam rows — these are not real leads and must not reach the CRM:
- the email is
@zynkr.ai, or
- the name, company, or email contains
test, 測試, ga4, or similar dummy markers.
Skipping is normal — name what you skipped and why in the final report so Peter
can sanity-check your judgment.
3 · De-dup against the CRM
Before creating anything, check which of these leads already have a deal — they
were processed on a previous run and must be left alone. One query covers the batch:
SELECT lower(c.email) AS email
FROM crm_deals d JOIN crm_contacts c ON d.contact_id = c.id
WHERE lower(c.email) IN ( <lowercased emails of this week's real leads> );
Any email that comes back is already handled → skip it entirely (no deal, no
folder, no doc). The remaining leads are your work list. (The deal-insert SQL in
step 4 also guards against this internally, but checking up front means you don't
create an orphan folder for a lead whose deal gets de-duped.)
4 · For each surviving lead, create the three artifacts
Do these in order so each can reference the one before it.
a) The CRM deal. Read references/deal-insert.sql, fill in the placeholders,
and run it via mcp__supabase__execute_sql (project_id above). It find-or-creates
the company and contact, inserts the deal, and logs the created activity — all
in one idempotent statement. Mapping:
| Placeholder | Value |
|---|
{{FIRST_NAME}} | the lead's Name |
{{EMAIL}} | the lead's Email |
{{COMPANY}} | the lead's Company (empty string if blank) |
{{DEAL_NAME}} | Name(Company)AI 顧問 — e.g. Jane(轉職計劃中)AI 顧問. If Company is blank, use just Name AI 顧問. |
{{NOTES}} | the Brief context text, prefixed with a source line (see below) |
Escape single quotes by doubling them (O'Brien → O''Brien). Suggested
{{NOTES}} value:
來源:官網 consult 頁面 · Interest: AI 顧問服務
諮詢內容:
<Brief context>
The query returns the new deal_id. Zero rows back = the lead was de-duped at
the DB level → skip its folder/doc too and note it.
b) The Drive project folder. The folder name is [N] Company(Name) —
e.g. [1] 轉職計劃中(Jane). If Company is blank, use [N] Name.
Compute N by listing the parent folder and scanning folders (not files) for
a leading [number]:
mcp__google-workspace__list_drive_items(
user_google_email = "peter_tu@zynkr.ai",
folder_id = "1hkXPX7OXPFOU0BcloPbJSFp8O0zArM8t"
)
N = (highest [number] found among folders) + 1, or 1 if none are numbered.
When you create several folders in one run, keep incrementing locally so each
lead gets a distinct number. List the parent once at the start of the run and
track the counter yourself — don't re-list between leads.
Create the folder:
mcp__google-workspace__create_drive_file(
user_google_email = "peter_tu@zynkr.ai",
file_name = "[N] Company(Name)",
folder_id = "1hkXPX7OXPFOU0BcloPbJSFp8O0zArM8t",
mime_type = "application/vnd.google-apps.folder",
content = " "
)
Capture the new folder's id from the response. (The tool rejects a completely
empty call, so pass a single-space content even for a folder — it's ignored.)
c) The kickoff doc. Read references/starting-doc-template.md and fill its
placeholders ({{DEAL_NAME}}, {{TODAY}}, {{NAME}}, {{EMAIL}}, {{COMPANY}},
{{CONTEXT}}, {{DEAL_URL}}). Create it as a real Google Doc, then move it into
the project folder — this two-step path is reliable, whereas creating a Doc
directly via create_drive_file with the document mime-type returns HTTP 400:
# 1. create the doc (lands in My Drive root)
mcp__google-workspace__create_doc(
user_google_email = "peter_tu@zynkr.ai",
title = "{{DEAL_NAME}} — 專案啟動",
content = "<filled-in template>"
)
# 2. move it into the project folder (capture the doc id from step 1)
mcp__google-workspace__update_drive_file(
user_google_email = "peter_tu@zynkr.ai",
file_id = "<doc id>",
add_parents = "<new folder id from step b>"
)
Capture the doc's link for the report.
d) Link the folder back to the deal so the team can jump from CRM to the
workspace. Append the folder URL to the deal's notes:
UPDATE crm_deals
SET notes = notes || E'\n\n專案資料夾:<folder url>'
WHERE id = '<deal_id>';
5 · Report what happened
End with a compact summary table the user can scan in one glance — one row per
inquiry, including the skipped ones so nothing is silently dropped:
本週顧問諮詢:找到 N 封,建立 M 筆
| # | 姓名 | 公司 | 結果 | 交易 | 資料夾 |
|---|------|------|------|------|--------|
| 1 | Jane | 轉職計劃中 | ✅ 已建立 | [deal](url) | [1] 轉職計劃中(Jane) |
| 2 | GA4 Test | Zynkr Test | ⏭️ 跳過(測試) | — | — |
| 3 | 小明 | 某公司 | ⏭️ 跳過(已存在) | [deal](url) | — |
Then state the headline in prose (e.g. "Created 2 deals + 2 folders + 2 docs;
skipped 1 test and 1 already-processed").
Why it's built this way
- Idempotent over confirm-first. Peter chose autonomous mode, so correctness
can't depend on him eyeballing a preview. The dedup-by-email guard (both up
front and inside the SQL) is what makes an unattended or scheduled re-run safe.
- One SQL statement per deal. The CRM normally creates a deal through a
Next.js server action that also logs a
created activity and find-or-creates
the company/contact. deal-insert.sql reproduces that exact behavior in a
single atomic query, so a skill run leaves the same database state a manual
"Create Deal" click would — no half-built records.
- Numbered folders are sequential, not timestamped. Peter tracks consult
projects by a simple running count (
[1], [2], …). Scanning existing folders
for the max keeps the sequence continuous even across weeks and reruns.
- Skipped rows stay visible. Tests and duplicates are filtered, but always
reported — a silent skip looks identical to "found nothing", and Peter needs to
trust that a real lead never quietly vanished.
Defaults you can safely assume (and Peter can override later)
stage=new · service_tier=advisory (顧問訂閱) · priority=medium ·
lead_source=content (the enum has no "website" value; content is the closest)
· value=NULL (unknown at intake) · close_date=today+30. These live in the SQL;
if Peter asks to change a default, edit references/deal-insert.sql, not the body.
Optional mode — inbound sales / proposal lead intake (folded in from inbound-sales-project-init)
The autonomous weekly flow above handles website consult inquiries
(Interest: AI 顧問服務) → CRM deal + Drive folder + kickoff doc. For a one-off
inbound sales / proposal lead — a forwarded 講座提案 / 合作邀請 / introduction
email — run this interactive branch instead, which additionally captures
attachments and opens a Notion ticket in the Consultant service DB:
- Locate & read the email — a Gmail link / message ID / search hint, or a
pasted body. Capture
Subject / From / Date / Message-ID and every
attachment (filename, mime_type, attachment_id); download attachments so
they can be re-uploaded to Drive.
- Derive a glance-able project name:
<Org/Sender> - <Topic> (<Key hook>)
(e.g. HRAI - Wesley 講座提案 (從痛點到系統的發想框架)). Pick Team(s) — HR/人資 →
Human Resources, revenue-bearing → also Business Development, product/UX →
Product Design, existing client → Account Management — and Priority
(default Medium).
- Re-read the Notion Consultant-service DB schema before writing (don't trust
a frozen snapshot); stop and ask if a property name has drifted.
- Create the Drive folder → a context Google Doc (metadata, inbound
source block, verbatim original letter, proposal breakdown, open questions,
next actions) → move the doc into the folder → re-upload the attachments
into the folder.
- Create the Notion ticket at status Consult intake in the Consultant
service DB and bidirectionally link Drive ↔ Notion.
This branch was merged in from the former inbound-sales-project-init skill
(deprecated 2026-06-06). Its config (inbound-sales-config.md, Notion data-source
id) and references/context-doc-template.md live in git history at
skills/2-sales-consultant/inbound-sales-project-init/ (the commit before the
merge) — restore them into this skill's references/ when you wire the Notion
branch live.