Skip to main content
Connect a CRM and your agent keeps it fed automatically:
  1. People become contacts. Sign-ups are upserted by email (so re-syncing never duplicates); messaging senders — WhatsApp, Telegram, Slack, and every other connected chat app — are filed automatically from a name, number, or handle; and anonymous website visitors become contacts the moment they share any detail — a name, email, or phone. See Where contacts come from.
  2. Conversations become activities. Each chat is written back as a note (HubSpot, Pipedrive, Zoho) or a completed task (Salesforce), attached to that contact.
Nothing is sent synchronously during a signup or a chat turn. Work goes onto a durable queue that survives restarts, retries with exponential backoff, and dead-letters anything permanently broken so you can see it and act.
Full transcripts are off by default. Out of the box only conversation metadata leaves the platform — channel, contact, message count, last activity. Turn on Include the full transcript to also export the conversation text, which is scrubbed of API keys and bearer tokens first.

The four connectors

Each connector is a full-width card on the CRM page. Click a card’s header to expand it. The Upsert key is how a contact with an email avoids duplicates. A contact that arrives with no email — a messaging sender known only by a handle — is created directly and de-duplicated by Agentheya instead (see Messaging channels).

Connecting

With a token (HubSpot, Pipedrive)

The quickest path. Paste a HubSpot private-app access token or a Pipedrive API token, and for Pipedrive also give your company domain (https://yourco.pipedrive.com — not the shared api.pipedrive.com proxy, which has no web UI and cannot deep-link back to a person). The token is verified against the live API before it is saved, and stored encrypted at rest.

With OAuth

If the platform has registered an app for that provider, Connect with OAuth appears and does everything for you.

With your own OAuth app

Salesforce and Zoho have no static-token mode, so they need OAuth. If this server hasn’t registered a platform-wide app for them, click Use your own OAuth app, register an app with the provider, and paste its client ID and client secret. They are stored encrypted against that one agent, and used for the authorization, the token exchange, and every later refresh. Two providers also need a login host:
  • Salesforce — sandboxes authenticate at https://test.salesforce.com, never at the production login.salesforce.com. Leave the field blank for production orgs.
  • Zoho — accounts are data-center bound (accounts.zoho.eu, .in, .com.au, .jp, .ca, …). An authorization code minted in one data center cannot be redeemed in another. The callback detects your data center automatically, so you normally leave this blank.
Set your redirect URI on the provider’s app to https://<your-domain>/api/crm-oauth/callback.

What gets synced

Three toggles per connector. They save the moment you flip them.

Where contacts come from

Most CRM contacts are keyed on an email address — that is how connectors upsert without ever making a duplicate. Messaging channels are the exception: a chat-app sender is filed from whatever they arrive with — any one of a name, a phone number, or a messaging handle is enough, no email required. So for each place your agent talks to people, the question is simply: what identifying detail arrives on its own, and does the agent still have to ask for more? That splits every surface into two kinds:
  • Known-identity surfaces — the person has an account, arrived with their email attached, or messaged from a chat app (which always carries at least a handle). The contact is created automatically; the agent does nothing.
  • Cookie-only surfaces — the person is just a browser cookie or an IP (a website chat or widget). There is nothing a CRM can file them under, so the agent must collect one identifier first — a name, an email, or a phone number — and save it with the capture_contact tool (or a pre-chat form collects it up front).
A contact is always filed in the CRM that owns the surface it came in on. A visitor on your widget lands in your CRM; a visitor on a subscriber’s hosted page or widget lands in that subscriber’s CRM — never yours. The agent cannot choose the destination; the layer is fixed by the surface.

Surface by surface

Messaging channels

Every chat app your agent is connected to files its senders into the CRM automatically — you don’t ask for anything. A CRM contact needs only one identifier, and a messaging sender always brings at least their platform handle, so a contact is always created. Whatever richer detail the channel exposes (a display name, a phone number) is added on top. The contact is labelled with the best identifier available — the display name if there is one, otherwise the phone number, otherwise the handle — and de-duplicated on a stable per-sender key, so the same person messaging again updates the same record instead of creating a new one. It carries no email until someone provides one; use capture_contact to attach an email later.
mqtt, redis and db channels are machine-to-machine routes, not people — they never create contacts. An imap mailbox is treated as inbound email, where the sender’s address is the contact.

The capture_contact tool

When a CRM is connected with New users become contacts turned on, your agent gains a capture_contact tool. It is how an anonymous visitor becomes a real contact: the agent calls it the moment someone shares any identifier — a name, an email, or a phone number. At least one of name, email, or phone is required — any single one is enough to create the contact. The agent is instructed to ask for whichever the visitor will share, and never to invent details. The tool files the contact into the correct CRM by itself — the agent never chooses which one — and it is hidden on preview/test chats so testing never pollutes your CRM.

Getting an identifier in the first place

On anonymous surfaces the agent can only file a contact after it has at least one identifier — a name, an email, or a phone number — so the job is to reliably collect one. Three ways, strongest first:
  1. Tell the agent to ask. Add an instruction on the Instructions / Purpose tab so the agent collects contact details at the right moment — for example:
    When a visitor wants a quote, a demo, a callback, or to be contacted, ask for their name, email, or phone number (any one is fine) before continuing, then call capture_contact to save them. Ask once, politely — don’t nag.
    The agent then asks naturally in the conversation and files the contact itself. Use it to add richer details to a messaging contact (chat-app senders are already saved automatically from their handle / name / number), and as the only way to capture a contact on a one-off, cookie-only web chat.
  2. Add a pre-chat form (widgets). Give your widget’s pre-chat form a name, email, or phone field (any of them — an email dedupes best, but any one creates the contact). Whatever a visitor types there is filed as a contact automatically on their first message — no asking required.
  3. Require sign-in. Gate a hosted page so a visitor must register or log in before chatting. Every conversation then belongs to an account with a known email, and contacts are created automatically — at the cost of a higher barrier to starting a chat.
Messaging senders are saved automatically from whatever they arrive with — a handle alone is enough, so no email is required and you don’t need to do anything. (A messaging contact has no email until someone provides one, so it won’t merge with an email-based record for the same person; capture_contact is how you attach that email later.) Everywhere else — website chat, widgets — a browser cookie or IP is not something a CRM can file, so there the agent still has to obtain one identifier — a name, email, or phone — first.

Configuration

API host — the per-tenant host the provider handed us at connect time (Salesforce instance_url, Zoho api_domain, your Pipedrive company domain). Captured automatically; change it only if your tenant moved. External-id field (Salesforce only) — the name of a custom external-id field on Contact, for example AgentheyaUserId__c. Set it and every sync becomes a true PATCH-by-external-id upsert keyed on our stable id, which survives a user changing their email address. It also gives email-less messaging-sender contacts a native Salesforce dedup key, instead of relying on Agentheya’s own de-duplication. Leave it blank and Salesforce is matched on email instead.

Testing before you trust it

Every connector card has an Add a test user panel. Fill in an email and a name, then:
  • Preview payload — renders the exact HTTP request that would be sent: method, URL, headers (with the token redacted) and the full JSON body. Nothing leaves the server. This works before you connect, so you can see precisely which fields would be exported.
  • Send test contact — pushes that one synthetic contact straight to your CRM, bypassing the queue so you see the result immediately: the record id, a deep link into the CRM (when the provider returns one — Salesforce always, Pipedrive on your company domain), the round-trip time, and the body that was actually uploaded.
  • Send test conversation — attaches a sample conversation note to that test contact, so you can check how a real transcript will look.
  • Remove test contact — deletes it again.
Test connection does a cheap authenticated read and reports the account it reached and how long it took.

Keeping it healthy

The Recent sync activity table at the bottom of the page lists the newest jobs across every connector: what was synced, its status, how many attempts it took, and the error if it failed. A connector shows Needs attention after a permanent failure — a revoked token, a missing scope, a wrong API host. The next successful sync clears it on its own. Completed jobs are pruned after seven days. Dead-lettered jobs are kept, because they are the only record that something never made it.

Removing a user

Deleting a subscriber from the Users page queues a delete against every connected CRM, and drops the local mapping.
This archives the CRM record (HubSpot’s recycle bin, Salesforce’s, and so on) rather than irreversibly erasing it. That is deliberate: HubSpot’s permanent GDPR delete also blocklists the email address, so the person could never be re-added to your CRM by any means. If you owe a data-subject erasure, perform it in the CRM directly.

Subscriber CRM sync

Everything above is your (the agent creator’s) CRM connection. Subscribers who resell or embed your agent get their own, completely separate CRM sync on their personal dashboard — with the same connectors, toggles, and test panel described here. Their page states it plainly:
Connect your own CRM and your customers are added to it automatically, with their conversations logged as activities. This connection is yours alone — it is separate from anything the agent’s creator has configured, syncs only your customers, and the creator cannot see it.
So a subscriber’s CRM connection:
  • Is scoped to their own downstream customers — the people who chat on their hosted page or widget — and never touches yours.
  • Is invisible to you, the creator: you can’t view, edit, or read their connection, and they can’t see yours.
  • Runs the same machinery — durable queue, retries, dead-letter, the capture_contact tool, and the metadata-only default — independently, per subscriber.
A contact is always filed in the CRM that owns the surface it arrived on; the agent never chooses the layer. See Where contacts come from for how each surface maps to a CRM.

Scopes for your own OAuth app

If you register your own app, grant it exactly these:
Zoho’s ZohoCRM.org.READ is what backs Test connection — without it the test reports a failure even though syncing works.Pipedrive ignores scopes passed at authorization time and takes them from the app’s own configuration, so granting them in the Developer Hub is the only thing that matters.
If a provider’s card says the server has no OAuth app for it, bringing your own is the supported path — nothing else is required from an administrator.