# External CRM Integration & Contact Syncing

## Overview

QuickReply.ai can sync your **Contacts/Leads** to any external CRM that exposes HTTP APIs or webhooks.

With this integration you can:

* **Create new leads** in your CRM whenever QuickReply.ai captures a new contact
* **Update existing leads** in your CRM whenever contact attributes change in QuickReply.ai
* Optionally **fetch and de-duplicate** leads in your CRM before creating new ones

QuickReply.ai calls **your CRM APIs** over HTTPS (JSON or form-encoded), using your auth mechanism (e.g. static API key).

***

## How it Works (High-level Flow)

{% @mermaid/diagram content="sequenceDiagram
participant QR as QuickReply.ai
participant CRM as External CRM

```
Note over QR: Contact changes in QuickReply.ai

alt New Lead Flow
    QR->>CRM: Create Lead
    CRM-->>QR: lead_id returned
    QR->>QR: Store lead_id
else Existing Lead Flow
    QR->>CRM: Update Lead (using stored lead_id)
    CRM-->>QR: Update confirmed
    QR->>QR: Lead data refreshed
end

Note over QR:  Sync completes (debounced ~5 mins)" %}
```

{% stepper %}
{% step %}
**A contact changes in QuickReply.ai**

* New contact created (from Popup / Website Chat/ WhatsApp chat or Instagram Chat, etc.)
* Existing contact updated (name, email, tags, attributes, etc.)
  {% endstep %}

{% step %}
**QuickReply.ai prepares a payload**

Typical fields:

* `phone` – with country code (recommended primary identifier)
* `email` – optional identifier
* `name` – full name of the contact
* `custom_fields` – key–value attributes mapped to your CRM schema
* Optionally: `lead_id` / `contact_id` – if previously returned by your CRM
  {% endstep %}

{% step %}
**QuickReply.ai calls your CRM endpoints**

Depending on configuration, QuickReply.ai will:

* Directly call **Create Lead** for new contacts
* Call **Fetch Lead** first (using phone/email/lead\_id), then decide to **Create** or **Update**
* Call **Update Lead** whenever an existing contact changes in QuickReply.ai
  {% endstep %}

{% step %}
**CRM returns its Lead/Contact ID**

* On success, your CRM returns a unique identifier (e.g. `lead_id` / `contact_id`).
* QuickReply.ai stores this and uses it for subsequent updates to avoid duplicates.
  {% endstep %}

{% step %}
**Sync Timing**

* Contacts are synced in **near real-time**.
* By default, QuickReply.ai waits for \~5 minutes of inactivity before syncing to avoid noisy updates and excessive API calls.
  {% endstep %}
  {% endstepper %}

***

## Pre-requisites

To use this integration, you need:

* An active **QuickReply.ai** account
* An active **CRM** with APIs to:
  * Create a lead/contact
  * Fetch a lead/contact
  * Update a lead/contact
* A static **Auth Key / token** or similar mechanism to secure your APIs

***

## API Contract (Your CRM)

QuickReply.ai expects your CRM to expose **three** HTTP endpoints:

1. [**Create Lead / Contact**](#id-1.-creating-a-new-lead-crm-api)
2. [**Fetch Lead / Contact**](#id-2.-fetching-an-existing-lead-crm-api)
3. [**Update Lead / Contact**](#id-3.-updating-an-existing-lead-crm-api)

You can use any URL structure you like (e.g. `/api/leads/create` instead of `/crm/create`) as long as it matches the contract below.

{% hint style="success" %}
🔐 All endpoints should:

* Be served over HTTPS
* Accept JSON payloads (preferred) or form-encoded body
* Validate a shared `Auth-Key` (or equivalent) in the headers
  {% endhint %}

### 1. Creating a new Lead - CRM API

#### Endpoint

```http
POST https://yourdomain.com/crm/create
```

#### Headers

| Name                                       | Type                 | Description                            |
| ------------------------------------------ | -------------------- | -------------------------------------- |
| Auth-Key<mark style="color:red;">\*</mark> | Authorization Header | Your shared API key / token (required) |

#### Request Body

| Name                                    | Type            | Description                                                                                                                      |
| --------------------------------------- | --------------- | -------------------------------------------------------------------------------------------------------------------------------- |
| phone<mark style="color:red;">\*</mark> | string          | Phone number with country code                                                                                                   |
| name<mark style="color:red;">\*</mark>  | string          | Full name of the contact                                                                                                         |
| email                                   | string          | Email address of the contact                                                                                                     |
| custom\_fields                          | Key-Value Pairs | Extra Attributes which you want to sync to the CRM, where the **key-names** are the respective **schema field names** in the CRM |

{% hint style="success" %}
Fields marked with <mark style="color:red;">\*</mark> are Required.
{% endhint %}

#### Sample cURL request

```bash
curl --location --request POST 'https://yourdomain.com/crm/create' \
--header 'Auth-Key: YOUR_AUTH_KEY' \
--header 'Content-Type: application/json' \
--data-raw '{
  "phone": "[CONTACT_PHONE_WITH_COUNTRY_CODE]",
  "name": "[CONTACT_NAME]",
  "email": "[CONTACT_EMAIL]",
  "custom_fields": {
    "key1": "value1",
    "key2": "value2",
    "key3": "value3"
  }
}'
```

#### API Response

{% tabs %}
{% tab title="Success (Status 200)" %}

```json
{
    "lead_id": "[CONTACT_LEAD_ID]",
    "status": "Successfully Created"
}
```

{% endtab %}

{% tab title="Failure (Status XXX)" %}

```json
{
    "status": "Creation Failed",
    "reason": "Error message"
}
```

{% endtab %}
{% endtabs %}

### 2. Fetching an Existing Lead - CRM API

Used by QuickReply.ai to **check if a lead already exists** before creating a new one or to fetch your CRM’s `lead_id`.

#### Endpoint

```http
GET https://yourdomain.com/crm/fetch
```

{% hint style="success" %}
If your stack prefers `POST` for this, you can use `POST /crm/fetch` with the same body
{% endhint %}

#### Headers

| Name                                       | Type                 | Description                            |
| ------------------------------------------ | -------------------- | -------------------------------------- |
| Auth-Key<mark style="color:red;">\*</mark> | Authorization Header | Your shared API key / token (required) |

#### Request Body (or query parameters)

At least one of these should be accepted:

| Name     | Type   | Description                             |
| -------- | ------ | --------------------------------------- |
| phone    | string | Fetch by phone (with country code)      |
| email    | string | Fetch by email                          |
| lead\_id | string | Fetch by CRM lead ID (if already known) |

{% hint style="warning" %}
At least 1 field should be available.&#x20;
{% endhint %}

#### Sample cURL Request

```bash
curl --location --request GET 'https://yourdomain.com/crm/fetch' \
--header 'Auth-Key: YOUR_AUTH_KEY' \
--header 'Content-Type: application/json' \
--data-raw '{
  "phone": "[CONTACT_PHONE_WITH_COUNTRY_CODE]",
  "email": "[CONTACT_EMAIL]",
  "lead_id": "[CONTACT_LEAD_ID]"
}'
```

#### API Response

{% tabs %}
{% tab title="Success (Status 200)" %}

```bash
{
  "lead_id": "[CONTACT_LEAD_ID]",
  "lead_fields": {
    "phone": "[CONTACT_PHONE_WITH_COUNTRY_CODE]",
    "name": "[CONTACT_NAME]",
    "email": "[CONTACT_EMAIL]",
    "custom_fields": {
      "key1": "value1",
      "key2": "value2",
      "key3": "value3"
    }
  }
}
```

{% endtab %}

{% tab title="Failure (Status XXX)" %}

```json
{
    "status": "Not Found",
    "reason": "No matching lead"
}
```

{% endtab %}
{% endtabs %}

### 3. Updating an Existing Lead - CRM API

#### Endpoint

```http
POST https://yourdomain.com/crm/update/{{lead_id}}
```

or

```http
PUT https://yourdomain.com/crm/update/{{lead_id}}
```

#### Headers

| Name                                       | Type                 | Description                            |
| ------------------------------------------ | -------------------- | -------------------------------------- |
| Auth-Key<mark style="color:red;">\*</mark> | Authorization Header | Your shared API key / token (required) |

#### Path / Query Parameter

| Name                                       | Type   | Description                                                                                                 |
| ------------------------------------------ | ------ | ----------------------------------------------------------------------------------------------------------- |
| lead\_id<mark style="color:red;">\*</mark> | string | <p>Respective Lead ID in the CRM<br><br><em>(The same as returned in response from Fetch Lead API)</em></p> |

#### Request Body

| Name                                    | Type            | Description                                                                                                                      |
| --------------------------------------- | --------------- | -------------------------------------------------------------------------------------------------------------------------------- |
| phone<mark style="color:red;">\*</mark> | string          | <p>Phone Number of the user<br><em>(With Country Code)</em></p>                                                                  |
| name<mark style="color:red;">\*</mark>  | string          | Full Name of the user                                                                                                            |
| email                                   | string          | Email of the user                                                                                                                |
| custom\_fields                          | Key-Value Pairs | Extra Attributes which you want to sync to the CRM, where the **key-names** are the respective **schema field names** in the CRM |

{% hint style="warning" %}
Fields marked with <mark style="color:red;">\*</mark> are Required.
{% endhint %}

#### Sample cURL Request

```bash
curl --location --request POST 'https://yourdomain.com/crm/update/[CONTACT_LEAD_ID]' \
--header 'Auth-Key: YOUR_AUTH_KEY' \
--header 'Content-Type: application/json' \
--data-raw '{
  "phone": "[CONTACT_PHONE_WITH_COUNTRY_CODE]",
  "name": "[CONTACT_NAME]",
  "email": "[CONTACT_EMAIL]",
  "custom_fields": {
    "key1": "value1",
    "key2": "value2",
    "key3": "value3"
  }
}'
```

#### API Response

{% tabs %}
{% tab title="Success (Status 200)" %}

```json
{
  "lead_id": "[CONTACT_LEAD_ID]",
  "status": "Successfully Updated"
}
```

{% endtab %}

{% tab title="Failure (Status XXX)" %}

```json
{
    "status": "Update Failed",
    "reason": "Error message"

```

{% endtab %}
{% endtabs %}

***

## Webhooks & Security Requirements

* Endpoints must be **publicly accessible HTTPS URLs**
* Accept **JSON** (recommended) or Form POST bodies
* Support a **static auth mechanism**, such as:
  * `Auth-Key` header
  * `Authorization: Bearer <token>` header
  * A shared secret in the body/query (less secure; not recommended if you can avoid it)

***

## Implementation Steps

1. **Design your CRM endpoints**
   * Decide URL structure for `create`, `fetch`, and `update`
   * Make sure they match the field structure above
2. **Implement and test locally**
   * Implement your APIs and test with curl/Postman
   * Confirm responses and error codes are correct
3. **Share details with QuickReply.ai**

   Send the following to your POC or QuickReply.ai's support:

   * Base URL and full paths for Create/Fetch/Update
   * Auth type and keys (in a secure channel)
   * Any special field mappings (e.g. `utm_campaign` → `crm_campaign_code`)
4. **QuickReply.ai configuration**
   * QuickReply.ai team configures your endpoints and mapping
   * They enable contact sync and test with sample contacts
   * Typical go-live time is around **5 business days**, depending on complexity.

***

## FAQs

<details>

<summary>Is the sync real-time?</summary>

It’s **near real-time**. By default, QuickReply.ai waits \~5 minutes after the last change before syncing to avoid too many small updates. This delay can be adjusted (shorter intervals may incur additional charges).

</details>

<details>

<summary>Can we use a single endpoint for both create and update?</summary>

Yes. As long as your endpoint can decide whether to create or update based on the payload (e.g. presence of `lead_id`, or unique `phone`/`email`), you can use a single URL.

</details>

<details>

<summary>Can the payload structure be customized to our CRM?</summary>

Yes. Field names under `custom_fields`, or even the top-level field naming, can be customized by the QuickReply.ai team to match your CRM’s schema.

</details>

<details>

<summary>What happens if our API fails (4xx/5xx)?</summary>

QuickReply.ai will treat this as a sync failure and will retry if there is new change in contact details or attributes.&#x20;

</details>

<details>

<summary>We don’t have a Fetch API. Can we still integrate?</summary>

Yes. In that case QuickReply.ai will typically only use the **Create** and **Update** APIs, and you must handle de-duplication based on phone/email on your side.

</details>
