Skip to main content

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.

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 under Organization Settings → Developer Tools. See 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

git clone https://github.com/Qminder/crm-integration-template.git
cd crm-integration-template
docker build -t crm-integration .
docker run -e API_KEY=your_qminder_api_key crm-integration

Option 2: Run with Node.js

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 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 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

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

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

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

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 VariableRequiredDescription
API_KEYYesYour Qminder REST API key
Add any CRM-specific environment variables your integration needs (e.g., SF_USERNAME, HUBSPOT_TOKEN, DATABASE_URL).

Deployment

Docker (Production)

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:
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

Getting Help

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