以 AI 机器人身份加入视频会议,提供语音、虚拟形象与屏幕共享四种模式。
集成
inbound-qualification
试用Qualify and route inbound demo requests from your website form: each submission runs a Cargo tool that identifies the company by work email, qualifies it against your ICP, lands the account and contact in the shared models, posts it to Slack, and answers with a booking link or a thank-you; an optional play then tiers and summarises each qualified contact. Triggers: "add a demo form to our website", "handle inbound leads", "route website form submissions", "qualify inbound demo requests", "contact form that books meetings", "inbound lead flow", "Cargo public form", "tier each demo request and summarise it". Cargo project, defineTool, publicForm, form-sdk, definePlay, gtm_accounts, gtm_contacts. Skip when: you want visiting companies that fill nothing in, which is visitor-identification; no site yet, which is website-building first; or leads from a list, which is find-b2b-leads.
它能做什么
Someone asks for a demo on the website, and within seconds their company is identified and qualified, they land in the workspace's accounts and contacts, the team hears about it in Slack, and the page answers them: a booking link if they fit, a thank-you otherwise. There is no backend to run and no…
技能文档
Inbound qualification
The outcome
Someone asks for a demo on the website, and within seconds their company is identified and qualified, they land in the workspace's accounts and contacts, the team hears about it in Slack, and the page answers them: a booking link if they fit, a thank-you otherwise. There is no backend to run and no form vendor: the form is a Cargo tool.
Four pieces make it:
- A tool with a public form.
defineTool("inbound_form", { workflow, publicForm }). The workflow's input is the form's fields;publicFormallows only the site's origin and sets the spam guards. Each submission runs the workflow. - The workflow. Refuse a personal email before anything is paid; identify the company from
the email domain with LinkedIn company data; apply the ICP rules; upsert the account into
gtm_accountsand the contact intogtm_contacts; post one Slack message; return the answer. - The shared GTM models.
gtm_accountsandgtm_contacts, declared exactly as every pipeline declares them, so whatever else the project installs reads the same inbound contacts. - The form on the site. Rendered with the site's own markup and run headless through
@cargo-ai/form-sdk, loaded once the page has hydrated. The tool's uuid arrives as the app env tokeninboundForm.uuid.
Two things worth knowing before you start. The company is enriched, never the person: the person already said who they are. And the server's minimum fill time counts from when the SDK loaded, which is why the site loads it once the page has hydrated: a form loaded on a lost focus event would stamp and submit in the same moment and be refused as a bot.
Example
Add a demo form to www.fabrikam.example: qualified companies get our booking link, the rest a thank-you, and the sales channel hears about every request.
Illustrative output, fictional records:
tool:inbound_form deployed public form on https://www.fabrikam.example
POST /contact ada@northwind.example -> qualified (320 employees, United States)
gtm_accounts northwind.example upserted
gtm_contacts ada@northwind.example lead_source website, inbound_status qualified
#inbound "New inbound, qualified from Ada Lovelace at Northwind"
page "Thanks, let's find a time." [Book a demo]
POST /contact sam@gmail.com -> work_email_required (nothing paid or written)
Put it in your project
This folder is a worked example: real CDK resources written for some other company. The job is
to end up with the code your company would have written, in your project, and an agent does the
adapting. If the cargo-project skill is in your session it carries the long form of this; if
not, this is enough.
- Install it: the CLI does the copy. From inside the CDK project,
cargo-ai cdk add cookbook/inbound-qualificationwrites the tool and models toinfra/inbound-qualification/and this procedure, with thesite/files, to.claude/skills/inbound-qualification/. No project yet?cargo-ai cdk init --cookbook inbound-qualification && cd && npm install. If you are reading this from the project's.claude/skills/, the install already happened; start at step 2. - Reconcile it with what is already declared. The website must already be declared, usually
by
website-building. Agtm_accountsorgtm_contactsanother pipeline declares is the same model: import that one and delete this copy. A Slack or LinkedIn connector the project already has is rewired to. - Wire the site. Follow form: copy
site/into the website app, add the dependency and the env token, link the contact page. - Adapt. Work the sections below in order: What should not change is what you argue back
about (say what breaks, then do it if they still want it); What you can change is what you
offer unprompted; What you will be asked is the floor, and you derive before you ask. Record
what you changed and why under a
## Decisionssection in your copy of this file. - Plan, then stop.
node --import tsx evals/contract.mjsfrom this skill's folder, thennpm run check && cargo-ai cdk planfrom the project root, show the diff, and deploy only on an explicit yes:cargo-ai cdk deploy. If the project deploys from CI, the merged pull request is the deploy; do not also deploy from a laptop. - Verify. Walk Done when line by line with real submissions, and report each with evidence.
What you will be asked
Derive before you ask. An input with a lookup is looked up, not asked.
| Input | Kind | How it is answered | Why it matters |
|---|---|---|---|
ICP rules (infra/tools/inbound-form.ts) | derived | the ICP in context/: headcount band and ISO country codes, confirmed with one live enrichment | Decides who sees the booking link. Rules nobody can read back are the reason inbound stops being trusted. |
site origin (publicForm.allowedOrigins) | derived | the website app's site.json canonicalUrl, without the trailing slash | Every other origin is refused with 403. A missing origin is a form that never submits. |
Slack channel (slackChannelId) | asked | the channel's id, resolved through the Slack connector's autocomplete | Every submission is posted there and only there. |
booking link (bookingUrl) | asked | the team's scheduling page | What a qualified visitor is sent to, on the page, while they are still on it. |
| privacy disclosure | asked | the operator's reviewed page, with a contact form section | The form collects personal data. This skill never writes legal text. |
Checked before moving on, not after the deploy:
- the ICP rules match
context/, and one liveenrichCompanyFromDomainon a known customer returned the ISO country code the rules use allowedOriginsis exactly the canonical origin- the Slack channel id resolves through the connector
node --import tsx evals/contract.mjspasses against the adapted graph
What you can change
The code is a worked example. These reshapes are expected, and the agent offers them rather than waiting to be asked. Every one costs something.
| Variation | When it is right | How | What it costs |
|---|---|---|---|
crm-backed | Contacts belong in HubSpot, Salesforce or Attio | Replace the two model.upsert calls with the CRM connector's upsert (matched on domain and email), same fields (data) | The worked example no longer deploys on a bare workspace; CRM properties must exist first. |
agent-qualification | The ICP does not reduce to headcount and country | Replace the rules with an agent that reads the ICP from context/ and returns a verdict and a reason | Each submission pays for a model call, and the answer is less predictable. |
owner-routing | More than one rep takes inbound | Assign owner_id on the contact by territory or round robin before the Slack post, and mention the owner | An owner table to keep current, and a fallback when nobody matches. |
turnstile | Spam gets through the honeypot, time-trap and rate limit | publicForm.spam.captchaProvider: "turnstile", the site key, captchaSecret: env("TURNSTILE_SECRET"), the widget on the page | A third-party script on the page, with its own privacy disclosure. |
accept-personal-email | A form that is not about the company (newsletter, events) | Drop the free-mail refusal, skip enrichment for those domains | No company to qualify or route; those contacts arrive without an account. |
What should not change
However far you adapt, these hold. Ask for one anyway and the agent tells you what breaks, then does
it if you still want it, and records why under ## Decisions in your copy of this file.
- The public form allows only the site's origin. (
infra/tools/inbound-form.ts)*lets any page on the internet submit into the workspace and spend its credits. - Nothing is paid or written before the cheap checks. A personal email is refused before the enrichment call; the paid call comes before any write.
- The company is enriched, never the person. No person lookup, no personal email or phone search on a submitter.
- The shared models stay shared.
gtm_accountsandgtm_contactskeep their exact definition; what only inbound needs is an added column under a plain name. - No CAPTCHA secret in the repository. It goes through
env(). - The SDK loads once the page has hydrated. (
site/components/inbound-form.tsx) Its load time is what the minimum fill time counts from. Loaded on a focus event, a focus before hydration is lost and the visitor's submission is refused as a bot. Its id cookie is set only on submit. - Marketing email only with the box ticked.
marketing_consentis recorded per contact; a demo request alone is not consent to a newsletter.
Done when
node --import tsx evals/contract.mjspasses: one tool whose public form is enabled for exactly the canonical origin with no CAPTCHA secret in code, a workflow that refuses personal email before the enrichment call and writes onlygtm_accountsandgtm_contacts, both models matching the shared definitions- the plan shows the tool, the models and the connectors, and the app update with the env token
- a work-email submission on the live site returns the right answer on the page, upserts the
account and the contact (with
account_id,lead_sourcewebsite,inbound_status), and posts once to the Slack channel - a personal-email submission is refused on the page, with no enrichment call and no row written
- a submission from another origin is refused with 403, and one sent faster than the minimum fill time is refused
- loading the contact page sets no
cargo_anon_idcookie until a submission - the privacy page has a contact form section the operator reviewed
What it costs
Each submission from a work email pays for one LinkedIn company enrichment; a refused personal
email pays nothing. Read the live price from the integration (cargo-ai connection integration get linkedin) and say it per submission. The server's rate limit caps a single address at ten
submissions a minute, and the origin allow-list keeps other sites from spending it. The models and
the Slack post add nothing.
Composes into
website-building (the site the form sits on), visitor-identification (the companies that read
the site without filling the form in), score-leads (inbound contacts scored against the ICP before
a rep calls), and agentic-engagement (a follow-up to a contact who ticked the consent box).
相关技能
把自然语言描述转为结构化 JSON,并由 mcp-diagram-generator MCP 服务生成 Draw.io、Mermaid 或 Excalidraw 图表文件。
诊断生产力系统反复失效的根因,给出最小干预——容量测算、瓶颈定位、可靠的本地记录。
在本地磁盘以分类纯 Markdown 文件保存需要长期留存的事实,与智能体内置记忆并存。
编写、调试与调优 Playwright 测试,涵盖定位器策略、追踪诊断与 CI 友好的超时配置。
为自然搜索排名提供站点审计、内容撰写与竞品分析。
cargo-ai 的更多技能
浏览全部技能读写团队的 GTM 知识库——一个由 Git 管理的 Markdown 仓库,记录 ICP、人设、玩法、证据、异议等。
用一个 CLI 表面执行、构建、绘制并查询 Cargo 工作流、动作、批量与 AI 代理。
查找、认证并配置 Cargo 工作流节点所需的外部系统连接器。
检查并修改 Cargo 工作区的数据模型,并对存储运行 SQL 查询。
在 Cargo 上跑 B2B 拓客、联系人补全、线索打分与 CRM 同步,每一步都内置授权基础与抑制名单校验。
通过 Cargo CLI 管理 AI agent、release、MCP server 与 memory。