> ## Documentation Index
> Fetch the complete documentation index at: https://kb.vinfer.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Sync VInfer Neuron Call Data with Your CRM in Real Time

> Automatically push call dispositions, transcripts, and NeuronLens QA scores from VInfer to your CRM in real time using webhooks or the REST API.

After every call Neuron completes, your CRM should reflect what happened — lead status updated, call notes logged, transcript attached, QA score filed. Doing this manually is slow, error-prone, and simply doesn't scale when you're running thousands of calls a day. VInfer gives you two primary integration paths — real-time webhooks and REST API polling — plus native connectors for popular CRM platforms. This guide walks you through each approach so your CRM stays in sync without any manual intervention.

## Why CRM Integration Matters

<CardGroup cols={2}>
  <Card title="Eliminate Manual Updates" icon="ban">
    Without integration, agents must manually update lead status, paste call notes, and attach recordings after each call — at scale, this is unsustainable and introduces data lag.
  </Card>

  <Card title="Real-Time Lead Status" icon="bolt">
    With webhooks, your CRM reflects the call outcome seconds after the call ends — "interested" contacts move to Hot Lead instantly, so your sales team can act while intent is fresh.
  </Card>

  <Card title="Full Conversation Context" icon="file-lines">
    Push transcripts, QA scores, and NeuronLens summaries directly to the contact record so anyone on your team has the full picture without listening to a recording.
  </Card>

  <Card title="Closed-Loop Reporting" icon="chart-pie">
    Combine VInfer disposition data with your CRM pipeline metrics to measure actual revenue impact of your outbound and inbound campaigns end to end.
  </Card>
</CardGroup>

***

## Integration Methods

<Tabs>
  <Tab title="Webhooks (Recommended)">
    VInfer sends an HTTP POST to your CRM endpoint immediately after each call event. This is the fastest integration method — your CRM is updated within seconds of a call completing, with no polling overhead.

    **Best for:** Real-time lead status updates, immediate follow-up workflows, and high-volume campaigns where polling delays are unacceptable.
  </Tab>

  <Tab title="REST API Polling">
    Your system periodically calls the VInfer API to fetch completed calls and push the data to your CRM. Simpler to set up if your CRM doesn't support inbound webhooks, but introduces a lag equal to your polling interval.

    **Best for:** CRMs that don't expose a public webhook endpoint, batch-processing workflows, or low-volume use cases.
  </Tab>

  <Tab title="Native Connectors">
    VInfer offers pre-built connectors for select CRM platforms that handle field mapping, authentication, and sync logic out of the box. Contact [VInfer support](mailto:support@vinfer.ai) to check availability for your CRM and request setup assistance.

    **Best for:** Teams that want zero-code integration without building and maintaining a custom webhook handler.
  </Tab>
</Tabs>

***

## Setting Up Webhook-Based CRM Sync

