initiate-inboxmate-knowledge
InboxMate Architecture & Data Model
This is a context-loading skill. Read it, internalize the model, then proceed with whatever task you were doing.
The Five Systems
InboxMate spans five independent systems. Each owns its data and exposes its own API. No system directly reads another's database — they're connected through shared IDs.
┌──────────────┐ ┌──────────────┐ ┌──────────────┐
│ Twenty CRM │ │ AgentHub DB │ │ Notif. Svc │
│ (GraphQL) │ │ (Supabase) │ │ (REST) │
│ │ │ │ │ │
│ Opportunities│────▶│ demo_pages │ │ email_drafts │
│ Companies │ ↑ │ agents │ │ email_queue │
│ Campaigns │ │ │ knowledge_* │ │ │
│ People/Notes │ │ │ │ │ │
└──────────────┘ │ └──────────────┘ └──────────────┘
│ │ │
│ serves demo │
│ page content │
│ ▼ │
│ ┌──────────────┐ │
│ │ Demo Pages │ │
│ │ (frontend) │ │
│ │demo.inboxmate│ │
│ │.psquared.dev │ │
│ └──────────────┘ │
│ │
│ ┌──────────────┐ │
└──│ Ackee │◀────────────┘
│ (analytics) │ (tracks views)
└──────────────┘
The Key ID: How Objects Link Across Systems
The CRM Opportunity is the central record. Everything else hangs off it.
CRM Opportunity
├── .id → used as crm_opportunity_id in email_drafts
├── .demoId → points to demo_pages.id in AgentHub DB
├── .companyId → points to CRM Company
├── .campaignId → points to CRM Campaign
└── .demoStatus → state machine (see below)
AgentHub: demo_pages
├── .id → this IS the demoId stored in CRM
├── .agent_id → points to agents table
└── .crm_opportunity_id → backlink (often NULL, don't rely on it)
AgentHub: agents
├── .knowledge_bucket_ids → uuid[] pointing to knowledge_buckets
└── .published_at → non-null means live
AgentHub: knowledge_buckets → knowledge_bucket_items → knowledge_bucket_chunks
└── items have: source_type ('url' | 'knowledge_link' | 'text' | 'pdf')
└── items have: embedding_status ('pending' | 'processing' | 'completed' | 'failed')
Notification Service: email_drafts
├── .crm_opportunity_id → links back to CRM Opportunity
├── .crm_company_id → links to CRM Company
├── .draft_type → 'outreach' | 'followup'
├── .parent_draft_id → followup points to its outreach draft
└── .status → 'DRAFT' | 'QUEUED' | 'SENT' | 'FAILED'
Critical rule: The CRM opportunity's demoId field is the only reliable cross-system link between CRM and AgentHub. The demo_pages.crm_opportunity_id column exists but is often NULL. When resolving CRM → demo page, always query CRM for demoId, don't query Supabase by crm_opportunity_id.
Object States
CRM Opportunity.demoStatus (the pipeline state machine)
┌─────────────────┐
│ PENDING_REVIEW │ ← demo built, awaiting QA
└────────┬────────┘
pass │ │ fail
▼ ▼
┌────────────┐ ┌───────────┐
│ OK_TO_SEND │ │ NEEDS_FIX │ → fix → back to PENDING_REVIEW
└─────┬──────┘ └───────────┘
│ campaign + email sent
▼
┌──────────┐
│ SENT │ ← outreach email sent, outreachSentAt set
└─────┬────┘
│ follow-up sent
▼
┌─────────────────┐
│ FOLLOW_UP_SENT │
└─────────────────┘
Skip states (terminal): SKIP_FAULTY_WEBSITE, SKIP_IRRELEVANT, SKIPPED, DISQUALIFIED
CRM Opportunity.stage
| Stage | Meaning |
|---|---|
SCREENING |
Outreach phase (all demo pipeline work happens here) |
MEETING |
Prospect responded / call scheduled |
PROPOSAL |
Quoted or decision-maker demo |
CUSTOMER |
Paying customer |
Email Draft.status
DRAFT → (schedule) → QUEUED → (send job) → SENT
→ FAILED
Follow-up drafts are created alongside initial outreach (linked via parent_draft_id), with send_after_days controlling when they queue.
Campaign Structure
Campaigns group opportunities into sendable batches. A campaign has offerExpiresAt and offerText which are injected into demo pages dynamically (not stored on demo_pages directly — the API merges them at read time with a 5-min cache).
Data Flow: Demo Creation → Email Outreach
1. Lead Found → CRM
- Company + Person created in CRM
- No opportunity yet
2. Demo Built → AgentHub + CRM
- Agent created via MCP (
create_agent) - Knowledge bucket created, URLs scraped, items embedded
- Demo page created via MCP (
create_demo_page) → returnsdemo_pages.id - CRM Opportunity created at stage=SCREENING, demoStatus=PENDING_REVIEW
demoId= the demo page UUIDdemoUrl=https://demo.inboxmate.psquared.dev/?id=<demoId>
3. QA Review → CRM status change
/review-demosfetches PENDING_REVIEW opportunities- Checks agent, colors, knowledge, greeting
- Sets demoStatus to OK_TO_SEND or NEEDS_FIX
4. Campaign Planned → CRM
/plan-campaigncreates a CRM Campaign- Links selected opportunities via
campaignId - Sets
offerTextandofferExpiresAton the campaign
5. Email Drafts Created → Notification Service
/setup-email-draftsqueries OK_TO_SEND opportunities- Creates outreach draft + follow-up draft pair per opportunity
- Drafts stored with
crm_opportunity_idfor back-linking
6. Emails Scheduled & Sent → Notification Service + CRM
- Admin schedules drafts → status: QUEUED, enters
email_queue - Send job fires → status: SENT, CRM updated to demoStatus=SENT
- Follow-up auto-queues based on
send_after_days
7. Prospect Claims Demo → AgentHub
- Prospect clicks CTA on demo page
- Claim flow: clones agent + knowledge to new account
- Marks
demo_pages.claimed = true - Sends Telegram notification + welcome email
- Updates CRM stage/status
API Quick Reference
| System | Endpoint | Auth Token |
|---|---|---|
| CRM | POST https://crm.psquared.dev/graphql |
$PSQUARED_CRM_TOKEN |
| MCP | POST https://app.psquared.dev/api/mcp |
$NUXT_MCP_DEMO_TOKEN |
| Demo API | GET https://app.psquared.dev/api/demo/<id> |
None (public) |
| Notif. Svc | https://notifications.psquared.dev/drafts/* |
$EMAIL_DRAFT_ONLY_BEARER |
| Sanity Check | POST https://notifications.psquared.dev/drafts/sanity-check |
$EMAIL_DRAFT_ONLY_BEARER |
| Ackee | POST https://ackee.psquared.dev/api |
$ACKEE_TOKEN |
| Supabase | mcp__plugin_supabase_supabase__execute_sql |
project: fevtfywriufbqnvbgyrm |
Sanity Check
The notification service exposes a sanity check that validates demo agent health. Accepts three input modes:
// By opportunity IDs
{ "crm_opportunity_ids": ["uuid", ...] }
// By campaign
{ "campaign_id": "uuid" }
// By draft IDs
{ "draft_ids": ["uuid", ...] }
Returns per-item: healthy boolean + issues array + warnings array. Checks:
- Health failures (unhealthy): demo/agent not found, not published, empty buckets, text-type items, failed/processing embeddings, 404 content detected, >50% items under 50 tokens
- Warnings (non-blocking): missing source URLs, some low-token items, thin avg tokens, claimed demos
- Also checks: demo API reachability, email draft link correctness
Response includes demo_page_id for cross-system debugging, and avg_tokens for knowledge quality assessment.
Common Gotchas
-
CRM uses inline IDs, not parameterized variables. Twenty CRM's GraphQL does NOT support
$id: ID!parameters. Always inline UUIDs directly in the query string. -
Don't
source .env— values contain semicolons. UseReadtool orgrepto extract specific values. -
MCP token is
NUXT_MCP_DEMO_TOKEN(notINBOXMATE_DEMO_MCP_TOKEN). Some older skills use the wrong name. -
knowledge_link= URL-sourced. Inknowledge_bucket_items.source_type, items scraped from URLs are stored asknowledge_link, noturl. Both are valid URL-sourced content. -
Campaigns manage deadlines, not individual demos. Never set
offerExpiresAtorofferTextdirectly ondemo_pages— these come from the campaign and are merged dynamically at API read time. -
Follow-up emails: never mention "follow-up" in the subject. Body must be unique, not a generic template. Always include
variablesin PUT body when updating drafts. -
Never guess URLs for scraping. Always discover real pages from the homepage navigation first. Generic patterns (
/kontakt/,/ueber-uns/,/leistungen/) fail on 60%+ of sites. -
Tavily failures → WebFetch fallback.
scrape_and_build_knowledgeuses Tavily. Some sites block it (all URLs timeout). When this happens, use WebFetch to scrape pages manually and add viaadd_to_bucketwithsourceUrl. Never leave a bucket empty. -
Skip junk URLs:
/wp-json/,/xmlrpc.php,/feed/,/favicon.ico,.webp,.ico— these produce garbage knowledge items. -
Content limit is 50K characters per knowledge item. Tavily advanced mode can return large pages — they won't be truncated.
-
404 pages are auto-detected by
scrape_and_build_knowledge— pages with "nicht gefunden"/"not found"/"404" in the title are rejected and returned in thefailedarray. No need to check manually.
What Skill Do I Need?
Based on what you're trying to do, use:
| Goal | Skill |
|---|---|
| Need new leads | /find-leads [N] |
| Create demos for CRM leads | /inboxmate-batch-demo or /inboxmate-demo <company> |
| QA existing demos | /review-demos |
| Fix flagged demos | /fix-demos |
| Check if demos are healthy | /sanity-check |
| Group demos into a campaign | /plan-campaign |
| Create outreach emails | /setup-email-drafts [campaignId] |
| Edit email drafts | /refine-email-drafts |
| Check how outreach is doing | /check-outreach-status |
| Full funnel analytics | /check-demo-analytics |
| Upgrade demo knowledge | /refurbish-demos |
| Change pricing | /price-change |
| Deep debugging / raw queries | /analyse-inboxmate |
Pipeline Order
If starting from scratch, follow this sequence:
/find-leads → /inboxmate-batch-demo → /review-demos → /fix-demos (if needed)
→ /sanity-check → /plan-campaign → /setup-email-drafts → /refine-email-drafts (if needed)
→ 👤 send → /check-outreach-status → /check-demo-analytics
More from psquared-development/psquared-skills
inboxmate-demo
Set up a personalized InboxMate demo chatbot for a sales prospect. Use when asked to create a demo, set up an InboxMate playground, or prepare a chatbot demo. Guides the full pipeline: research company, scrape content, call MCP, deliver playground URL.
33find-leads
Find new B2B leads in Germany for InboxMate outreach. Validates each lead against legal requirements (UWG), checks email is publicly visible, documents justification, and adds to CRM. Germany only — Austrian law (TKG) is stricter. Pass the number of leads to find as a parameter.
32review-demos
Review InboxMate demos waiting for QA. Finds CRM opportunities at SCREENING with demoStatus=PENDING_REVIEW, opens each demo link, checks quality, and flags as OK_TO_SEND or NEEDS_FIX with a note explaining why.
24setup-email-drafts
Create email drafts for approved InboxMate demos. Verifies all demos are ready, pulls contacts from CRM, creates CRM tasks, and creates draft emails via the notification service. Run after /review-demos has processed all pending demos.
24inboxmate-batch-demo
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.
24check-outreach-status
Check status of sent demo outreach emails and monitor follow-up draft status. Follow-up drafts are created by /setup-email-drafts — this skill only monitors. Run periodically after /setup-email-drafts has been used.
23