CRM Integrations Guide
ICTContact talks to a CRM in two separate directions, and each one lives on its own screen. You pull contacts out of a CRM into a contact group under CRM > Customer > Contact Groups. You push call results back into a CRM, or into any other web service, under Contact Center > Messages > Integrations. You can use either one on its own.
Direction 1: import contacts from a CRM into a Contact Group
Every contact group has a Group Type. The default is Internal Contacts, a plain list you fill from a CSV file or from another group. The other types are CRM feeds. When you pick one, the form shows that CRM's connection fields, and ICTContact fetches the records from the CRM itself.
- Go to CRM > Customer > Contact Groups and click Add new.
- Under Group Description, fill in Title and Description.
- Set Group Type to the CRM you want. The list has Internal Contacts plus the CRM entries your account has permission for (see the connector list below).
- Fill in the fieldset that appears for that CRM. The fields are the CRM's own API settings. Here are three examples from the code:
- Zoho CRM Feed Configuration: Accounts URL (default https://accounts.zoho.com), API Base (default https://www.zohoapis.com/crm/v6), Client ID, Client Secret, Refresh Token, Module (Contacts or Leads), Page Size (max 200), Import Limit (0 = unlimited).
- Pipedrive Feed Configuration: API Base URL (default https://api.pipedrive.com/api/v1), API Token, Import Limit (0 = unlimited), Page Size (default 100).
- Salesforce Feed Configuration: Login URL, Consumer Key, Consumer Secret, Refresh Token, API Version, Object (Contact or Lead), Page Size (LIMIT), Import Limit (0 = unlimited).
SuiteCRM asks for Base URL, Grant Type (Client Credentials or Password), Client ID, Client Secret, Username and Password (password grant only), Module, Page Size and Import Limit. Agile CRM asks for Subdomain, Login Email and API Key. Most connectors follow one of these two shapes: an OAuth refresh token, or a static API key.
- Click Save.
Two things catch people out here.
First, the Import Contacts (Optional) and Scan for CNAM sections disappear as soon as you choose a CRM type. That's expected. A CRM group is filled by the feed, not by a file.
Second, saving the group doesn't contact the CRM. The fetch runs when a campaign that uses this group starts. At that point the campaign daemon:
- calls the feed,
- pulls records page by page up to the Import Limit,
- normalises the phone numbers and writes the contacts,
- links each one to the CRM record in
contact_link(remote id and module), - puts them in the campaign spool.
So if the credentials are wrong, you'll find out in the campaign log, not on the group form. Check logs/campaign-<id>/daemon.log and logs/gui.log for lines starting with Creating instance of and Loading feed.
Group Type is locked after the first save. If you want to switch a group from Internal to a CRM feed, create a new group.
Lead Groups and Deal Groups don't offer CRM types. Their Group Type list has one entry (Internal Leads, Internal Deals).
Direction 2: push call results with an Integration
An Integration is a saved outbound request. The campaign daemon runs it at the end of a call and fills in tokens such as the contact's phone number and the call status. You create it once under Contact Center > Messages > Integrations, then attach it to a campaign, a feedback option or an IVR node.
The list shows Integration Name, Type, Description and Operations (Edit). The top buttons are search, New Integration and Refresh. The form has Save, Reset and Cancel. Delete Selected removes the rows you've ticked. The search panel filters on Integration, Integration Type and Date.
Creating an integration
- Click New Integration.
- Under Integration Info, enter Integration Name (required), choose Integration Type and add a Description if you like.
- Fill in the fieldset for the type you chose (details below).
- Click Save.
You can't change Integration Type after saving. The field is hidden on the edit screen. If you picked the wrong type, create a new record.
The three built-in types
SOAP WSDL API. Integration Configuration has WSDL URL, SOAP Action, and nine Parameter# rows (Name / Value). In each row, type the parameter name, then either pick a token from the value dropdown or choose Custom value and type your own text. The Show available tokens link lists what you can insert. Request parameters and the SOAP reply are written to logs/gui.log as Soap request parameters: and Soap results:. A SOAP fault is logged as Soap error!.
Custom Http request. Fields: Remote URL, Http Method (GET, POST, PUT, DELETE), Request data, Request headers, Response encoding (Plain or JSON Encoded). The insert token button next to Remote URL and Request data drops a token at the cursor. Request data is only sent for POST and PUT. Put one header per line in Request headers, for example Content-Type: application/json. Response encoding is stored, but the current code doesn't apply it. The reply is logged as text either way. For every run you'll see Http request url:, Http request body: and Http results: in logs/gui.log.
Queue for callback. Callback Configuration has Callback Campaign (a dropdown of your campaigns), Schedule After and Expiry After (both duration pickers). When it runs, the contact is inserted into the selected campaign as a new spool entry. If the call came through an agent, the new entry keeps the same extension. It's handy for "call me back later" from a survey or an IVR key press. The log line is Callback saved as spool_id:.
The CRM connectors
These are the connector types registered in integration.php on the current build, in the order they appear in the Integration Type list: Agile CRM, ActiveCampaign, Apptivo CRM, Capsule CRM, Close CRM, Copper CRM, Creatio CRM, Dynamics 365, Freshsales, Keap (Infusionsoft), Less Annoying CRM, Monday.com, Streak CRM, YetiForce CRM, Zendesk Sell, Vtiger CRM, SuiteCRM, Salesforce, Zoho CRM, SugarCRM, Pipedrive. Each entry only appears if your user has the matching permission, so a tenant may see fewer than 21.
Each connector shows a fieldset named "
| Connector | Fields |
|---|---|
| Zoho CRM | Accounts URL, API Base, Client ID, Client Secret, Refresh Token, Module (Contacts / Leads) |
| Salesforce | Login URL, Consumer Key, Consumer Secret, Refresh Token, API Version, Default Object (Contact / Lead) |
| Pipedrive | API Base URL, API Token, Custom Status Field Hash (optional), Create Pipedrive Activity for each call (Yes / No) |
| SuiteCRM | Base URL, Grant Type, Client ID, Client Secret, Username and Password (password grant only), Default Module (Contacts / Leads) |
| Agile CRM | Subdomain, Login Email, API Key |
Here's what a CRM connector does on each call, in order:
- It receives the contact id, campaign id, spool id, call status and disposition id from the daemon.
- It reads the local contact (name, phone, email), the campaign name, the agent, and the disposition with its comments, call duration and recording URL.
- It looks for the CRM record using the remote id already stored in
contact_link(set when the contact was imported from that CRM). If there isn't one, it searches by phone, then by email. - If nothing matches, it creates the record.
- It updates the record and adds a note or activity carrying the call result.
- It stores the new remote id back in
contact_link, so the next call doesn't have to search again.
Disposition mapping. Before sending, the connector translates the ICTContact disposition name through the crm_disposition_map table (scope integration, keyed by the integration id). If there's no mapping row, the ICTContact name is sent unchanged. The current build has no screen for this table. The mapping is maintained in the database. Pipedrive also needs the Custom Status Field Hash filled in before it writes the mapped value to a custom field. Leave it blank and only the activity is created.
Connector log lines go to logs/gui.log, prefixed with the type in capitals, for example [ZOHOCRMHTTP] authenticate FAILED http=401.
