Skip to content

Main Inbound Zap (347165805)

Name: 01 - Inbound Responses < 5min [Updated Close CRM]
Status: Live (v25 as of Mar 26, 2026)
Trigger: New email in intake@creatorsagency.co INBOX
Full code doc: https://docs.google.com/document/d/1NsGbyswG_kFhuw58zfrHxW6_Odc-7NQwzlIgjX1QLQzo/edit


What It Does

When a brand emails our intake address, this Zap:

  1. Parses the sender's name and email from headers
  2. Classifies the email (brand, agency, or spam/irrelevant)
  3. Finds or creates a Close CRM lead
  4. Identifies which creator they're asking about
  5. Enriches the brand via Apollo
  6. Assigns an agent via round-robin
  7. Drafts a reply via Claude and sends it from the agent's email
  8. Logs everything to Airtable and Mailchimp

Steps

Step 1 — Gmail Trigger

Watches intake@creatorsagency.co INBOX for new emails. Passes all headers and body downstream.

Step 2 — Code: Parse Sender

Resolves the true sender email and human name from all available headers (From, Reply-To, X-Original-Sender, etc.) using a scoring system. Also:

  • Runs a DNS check via Cloudflare to confirm the sender domain exists (catches fake domains)
  • Checks against a SPAM_DOMAINS, SPAM_EMAILS, and INTERNAL_DOMAINS blocklist
  • Outputs: sender_email, sender_name, sender_domain, is_spam, greeting

Step 3 — ChatGPT: Classify

GPT-4o-mini classifies the email as:

  • BRAND_DIRECT — the sender's own company wants to sponsor a creator
  • AGENCY — an agency reaching out on behalf of a brand client
  • OTHER — service pitches, SEO spam, job inquiries, etc.

Also extracts brand_name and brand_domain from the email body for agency emails.

Step 4 — Filter: Stop if OTHER or spam

Stops the Zap if classification is OTHER, or if is_spam = true.

Step 5 — Airtable: Find Thread ID

Checks if this Gmail thread has already been processed. Uses Thread ID field to deduplicate — prevents sending two auto-replies to the same thread.

Step 6 — Filter: Stop if already replied

Stops if a matching thread ID record already exists in Airtable.

Step 7 — Code: Find/Create Close Lead

5-layer lookup to find the right Close CRM lead:

  1. Exact sender email match via contact search
  2. AGENCY path: if GPT classified as AGENCY and sender domain matches an existing lead → add contact to that agency lead and return it. No brand lead created. (e.g. ejl@obvious.ly → routes to the Obviously lead, not a new La Roche-Posay lead)
  3. Sender/brand domain match via URL search
  4. Name-keyword similarity fallback
  5. Create new lead if all miss — for AGENCY with no existing lead, creates using sender domain (not brand domain) so the agency is the lead, not the brand

New leads get status = Inbound Follow-Up. Backfills Lead Type (Brand / Brand Side Agency) on leads that are missing it. If sender uses a generic email (gmail.com, etc.) and no brand domain found, sets slack_review_needed = true for manual review.

Updated March 27, 2026 — Agency routing fix. Previously, AGENCY emails would create separate brand leads (La Roche-Posay, Valentino Beauty, Vichy, Kiehl's were all incorrectly created as brand leads from Obviously agency contacts). Now correctly routes to the agency lead.

Step 8 — Code: Match Creator

GPT-4o-mini scans To, CC, Subject, and Body to match the email to a creator on the Airtable roster. Outputs creator_name and confidence level.

Step 9 — Airtable: Fetch Creator Record

Pulls full creator data (audience summary, etc.) to give Claude context for the reply.

Step 10 — Code: Apollo Brand Enrichment

Enriches the brand via Apollo API and homepage scrape. Skips enrichment for AGENCY category (enriches the actual brand, not the agency).

Step 11 — Code: Write Enrichment to Close

Writes industry, company size, and LinkedIn to the Close lead. Writes description + keywords as a Close custom object. Skipped for AGENCY leads.

Step 12 — Code: Find/Assign Agent

If lead has an existing owner, resolves their name, email, calendar link, and Slack ID. If no owner, runs round-robin assignment: picks the agent with the oldest last_assigned_at timestamp from Airtable, updates the timestamp, and PUTs the owner to Close. If slack_review_needed, posts an alert to #crm-review-queue mentioning the assigned agent.

Step 13 — Close: Log Custom Activity

Creates a pinned 01 - Deal Create custom activity on the Close lead, attributing it to the assigned agent.

Step 14 — Anthropic (Claude): Draft Reply

Claude Sonnet drafts a reply email using:

  • Brand's original message
  • Apollo company description + keywords
  • Creator's audience summary
  • Agent's name and calendar link

Voice guidelines: typing from phone between calls, under 120 words, no templates. If brand email body is blank, Claude writes a short generic reply (no [NO_CONTENT] — fixed in v25).

Step 15 — Code: Format HTML

Converts Claude's plain text reply to HTML paragraphs, fixing sign-off formatting.

Step 16 — Gmail: Send Reply

Replies in the original Gmail thread FROM the agent's email address, BCC to bccsales@creatorsagency.co.

Step 17 — Mailchimp: Add/Update Subscriber

Adds the brand sender to the Creators Agency Mailchimp audience with tag "Inbound".

Step 18 — Airtable: Create Record

Creates a record in Inbound Messages table with all metadata: email, thread ID, sender name, domain, subject, original message, AI reply, assigned agent, reply sent = true, timestamps.

Step 19 — Filter: New or lapsed leads only

Only continues to Step 20 for newly created leads or leads with Lapsed status.

Step 20 — Airtable: Update Record (conditional)

Second Airtable update for new/lapsed leads only.


Key Design Decisions

Why BCC to bccsales@? Team visibility and audit trail. Every auto-reply has a copy in that inbox.

Why no [NO_CONTENT]? Old behavior returned [NO_CONTENT] if the email body was blank. Now Claude writes a short generic opener instead (fixed v25, Mar 26, 2026).

Why Close CRM lookup before the reply? We need the lead's existing owner (if any) for agent assignment. An existing lead with Charlie as owner should reply as Charlie, not start a new round-robin.

Why does Step 7 flag generic-domain senders? An email from info@gmail.com has no company domain to search Close with, so the lead gets created with Unknown - [Name]. The agent needs to manually identify the company.


What Gets Written to Airtable

When a new email is processed, a record is created in Inbound Messages with:

  • email — sender's email (used for follow-up matching)
  • Thread ID — Gmail thread ID (used for dedup)
  • name — sender's parsed name
  • from_domain — sender's domain
  • Subject — email subject
  • Inbound Message — full email body
  • Message ID — Gmail message ID
  • assigned_agent_email — which agent was assigned
  • AI Reply — what Claude wrote
  • Reply Sent — true
  • routed_at — auto-set to creation time (used by follow-up Zap for Day 3/7/11/14 timing)
  • Creator Name — matched creator (or "unknown")
  • Time Received — date
  • In Follow-Up — true (set by follow-up Zap when first follow-up runs)