<Steps>
  ### Create a Webhook Endpoint in Your CRM

  Your CRM (or a middleware tool like Zapier, Make, or a custom API server) must expose a public HTTPS endpoint that can receive POST requests from VInfer. This endpoint will receive a JSON payload for each call event.

  **If you're using middleware:**

  * **Zapier:** Create a new Zap with a **Webhook by Zapier** trigger (Catch Hook), copy the generated URL.
  * **Make (formerly Integromat):** Add a **Webhooks → Custom Webhook** module, copy the generated URL.
  * **Custom server:** Set up a POST route at a path like `/api/vinfer-events` and ensure it's accessible over HTTPS from the public internet.

  <Info>
    Your endpoint must return a `200 OK` HTTP response within 10 seconds of receiving the request. VInfer will retry delivery up to 3 times with exponential backoff if it receives a non-2xx response or a timeout.
  </Info>

  ***

  ### Register the Webhook with VInfer

  Once your endpoint is ready, register it with VInfer so the platform knows where to send events:

  <CodeGroup>
    ```bash cURL theme={null}
    curl -X POST https://api.vinfer.ai/v1/webhooks \
      -H "Authorization: Bearer YOUR_API_KEY" \
      -H "Content-Type: application/json" \
      -d '{
        "url": "https://your-crm.com/api/vinfer-events",
        "events": ["call.completed", "call.disposition_set"],
        "secret": "your_signing_secret"
      }'
    ```

    ```python Python theme={null}
    import requests

    response = requests.post(
        "https://api.vinfer.ai/v1/webhooks",
        headers={
            "Authorization": "Bearer YOUR_API_KEY",
            "Content-Type": "application/json"
        },
        json={
            "url": "https://your-crm.com/api/vinfer-events",
            "events": ["call.completed", "call.disposition_set"],
            "secret": "your_signing_secret"
        }
    )

    webhook = response.json()
    print(webhook["webhook_id"])
    ```

    ```js Node.js theme={null}
    const response = await fetch("https://api.vinfer.ai/v1/webhooks", {
      method: "POST",
      headers: {
        "Authorization": "Bearer YOUR_API_KEY",
        "Content-Type": "application/json"
      },
      body: JSON.stringify({
        url: "https://your-crm.com/api/vinfer-events",
        events: ["call.completed", "call.disposition_set"],
        secret: "your_signing_secret"
      })
    });

    const webhook = await response.json();
    console.log(webhook.webhook_id);
    ```
  </CodeGroup>

  **Available webhook events:**

  | Event                      | When It Fires                                                                     |
  | -------------------------- | --------------------------------------------------------------------------------- |
  | `call.completed`           | Every time a call ends (connected or not)                                         |
  | `call.disposition_set`     | When Neuron or a human agent sets a disposition                                   |
  | `call.escalated`           | When a call is transferred to a human agent                                       |
  | `call.transcription_ready` | When the NeuronLens transcript is available (may be a few minutes after call end) |
  | `campaign.completed`       | When an entire campaign finishes                                                  |

  <Tip>
    Subscribe only to the events you actually need. Subscribing to all events on high-volume campaigns generates a large number of webhook deliveries — if your endpoint has processing overhead, narrow your subscription to the events that drive CRM actions.
  </Tip>

  ***

  ### Verify Webhook Signatures

  Every webhook request VInfer sends includes an `X-VInfer-Signature` header. Always verify this signature before processing the payload — it confirms the request genuinely came from VInfer and hasn't been tampered with.

  VInfer generates the signature using **HMAC-SHA256** of the raw request body, keyed with the `secret` you provided during webhook registration.

  <CodeGroup>
    ```python Python theme={null}
    import hmac
    import hashlib

    def verify_vinfer_signature(raw_body: bytes, signature_header: str, secret: str) -> bool:
        expected = hmac.new(
            key=secret.encode("utf-8"),
            msg=raw_body,
            digestmod=hashlib.sha256
        ).hexdigest()
        return hmac.compare_digest(expected, signature_header)

    # In your webhook handler:
    raw_body = request.get_data()  # raw bytes, before JSON parsing
    signature = request.headers.get("X-VInfer-Signature")

    if not verify_vinfer_signature(raw_body, signature, "your_signing_secret"):
        return Response("Unauthorized", status=401)

    payload = request.get_json()
    # proceed to process payload...
    ```

    ```js Node.js theme={null}
    const crypto = require("crypto");

    function verifyVInferSignature(rawBody, signatureHeader, secret) {
      const expected = crypto
        .createHmac("sha256", secret)
        .update(rawBody)
        .digest("hex");
      return crypto.timingSafeEqual(
        Buffer.from(expected),
        Buffer.from(signatureHeader)
      );
    }

    // In your Express webhook handler:
    app.post("/api/vinfer-events", express.raw({ type: "application/json" }), (req, res) => {
      const signature = req.headers["x-vinfer-signature"];
      
      if (!verifyVInferSignature(req.body, signature, "your_signing_secret")) {
        return res.status(401).send("Unauthorized");
      }
      
      const payload = JSON.parse(req.body);
      // proceed to process payload...
      res.sendStatus(200);
    });
    ```
  </CodeGroup>

  <Warning>
    Always use a timing-safe comparison function (like `hmac.compare_digest` in Python or `crypto.timingSafeEqual` in Node.js) when comparing signatures. Standard string equality is vulnerable to timing attacks.
  </Warning>

  ***

  ### Map VInfer Disposition Values to Your CRM Fields

  VInfer uses its own set of disposition values. You need to translate these to the corresponding field values in your CRM. Do this mapping in **Settings → Integrations → CRM Mapping** in the VInfer dashboard, or handle it in your webhook processing logic.

  **Example disposition mapping:**

  | VInfer Disposition   | CRM Lead Status       |
  | -------------------- | --------------------- |
  | `interested`         | Hot Lead              |
  | `callback_requested` | Follow Up             |
  | `not_interested`     | Closed Lost           |
  | `not_reachable`      | Attempted – No Answer |
  | `dispute`            | Escalated             |
  | `payment_promised`   | Promise to Pay        |
  | `already_paid`       | Resolved              |

  If you use the dashboard mapping tool, VInfer automatically translates dispositions before sending the webhook payload — your CRM receives the mapped values directly and requires no transformation logic.

  ***

  ### Handle the Event Payload in Your CRM

  Here is an example `call.completed` event payload. Use this as a reference when building your webhook handler:

  ```json theme={null}
  {
    "event": "call.completed",
    "call_id": "call_abc123",
    "campaign_id": "camp_xyz456",
    "contact": {
      "phone": "+919876543210",
      "name": "Priya Sharma"
    },
    "disposition": "interested",
    "duration_seconds": 145,
    "language": "hi-IN",
    "timestamp": "2024-01-15T10:35:22Z",
    "transcript_url": "https://api.vinfer.ai/v1/transcription/job_def789/transcript",
    "neuronlens": {
      "sentiment": "positive",
      "qa_score": 87,
      "summary": "Customer confirmed interest in the top-up loan product. Requested a callback from the sales team before 3 PM today."
    }
  }
  ```

  **Recommended CRM update actions per event:**

  <Accordion title="On call.completed">
    1. Find the contact record by matching `contact.phone`
    2. Update Lead Status using the disposition mapping
    3. Log a call activity with `duration_seconds`, `language`, and `timestamp`
    4. Attach the `transcript_url` to the contact record for one-click access
    5. Store `call_id` in a custom field for future API lookups
    6. If `disposition` is `callback_requested`, create a follow-up task with the requested callback time
  </Accordion>

  <Accordion title="On call.transcription_ready">
    Fetch the full transcript from `transcript_url` using your API key and attach it as a note to the contact record. This fires a few minutes after `call.completed` since transcription processing takes additional time.
  </Accordion>

  <Accordion title="On call.escalated">
    Create an urgent follow-up task and notify the assigned account owner that the call was escalated. Log the escalation reason from the `trigger` field in the payload.
  </Accordion>
</Steps>

***

## REST API Polling

If webhooks aren't an option, poll the VInfer API on a schedule to fetch completed calls:

```bash theme={null}
curl "https://api.vinfer.ai/v1/calls?status=completed&after=2024-01-15T10:00:00Z&limit=100" \
  -H "Authorization: Bearer YOUR_API_KEY"
```

Use the `after` parameter to fetch only calls completed since your last poll timestamp. Store the timestamp of the most recent call you processed and use it as `after` in your next poll to avoid re-processing records.

<Note>
  If you poll frequently (every 1–2 minutes) on high-volume campaigns, be mindful of API rate limits. Check your account's rate limit headers (`X-RateLimit-Remaining`) in the response and back off if you approach the limit.
</Note>

***

## Storing the Call ID in Your CRM

<Tip>
  Always store VInfer's `call_id` in a custom field on your CRM contact or activity record. This gives you a direct lookup key if you ever need to fetch the full call record, re-download the transcript, or cross-reference a dispute — without searching by phone number or timestamp.
</Tip>

With the `call_id`, you can fetch any call's full details at any time:

```bash theme={null}
curl https://api.vinfer.ai/v1/calls/call_abc123 \
  -H "Authorization: Bearer YOUR_API_KEY"
```
