Skip to main content
Status: In Development · Playground demo coming soon. The recipe below is complete and runnable today; only the hosted playground showcase is pending.
A B2B SDR that runs structured outbound sequences while keeping a real picture of each prospect: what’s been said, what they replied to, what they ignored, and what they care about. Memory is per-prospect; the agent doesn’t restart from zero on every touch.

What you’ll build

An outbound SDR agent that:
  • Researches the prospect: company, role, recent signals
  • Drafts personalized first-touch: grounded in researched facts, not generic
  • Runs multi-touch sequences: email + LinkedIn DM + follow-up, with reply detection
  • Adapts on replies: interest, objection, unsubscribe, out-of-office
  • Books meetings when intent is detected
Est. build time: 90 minutes (most of it wiring email/LinkedIn/calendar tools).

When to use this recipe

Build this if:
  • You run cold outbound sequences and want them to feel less cold
  • You’ve got research data (your enrichment provider, company news, signal data) the agent should ground in
  • Multi-touch is the norm: a prospect sees the SDR over weeks, not minutes
  • You want the agent to learn what works for each persona over time

Architecture at a glance

The sequence orchestrator is dumb. The agent is smart. Memory is the bridge.

Stack

Prerequisites

  • A Synap API key, see Authentication
  • Email sender domain + DKIM / SPF / DMARC set up
  • Enrichment data source (Clearbit, Apollo, Crunchbase, or your own CRM)
  • Python: Python 3.11+
  • TypeScript: Node 18+ and Python 3.11+ on the host
Cold outbound is regulated (CAN-SPAM, GDPR, CASL). Make sure your sender list is permissioned and every email has a working unsubscribe. The agent doesn’t enforce this; you do.

Install

Build it

1. Identity & scoping

  • customer_id = "<your-company>"
  • user_id = <prospect ID>: your stable internal ID, NOT the email (people change emails)
  • conversation_id: one per prospect (long-running)
  • Metadata: account_id so you can roll up “everything on Acme Corp” across all prospects at that company
conversation_id, user_id, and customer_id must be valid UUIDs. Generate the per-prospect id with str(uuid.uuid4()) (Python) or crypto.randomUUID() (JS); map any non-UUID internal id to a deterministic UUID with uuid.uuid5(...).

2. Research tools

3. Action tools

4. The agent

5. The orchestrator (scheduler)

The orchestrator is a thin cron / queue worker. It picks prospects whose next touch is due and asks the agent to handle it.

6. Reply handling

Email reply webhooks (Postmark inbound, SES SNS, etc.) drop into a handler that ingests the reply and asks the agent to respond.

Run & verify

Touch 1 (first email)
Prospect replies (3 days later)
Touch 5, three months later
The agent didn’t restart from zero. It remembered the objection, watched for a signal, and re-engaged at the right moment.

Customize / extend

  • Salesforce CRM integration → tools wire into Salesforce; see Salesforce: Enterprise Sales Assistant for the read-side pattern.
  • LinkedIn channel → add send_linkedin_dm and linkedin_reply webhook. Same memory shape.
  • Reply review queue → for sensitive industries, don’t auto-send. Have the agent draft into a queue your humans approve.
  • Account-based marketing flavor → group prospects by account_id in metadata and have the agent coordinate touches across the buying committee.
  • Replay historical CRM activity → seed prospect memory with prior touches from your CRM at launch. See Patterns → Replay History.

Troubleshooting

Drafts feel generic
  • The agent isn’t pulling enrichment or signals before drafting. Sharpen the system prompt; require those tool calls.
  • Or your enrichment source is sparse; feed the agent more.
Agent re-pitches points the prospect already rejected
  • Memory ingestion of replies isn’t working, or synap_search isn’t called before drafting. Audit both.
Sequences fire too frequently
  • The orchestrator’s due-rules are the issue, not the agent. The agent should still see “last touch was 2 hours ago” in memory and refuse; add that check to the system prompt.
Unsubscribes not honored
  • The orchestrator must check unsubscribe state before each send. Don’t rely on the agent to remember; set a hard flag in your DB the worker checks first.