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

# CRM Integration Template

> A template for integrating Qminder with your CRM to identify VIP customers at check-in.

This template connects Qminder to your CRM and automatically labels VIP customers when they check in.

## What It Does

The template listens for visitor check-ins across all Qminder locations. When someone checks in, it queries your CRM to check if they're a VIP. If they are, it applies a "VIP" label to their ticket.

## Prerequisites

Before getting started, you'll need:

* **Qminder API key** - Create one in [Qminder Dashboard](https://dashboard.qminder.com) under Organization Settings → Developer Tools. See [Authentication](/reference/overview#authentication) for details.
* **Docker** (recommended) OR **Node.js 20+** with Yarn
* **CRM access** - Credentials for your CRM system (Salesforce, HubSpot, or custom)

## Quick Start

### Clone the Repository

```bash theme={null}
git clone https://github.com/Qminder/crm-integration-template.git
cd crm-integration-template
```

### Option 1: Run with Docker (Recommended)

```bash theme={null}
docker build -t crm-integration .
docker run -e API_KEY=your_qminder_api_key crm-integration
```

### Option 2: Run with Node.js

```bash theme={null}
yarn install
API_KEY=your_qminder_api_key yarn start
```

Once running, the template logs that it's listening for visitor events.

## How It Works

The template uses Qminder's [GraphQL API](/reference/graphql-api) to subscribe to real-time events:

1. **Connects to Qminder** - Establishes a WebSocket connection using your API key
2. **Subscribes to check-ins** - Listens for the [`createdTickets`](/reference/graphql/subscriptions/created-tickets) subscription across all locations
3. **Extracts visitor info** - When a ticket is created, extracts the visitor's name, phone number, and email
4. **Queries your CRM** - Calls your custom `checkVip()` function to look up the customer
5. **Applies the label** - If the customer is a VIP, adds a "VIP" label to their Qminder ticket

## Customization

Edit `src/crm.ts` to connect to your CRM. This file contains the `checkVip()` function.

### The Interface

```typescript theme={null}
export async function checkVip(visitor: Visitor): Promise<boolean> {
  // Your CRM lookup logic here
  // Return true if VIP, false otherwise
}
```

The `Visitor` object contains:

* `firstName` - Visitor's first name
* `lastName` - Visitor's last name
* `phoneNumber` - Phone number (if provided)
* `email` - Email address (if provided)

### Example: Salesforce Integration

```typescript theme={null}
import jsforce from 'jsforce';

const conn = new jsforce.Connection({
  loginUrl: 'https://login.salesforce.com'
});

await conn.login(process.env.SF_USERNAME, process.env.SF_PASSWORD);

export async function checkVip(visitor: Visitor): Promise<boolean> {
  const result = await conn.query(
    `SELECT Id, VIP__c FROM Contact
     WHERE Name = '${visitor.firstName} ${visitor.lastName}'
     LIMIT 1`
  );

  if (result.records.length > 0) {
    return result.records[0].VIP__c === true;
  }
  return false;
}
```

### Example: HubSpot Integration

```typescript theme={null}
import { Client } from '@hubspot/api-client';

const hubspot = new Client({ accessToken: process.env.HUBSPOT_TOKEN });

export async function checkVip(visitor: Visitor): Promise<boolean> {
  const response = await hubspot.crm.contacts.searchApi.doSearch({
    filterGroups: [{
      filters: [{
        propertyName: 'firstname',
        operator: 'EQ',
        value: visitor.firstName
      }, {
        propertyName: 'lastname',
        operator: 'EQ',
        value: visitor.lastName
      }]
    }],
    properties: ['vip_status']
  });

  if (response.results.length > 0) {
    return response.results[0].properties.vip_status === 'true';
  }
  return false;
}
```

### Example: Custom Database

```typescript theme={null}
import { Pool } from 'pg';

const pool = new Pool({
  connectionString: process.env.DATABASE_URL
});

export async function checkVip(visitor: Visitor): Promise<boolean> {
  const result = await pool.query(
    'SELECT is_vip FROM customers WHERE phone = $1 OR email = $2',
    [visitor.phoneNumber, visitor.email]
  );

  return result.rows[0]?.is_vip === true;
}
```

## Project Structure

```
src/
├── app.ts          # Main entry point - sets up the Qminder listener
├── crm.ts          # Your CRM integration logic (customize this file)
└── model/ticket.ts # TypeScript type definitions for visitor data
```

## Configuration

| Environment Variable | Required | Description               |
| -------------------- | -------- | ------------------------- |
| `API_KEY`            | Yes      | Your Qminder REST API key |

Add any CRM-specific environment variables your integration needs (e.g., `SF_USERNAME`, `HUBSPOT_TOKEN`, `DATABASE_URL`).

## Deployment

### Docker (Production)

```bash theme={null}
docker build -t crm-integration .
docker run -d \
  -e API_KEY=your_qminder_api_key \
  -e HUBSPOT_TOKEN=your_hubspot_token \
  --restart unless-stopped \
  crm-integration
```

### Cloud Platforms

The Docker image can be deployed to any container platform:

* **AWS** - ECS, Fargate, or App Runner
* **Google Cloud** - Cloud Run or GKE
* **Azure** - Container Instances or AKS
* **Heroku** - Container Registry

### Process Managers

If running directly on a VM without Docker, use a process manager like PM2:

```bash theme={null}
npm install -g pm2
API_KEY=your_key pm2 start yarn --name crm-integration -- start
```

## Troubleshooting

### Template not receiving events

* Verify your API key is valid and has access to locations
* Check that the WebSocket connection is established (look for connection logs)
* Ensure your firewall allows outbound WebSocket connections

### VIP labels not appearing

* Confirm your `checkVip()` function returns `true` for test customers
* Check the template logs for any CRM query errors
* Verify the "VIP" label exists in your Qminder location settings

### CRM connection issues

* Double-check your CRM credentials in environment variables
* Ensure your CRM API rate limits aren't being exceeded
* Test your CRM queries independently before integrating

## Source Code

The full source code is available on GitHub: [Qminder/crm-integration-template](https://github.com/Qminder/crm-integration-template)

## Getting Help

Contact Qminder support for assistance. Enterprise plan customers have access to premium API support.
