Contact Synchronization

A Dealer Management System can exchange consumer information on behalf of its (mutual) dealers. In this recipe we'll explain realtime synchronization of contact data. In particular, we'll discover how to exchange application links to consumer records in both the DMS application and Web1on1, so dealers can easily find corresponding contacts.

This recipe describes a method of exchanging those URLs, and enable cross-application linking on the individual consumer level for any dealer using both applications.

Use Case

Situation: Dealers use both the DMS and Web1on1 applications side by side to conduct business. Both applications have detail panels representing individual contacts, and each contact record can be accessed by a distinct URL in both apps.

Problem: There is no cross-referencing or data sharing between the applications. To access consumer information in the other system, the dealer has to use the app's search functionality, which is slightly inefficient.

Solution: By having the DMS store the Web1on1 conversation ID as part of their consumer record, it can link to the customer conversation at Web1on1 from within the DMS application. Likewise, we will store the DMS's contact URL as part of the Web1on1 contact record.

Each frontend application could then be adapted, to display a link to the other system in its contact details panel.

link-exchange

Solution Details - How contact linking could work

We'll create a small software application that receives and processes contact update events coming from both systems. It does this by exposing two HTTP endpoints: one where the DMS will post updates to, and another to receive contact updates from Web1on1 (a Web1on1 bot webhook).

We assume that for any dealer, a unique contact can be identified by an email address or a phone number.

When a DMS contact is created or updated, the DMS should call the /updatecontact endpoint. Our app will find the contact at Web1on1, and add the DMS URL to the Web1on1 contact record (for the relevant dealer). In our recipe, a View in DMS link will appear in the Web1on1 contact profile:

contact-link

When a Web1on1 contact is created/updated, our application simply logs the event. Your implementation would typically make a network request (API call, queueing system) to the DMS to update its contact record.

Implementation

For our purpose, we will create a simple DMS ContactLinker chatbot at Web1on1. The bot will:

  • Listen to contact.create and contact.update events, to pass on to the DMS,
  • Accept contact update requests from the DMS, and
  • Talk to the Web1on1 API when processing DMS contact updates.

First, we'll set up and configure a Contact Linker chatbot. We will use the example in the Webhooks section as a basis. Deploy that webhook code, and create the ContactLinker bot in the bot store:

create-a-bot

In the 'webhook' tab of the bot details panel, subscribe to the contact.create and contact.update events:

bot-webhook

Next, we'll modify the code to suit our purpose.

A contact is created or updated at Web1on1

We will have our bot's webhook subscribe to the contact.create and contact.update webhook events. In this example we just log some details like the Web1on1 URL for the contact conversation.

Your own implementation would probably make an API call to the DMS at this point, to add this link to the contact details. Make sure to also record the Web1on1 organization ID.

A contact is created or updated at the DMS

We assume the DMS is capable of making a HTTP request when a contact is created or updated. The DMS might even have a feature that offers dealers to set up their own webhook for this purpose.

Our running bot instance exposes an HTTP endpoint at the /updatecontact path to receive contact details: the backend system (DMS) should post all contact creations and updates to this URL. For this recipe example, we accept the following fields:

  • id: DMS contact ID
  • organization: Web1on1 organization ID. required
  • email: required, unless telephone provided
  • telephone: required unless email provided

The /updatecontact webhook handler finds the contact at Web1on1 by email or phone, and adds meta information to the Web1on1 contact record. Specifically, a link to the DMS will be added to the contact.links property.

The contact.links array of external links, when specified, are shown as part of the contact profile in the Web1on1 agent console. A link has the following properties:

  • label: DMS contact ID
  • icon: A favicon URL
  • url: The external URL to link to

In our example, these values are hard-coded, except the URL - that is built to include the DMS contact id provided to /updatecontact.

Full Script

'use strict';

const express = require('express');
const bodyParser = require('body-parser');

const app = express();
const chipchat = require('chipchat');
/**
 * TODO(developer): export bot API token as the TOKEN environment variable.
 */
