skills/inboxmate-batch-demo/SKILL.md
Batch-create InboxMate demos for CRM prospects. Queries Twenty CRM for companies without opportunities, validates their websites, creates demos for valid ones, and marks unreachable/outdated ones as DISQUALIFIED. Optional parameter: track ('inbox' builds Demo-Postfächer via /inboxmate-inbox-demo, 'chatbot' builds chatbot demos; default reads the Track from each company's qualification note).
npx skillsauth add psquared-development/psquared-skills inboxmate-batch-demoInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
/inboxmate-batch-demo [track] — track is optional:
inbox — build a Demo-Postfach for every company in the batch via /inboxmate-inbox-demo (no agent, no widget; opportunity gets demoType: INBOX).chatbot — build chatbot demos via /inboxmate-demo for every company (demoType: CHATBOT).Track: CHATBOT | INBOX set by /find-leads) and route per company; if no Track noted, default to CHATBOT.All required tokens are in the .env file in the current working directory (the agenthub repo root). Read it at startup to get:
PSQUARED_CRM_TOKEN — Bearer token for Twenty CRM GraphQL APINUXT_MCP_DEMO_TOKEN — Bearer token for the InboxMate MCP serverOPENBRAND_API_KEY — API key for OpenBrand brand color/logo extractionDo this first: Read .env from the current directory and extract these values. If any are missing, stop and ask the user.
Before starting, ask the user for the offer deadline:
When should the demo offers expire? This sets the countdown timer on all demo pages in this batch. Examples: "in 14 days", "2026-04-01", "end of month"
Wait for the answer. Convert to an ISO 8601 date. Pass this deadline to every demo created in the batch — do NOT ask again per company.
Announce:
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ InboxMate Batch Demo Pipeline Offer deadline: [date] Reading .env for CRM and MCP tokens... Querying CRM for unprocessed prospects... ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Read PSQUARED_CRM_TOKEN from .env and use it to query the Twenty CRM (GraphQL at https://crm.psquared.dev/graphql) for companies that do NOT yet have an opportunity. Use this approach:
outreachFor contains INBOX_MATE (up to 100)The remaining companies are unprocessed InboxMate prospects.
Critical filter: Only fetch companies tagged for InboxMate outreach. Never process PERSONAL_ONLY companies — these are Martin's private contacts. Companies tagged PSQUARED_SERVICES only are handled by /find-services-leads and its own funnel, not this pipeline. Companies tagged INBOX_MATE_MUSEUM only (the curated museum list) are also excluded automatically — they get a separate museum-specific outreach flow when one is built.
curl -s -X POST https://crm.psquared.dev/graphql \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $PSQUARED_CRM_TOKEN" \
-d '{"query":"{ companies(filter: { outreachFor: { containsAny: [INBOX_MATE] } }, first: 100) { edges { node { id name domainName { primaryLinkUrl } outreachFor } } totalCount } }"}'
Legacy companies (pre-field): Companies created before the
outreachForfield existed haveoutreachFor: nulland will NOT match this filter. That is intentional — they need to be tagged before automation touches them. If the user wants to process legacy untagged companies, they should tag them first (or we do it under explicit instruction).
Announce:
Found [N] unprocessed prospects.
If there's a specific company or list the user wants to process, use that instead.
For each prospect, before running the demo pipeline:
Use WebFetch on the company's domain (from CRM domainName.primaryLinkUrl).
Skip the prospect if ANY of these are true:
Do NOT ask the user what to do. Auto-skip and mark in CRM immediately.
First, check if the Company object has a field like idealCustomerProfile (ICP) or a rating/status field you can use. Run this introspection query once at the start:
query { __type(name: "Company") { fields { name type { name kind ofType { name } } } } }
If there's a usable boolean/enum field (e.g. idealCustomerProfile): set it to false or the "not a fit" value to mark the company directly.
Regardless, also create a DISQUALIFIED opportunity so the company is excluded from future batch runs:
mutation CreateOpportunity($data: OpportunityCreateInput!) {
createOpportunity(data: $data) { id name stage }
}
Variables:
{
"data": {
"name": "[Company] — Website not suitable",
"stage": "DISQUALIFIED",
"companyId": "[companyId]"
}
}
Announce for each skip:
SKIP: [Company] — [reason]Never pause to ask. Just mark and move to the next prospect.
Prospects with a working, current website proceed to Step 3.
Announce:
[N] prospects ready for demo creation, [M] skipped.
For each valid prospect, invoke the /inboxmate-demo skill (or follow the inboxmate-demo SKILL.md pipeline).
Process one prospect at a time — do not parallelize MCP calls.
After each demo is created:
SCREENING / PENDING_REVIEW with the demo URL — verify it was createdAnnounce after each:
DONE: [Company] — [playgroundUrl]
After all prospects are processed:
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
BATCH COMPLETE
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Demos created: [N]
- [Company 1] → [url]
- [Company 2] → [url]
Skipped (website issues): [M]
- [Company A] — [reason]
- [Company B] — [reason]
Already processed (had opportunity): [K]
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
| Signal | Action | |--------|--------| | HTTP error / timeout | SKIP — "unreachable" | | Parked / expired domain | SKIP — "parked domain" | | "Coming soon" / "Under construction" | SKIP — "placeholder page" | | No real content (just logo + contact form) | SKIP — "no meaningful content" | | Copyright year 2+ years behind current | SKIP — "outdated (copyright YYYY)" | | Broken images, dead links, 90s design | SKIP — "outdated/abandoned" | | Social media profile only | SKIP — "no website (social only)" | | No domain in CRM | SKIP — "no domain on file" | | Working site with real content | PROCEED with demo |
tools
Set up a personalized InboxMate INBOX demo (Demo-Postfach) for a sales prospect: a public, read-only seeded inbox showing 5-7 pre-triaged emails in their industry's language, with categories, routing and ready AI drafts. Use for email-automation outreach (the €49-349 product), NOT for chatbot outreach. No agent is created.
development
Build InboxMate demos AND write personalised outreach drafts in a single pass per company — eliminating the double-research that happens when /inboxmate-batch-demo and /setup-email-drafts run separately. Use when kicking off a new campaign where the campaign already exists (plan via /plan-campaign first). For each target company, dispatches ONE subagent that researches the site, builds the demo, creates the CRM opportunity, and drafts the outreach email — reusing the same research across all three. After all subagents return, runs a single batch call to auto-generate follow-ups.
testing
Autonomous pilot for the InboxMate EMAIL outreach (Demo-Postfach/INBOX track). Assesses where the inbox pipeline stands (leads → demos → review → campaign → drafts) and executes the next sensible step end-to-end, always finishing with the inbox sanity check and a summary of what the user should do next (ideally: just schedule the mails). Runs in save mode by default: orchestration + all quality gates on the top model, data collection on haiku subagents, content generation on sonnet subagents (pass 'full' to disable). Use when asked to 'advance the email outreach', 'run the inbox pipeline', or 'what's next for the Demo-Postfach motion'.
tools
Generate a polished psquared client offer as a multi-page PDF (title, project description, screenshots, Angebot/pricing, AGB). Walks the user through gathering inputs (or accepts a JSON config), renders branded HTML templates with Playwright in two passes (title page edge-to-edge + body pages with margins and pagination), then merges with pdf-lib.