CRM sync keeps your CRM up to date with the people who call your agents. After each analysed phone call, Tried creates or updates the caller's contact in every connected CRM and attaches a note with the call summary. Sync jobs push all contacts at once.
Supported CRMs: HubSpot, Salesforce, Dynamics 365, GoHighLevel and Zoho CRM.
Sync is push only. Changes made in the CRM are not pulled back into Tried contacts.
How it works#
- A phone call ends and post-call analysis is stored.
- Contact field mappings write values from the call onto the Tried contact.
- Contact memory is updated.
- For every enabled CRM connection with Sync after calls on, Tried upserts the contact and logs a call note.
Steps 2 to 4 run after the call_analyzed trigger and do not wait for your webhook. They do not run for silent analysis backfills. A failure in one CRM does not stop the others.
What is sent#
The contact:
| Tried contact field | Sent as |
|---|---|
first_name |
The CRM's first name field |
last_name |
The CRM's last name field. When empty, the first name or the phone number is used for CRMs that require a last name |
email |
The CRM's email field |
phone_number |
The CRM's phone field, and the match key where the CRM supports one |
| Mapped fields | Any CRM field in your field_mapping |
The note:
Tried call call_3ffd3f3831b0d4e1a2c96f07
Mia Chen called to book a check-up and clean. The agent offered Tuesday 9am or Wednesday 2pm and booked Tuesday at 9am.
Duration: 94 s
Ended: user_hangupThe title is Tried call <call_id>. The body is the call summary, the duration in seconds and the disconnection reason, each on its own line when present.
Only calls with a contact are synced. Phone calls get a contact automatically from the caller's number. Web calls have no contact.
Connect a CRM#
On Settings, Integrations, CRM sync, each CRM has a card with a badge: Not connected, Mock or Connected.
- Connect (or Reconnect) starts OAuth. Needs keys
- Use API token (or Configure once connected) opens a dialog with the credential fields and Field mapping (JSON). Leave the credentials empty to keep mock mode.
Once connected, the card shows the last sync time and:
- Run full sync: start a sync job for every contact.
- Sync after calls: the switch for the after-call push (
sync_calls). - Disconnect: delete the connection and its credentials.
The Sync jobs table below the cards lists recent jobs with Status, Contacts, Notes and Failed counts, refreshing every few seconds.
Field mapping#
field_mapping maps a CRM field name to a Tried contact field:
{
"lifecyclestage": "stage",
"hs_lead_status": "lead_status",
"description": "memory"
}- Keys are CRM field API names (
lifecyclestage,Description,telephone1). - Values are
first_name,last_name,email,phone_number,memory(the contact's memory text) or any custom contact field. - Empty values are not sent.
GET /get-crm-schema/:provider lists the CRM's contact fields and your contact fields so you can build the mapping:
{
"provider": "hubspot",
"object": "contact",
"mock": false,
"fields": [
{ "name": "firstname", "label": "First name", "type": "string" },
{ "name": "lifecyclestage", "label": "Lifecycle stage", "type": "enumeration" }
],
"contact_fields": ["first_name", "last_name", "email", "phone_number", "memory", "stage", "lead_status"]
}In mock mode fields is a built-in default list for the CRM. With credentials it is read live from the CRM.
To fill custom contact fields from calls, set up contact field mappings first, then map those contact fields to CRM fields here.
Sync jobs#
A sync job pushes many contacts at once. Each contact is upserted, and when sync_calls is on, a note for that contact's latest ended call is logged too. Each run logs the note again, so running full sync repeatedly adds repeat notes in CRMs that do not de-duplicate them.
curl -X POST https://api.tried.com.au/run-crm-sync-job \
-H "Authorization: Bearer $TRIED_API_KEY" \
-H "Content-Type: application/json" \
-d '{"provider": "hubspot", "kind": "full"}'| Field | Type | Required | Description |
|---|---|---|---|
provider |
string | Yes | hubspot, salesforce, dynamics365, gohighlevel or zoho |
kind |
string | No | full (default): every contact, when sync_contacts is on. contact: only contact_ids |
contact_ids |
string[] | For contact |
Up to 1,000 contact ids |
{
"job_id": "crm_sync_4f8b2e6a0c3d7f1b5e9a2c84",
"provider": "hubspot",
"kind": "full",
"status": "pending",
"stats": { "contact_ids": null },
"error": null,
"mock": false,
"created_timestamp": 1760691000000,
"completed_timestamp": null
}The job runs in the background, up to 10,000 contacts, oldest first. Poll GET /get-crm-sync-job/:id:
{
"job_id": "crm_sync_4f8b2e6a0c3d7f1b5e9a2c84",
"provider": "hubspot",
"kind": "full",
"status": "complete",
"stats": { "contacts": 412, "notes": 388, "failed": 2, "errors": ["+61400000000: POST /crm/v3/objects/contacts returned 400: {...}"] },
"error": null,
"mock": false,
"created_timestamp": 1760691000000,
"completed_timestamp": 1760691094000
}| Field | Description |
|---|---|
status |
pending, running, complete or failed |
stats.contacts |
Contacts pushed |
stats.notes |
Call notes logged |
stats.failed |
Contacts that failed |
stats.errors |
Up to 50 messages, each starting with the phone number |
error |
Set when the whole job failed |
A disabled connection returns 400 when you start a job.
CRM sync API#
| Method and path | Purpose |
|---|---|
GET /get-crm-config |
Every CRM with required_credentials and connected, plus your connections |
PUT /update-crm-config/:provider |
Create or update a connection: credentials, enabled, config |
DELETE /delete-crm-config/:provider |
Remove a connection. Returns 204 |
GET /get-crm-schema/:provider |
CRM fields and contact fields |
POST /test-crm-auth/:provider |
Check the credentials |
POST /run-crm-sync-job |
Start a sync job. Returns 202 |
GET /get-crm-sync-job/:id |
One job |
GET /list-crm-sync-jobs |
The 50 newest jobs |
update-crm-config and delete-crm-config need the admin or developer role.
curl -X PUT https://api.tried.com.au/update-crm-config/hubspot \
-H "Authorization: Bearer $TRIED_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"credentials": { "access_token": "pat-ap1-..." },
"enabled": true,
"config": { "field_mapping": { "lifecyclestage": "stage" }, "sync_calls": true, "sync_contacts": true }
}'| Field | Type | Description |
|---|---|---|
credentials |
object or null | The CRM's credential keys. All required keys must be present. null clears them (mock mode) |
enabled |
boolean | Disabled connections are skipped after calls and refuse sync jobs |
config.field_mapping |
object | CRM field to contact field |
config.sync_calls |
boolean | Push the contact and a call note after every analysed call. Default true |
config.sync_contacts |
boolean | Include all contacts in full sync jobs. Default true |
config is merged with the stored config, so you can send one key.
{
"provider": "hubspot",
"enabled": true,
"mock": false,
"credentials": { "access_token": "******3c1e" },
"required_credentials": ["access_token"],
"config": { "field_mapping": { "lifecyclestage": "stage" }, "sync_calls": true, "sync_contacts": true },
"last_sync_timestamp": null,
"last_modification_timestamp": 1760690800000
}{ "ok": true, "mock": false, "message": "Authenticated." }In mock mode the test returns {"ok": true, "mock": true, "message": "Mock mode: no credentials are set, operations are recorded instead of sent."}.
CRM actions during calls#
CRM sync runs after the call. To create a contact while the caller is still on the line, give the agent a <crm>.create_contact integration app tool.