Skip to main content
Status: Live in Playground · Try it: synap.maximem.ai/playground Open the playground and pick Salesforce: Enterprise Sales Assistant to see the reference implementation running.
A sales-rep-facing assistant that lives next to Salesforce. It knows the rep’s accounts, open opportunities, the deal narrative across recent activity, and what the rep has tried before. It drafts outreach, prepares for calls, logs activity, and updates the CRM, all while keeping the rep’s tone and territory context across sessions.

What you’ll build

A chat agent for sales reps that:
  • Pulls live CRM state: accounts, opps, contacts, recent activity
  • Remembers rep context: territory, ICP, tone of voice, deal-specific narratives
  • Drafts outreach in the rep’s voice, grounded in account history
  • Updates Salesforce: log calls, advance opp stages, create follow-up tasks
Est. build time: 45 to 60 minutes (most of that is Salesforce auth + field mapping).

When to use this recipe

Build this if:
  • Your reps work out of Salesforce and want an assistant that already knows their book of business
  • You want per-rep tone/persona memory (so drafts sound like the rep, not a generic LLM)
  • You need bi-directional CRM I/O (read accounts, write activities)
  • Account narratives span weeks and need to persist across sessions

Architecture at a glance

The rep is the user_id; the rep’s org is the customer_id. Account-specific notes get tagged in metadata so they’re recallable per-account.

Stack

Prerequisites

  • A Synap API key, see Authentication
  • A Salesforce Connected App with OAuth + offline refresh tokens for the reps
  • Python: Python 3.11+
  • TypeScript: Node 18+ and Python 3.11+ on the host
TypeScript recipe runs on Node only. Pin Next.js route handlers to export const runtime = "nodejs". See Installation → JavaScript / TypeScript SDK.

Install

Configure

Build it

1. Identity & scoping

  • customer_id = <salesforce_org_id>: one tenant per Salesforce org
  • user_id = <salesforce_user_id>: the sales rep
  • conversation_id = <session UUID>
  • Account-scoped memories get account_id in metadata so you can retrieve “everything we know about Acme Corp” later
conversation_id must be a valid UUID; generate it with crypto.randomUUID() (JS) or str(uuid.uuid4()) (Python), as shown below. Any Salesforce id used for user_id/customer_id that isn’t already a UUID should be mapped to a deterministic UUID (e.g. uuid.uuid5(...)).

2. Salesforce tools

These wrap your Salesforce client. Authenticate per-rep using their stored refresh token before each call.
The SOQL examples above use string interpolation for clarity. In production, parameterize all SOQL inputs to avoid injection; both simple-salesforce and jsforce support bind parameters.

3. System prompt

System prompt

4. Wire memory + LLM + tools

5. Tagging account-scoped memories

For richer recall (“what’s the latest on Acme?”), tag memories with the account when the conversation is about a specific account:
You can then filter retrieval by metadata.account_id for account-specific summarization.

Run & verify

Session 1
Session 2 (next day, fresh conversation)
Memory carries across days. CRM stays the source of truth for structured deal state; Synap carries the narrative.

Customize / extend

  • Slack interface → wrap handle_message in a Slack bot. See Patterns → Slack Bot.
  • Replay historical activity on initial setup so the assistant has years of context from day one. See Patterns → Replay History.
  • Per-territory scoping → if a single rep covers multiple territories that shouldn’t share context, use metadata.territory to filter.
  • HubSpot / other CRMs → replace the Salesforce tool layer; the memory pattern is identical.
  • SDR variant → for top-of-funnel work, see AI SDR.

Troubleshooting

Account narrative goes missing across sessions
  • Confirm customer_id is the org ID, not the rep ID. Reps within the same org should see shared account context if you want that.
  • If you want strict per-rep silos, keep customer_id = org_id and rely on user_id for isolation; Synap scopes searches automatically.
Drafts don’t sound like the rep
  • The rep hasn’t given enough signal yet. Capture explicit corrections (“don’t open with ‘I hope this finds you well’”) with synap_store.
  • Or seed memory at onboarding with 5 to 10 of the rep’s recent sent emails as historical documents.
Tools timing out
  • Salesforce REST has per-org rate limits. Cache get_account and get_opportunities for the duration of one chat session.
TS route fails on Vercel
  • Pin export const runtime = "nodejs". JS SDK requires Node + Python on the host.