const api = new chipchat({
    token: process.env.TOKEN
});

/**
 * The backend system (DMS) posts all contact creates and updates to this
 * endpoint. The endpoint function finds the contact at Web1on1 by email or
 * phone, and adds a link to the DMS as a contact.links object.
 */
app.post('/updatecontact', bodyParser.json(), async (req, res) => {
    console.log('updatecontact', JSON.stringify(req.body));
    if (req.body.id && req.body.organization &&
        (req.body.email || req.body.telephone || req.body.phone)
    ) {
        const options = { organization: req.body.organization };
        if (req.body.telephone || req.body.phone) {
            options['profile.telephone'] = req.body.telephone || req.body.phone;
        }
        if (req.body.email) {
            options['profile.email'] = req.body.email;
        }
        const contacts = await api.contacts.list(options);
        if (contacts.length === 1) {
            const contact = contacts[0];
            const dmsUrl = {
                label: 'View in DMS',
                url: `https://example.com/customer/${req.body.id}/`,
                icon: 'https://www.dealerweb.org/user/themes/halcyon/img/favicon.ico'
            };
            const links = contact.links || [];
            if (!links.find((link) => link.url === dmsUrl.url)) {
                await api.contacts.update(contact.id, { links: [...links, dmsUrl] });
                return res.status(201).json({
                    status: 'contact updated',
                    id: contact.id,
                    conversation: contact.conversation
                });
            }
        } else {
            console.log(`Found ${contacts.length} contacts`);
        }
    }
    return res.status(200).json({ status: 'contact not updated' });
});

/**
 * Web1on1 webhook - verification
 */
app.get('/webhook', (req, res) => {
    if (req.query.type === 'subscribe' && req.query.challenge) {
        // return challenge
        return res.end(req.query.challenge);
    }
    return res.status(403).end();
});

/**
 * Web1on1 webhook - subscribed events
 */
app.post('/webhook', bodyParser.json(), (req, res) => {
    if (req.body.event === 'contact.create'
        || req.body.event === 'contact.update') {
        const baseUrl = 'https://app.web1on1.chat/conversations';
        const { conversation, profile } = req.body.data.contact;
        if (profile.telephone || profile.email) {
            const web1on1Url = `${baseUrl}/${conversation}`;
            console.log(`Updating contact:
                phone ${profile.telephone}
                email ${profile.email}
                url ${web1on1Url}`
            );
            /**
             * TODO(developer): post contact update to external
             * system here...
             *
             * updateDMSContact
             */
        }
    }
    return res.status(200);
});

app.listen(4002, () => console.log('[Web1on1] Webhook is listening'));

Start synchronizing

Deploy your implementation to a production environment, and publish your bot in the Web1on1 bot store to see it used by our dealers. Once your bot is running, you need to verify the webhook in the bot details panel (tab: webhook) to enable it.

When a Web1on1 contact is updated, the above recipe just logs the URL; your implementation should notify the DMS backend.

When a DMS contact is updated, the DMS should call the /updatecontact endpoint of the bot application:

contact-update

This call will find the Web1on1 contact, by email address for a particular dealer. Next, a link to the DMS will be added to this contact's links. Contact links are shown as part of the contact profile:

contact-link

The View in DMS link will open a new browser tab, and load the contact record in the DMS application. It links to:

https://example.com/customer/gusty-goose-46/

Further work

This is just a proof-of-concept

You should add robust guardrails when further productizing this. Authentication, parameter and payload validation and proper error handling come to mind.

Seed contacts on bot activation

When a dealer activates the bot, it could retrieve all dealer contacts from the DMS, or have the DMS call the /updatecontact endpoint for all of them.

Updating contact details

In addition to exchanging URLs, you can adapt this script to also update contact details like name, email or phone number at the DMS when they're updated at Web1on1.

Synchronizing users

In a similar way, users can be synchronized across systems. The bot would subscribe to the user.create and user.update webhook events to do so.