Skip to main content
Business & GrowthCustomer Support Services242 lines

Drift

"Drift: conversational marketing, live chat, chatbots, meeting scheduling, contact management, REST API, webhooks, JavaScript SDK"

Quick Summary9 lines
You are an expert in integrating Drift for conversational support, live chat, chatbot playbooks, and meeting routing.

## Key Points

- Route conversations using Drift's Playbook targeting rules rather than building custom routing logic; Playbooks can filter by URL, contact attributes, visit count, and time on page.
- Store the Drift `conversationId` and `contactId` in your own system to correlate support interactions with internal user records and enable cross-platform reporting.
- Drift's API rate limits are not publicly documented in detail; implement exponential backoff on `429` responses and avoid polling conversation lists more frequently than once per minute.
skilldb get customer-support-services-skills/DriftFull skill: 242 lines
Paste into your CLAUDE.md or agent config

Drift — Customer Support Integration

You are an expert in integrating Drift for conversational support, live chat, chatbot playbooks, and meeting routing.

Core Philosophy

Overview

Drift (now part of Salesloft) is a conversational marketing and support platform that combines live chat, AI-powered chatbots (Playbooks), meeting scheduling, and contact enrichment. It is designed to convert website visitors into leads and route support queries to the right agents. The REST API provides access to conversations, contacts, messages, and account data. The JavaScript SDK controls the on-page chat widget behavior.

Setup & Configuration

Authenticate with an OAuth2 access token:

import axios, { AxiosInstance } from "axios";

function createDriftClient(accessToken: string): AxiosInstance {
  return axios.create({
    baseURL: "https://driftapi.com",
    headers: {
      Authorization: `Bearer ${accessToken}`,
      "Content-Type": "application/json",
    },
  });
}

const drift = createDriftClient(process.env.DRIFT_ACCESS_TOKEN!);

// Verify connection
async function verifyConnection(): Promise<void> {
  const response = await drift.get("/users/list");
  console.log(`Connected. ${response.data.data.length} agents found.`);
}

Embed the chat widget and control it via the JavaScript SDK:

<script>
  !function() {
    var t = window.driftt = window.drift = window.driftt || [];
    if (!t.init) {
      t.invoked = true;
      t.methods = ["identify", "config", "track", "reset", "debug", "show",
        "ping", "page", "hide", "off", "on"];
      t.factory = function(e) {
        return function() {
          var n = Array.prototype.slice.call(arguments);
          n.unshift(e);
          t.push(n);
          return t;
        };
      };
      t.methods.forEach(function(e) { t[e] = t.factory(e); });
      t.load = function(t) {
        var e = document.createElement("script");
        e.type = "text/javascript";
        e.async = true;
        e.src = "https://js.driftt.com/include/" + t + ".js";
        var n = document.getElementsByTagName("script")[0];
        n.parentNode.insertBefore(e, n);
      };
    }
  }();
  drift.SNIPPET_VERSION = '0.3.1';
  drift.load('YOUR_DRIFT_EMBED_ID');
</script>

Identify logged-in users:

declare global {
  interface Window {
    drift: {
      identify: (userId: string, attrs: Record<string, unknown>) => void;
      on: (event: string, callback: (...args: any[]) => void) => void;
      show: () => void;
      hide: () => void;
    };
  }
}

function identifyUser(userId: string, email: string, attributes: Record<string, unknown> = {}): void {
  window.drift.identify(userId, {
    email,
    ...attributes,
  });
}

// Example: pass plan and company info
identifyUser("user_12345", "jane@acme.com", {
  company: "Acme Corp",
  plan: "enterprise",
  createdAt: 1640000000,
});

Core Patterns

Send a Message in a Conversation

interface DriftMessage {
  conversationId: number;
  body: string;
  type?: "chat" | "private_prompt";
}

async function sendMessage(params: DriftMessage): Promise<any> {
  const response = await drift.post(
    `/conversations/${params.conversationId}/messages`,
    {
      type: params.type ?? "chat",
      body: params.body,
    }
  );
  return response.data.data;
}

List and Filter Conversations

async function getConversations(statusId?: number): Promise<any[]> {
  const params: Record<string, unknown> = {};
  if (statusId !== undefined) {
    params.statusId = statusId; // 1=open, 2=closed, 3=pending
  }
  const response = await drift.get("/conversations/list", { params });
  return response.data.data;
}

// Get conversation details with messages
async function getConversationWithMessages(conversationId: number): Promise<any> {
  const [convo, messages] = await Promise.all([
    drift.get(`/conversations/${conversationId}`),
    drift.get(`/conversations/${conversationId}/messages`),
  ]);
  return {
    conversation: convo.data.data,
    messages: messages.data.data,
  };
}

Manage Contacts

interface DriftContact {
  email: string;
  attributes: Record<string, unknown>;
}

async function createOrUpdateContact(contact: DriftContact): Promise<any> {
  // Drift auto-merges contacts by email
  const response = await drift.post("/contacts", {
    attributes: {
      email: contact.email,
      ...contact.attributes,
    },
  });
  return response.data.data;
}

// Retrieve a contact by email
async function findContactByEmail(email: string): Promise<any | null> {
  const response = await drift.get("/contacts", {
    params: { email },
  });
  const contacts = response.data.data;
  return contacts.length > 0 ? contacts[0] : null;
}

Webhook Handler

import express, { Request, Response } from "express";
import crypto from "crypto";

function verifyDriftWebhook(body: string, signature: string, secret: string): boolean {
  const expected = crypto.createHmac("sha256", secret).update(body).digest("hex");
  return crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(signature));
}

function setupDriftWebhooks(app: express.Application): void {
  app.post("/webhooks/drift", express.text({ type: "*/*" }), (req: Request, res: Response) => {
    const signature = req.headers["x-drift-signature"] as string;

    if (!verifyDriftWebhook(req.body, signature, process.env.DRIFT_WEBHOOK_SECRET!)) {
      res.sendStatus(401);
      return;
    }

    const event = JSON.parse(req.body);

    switch (event.type) {
      case "new_conversation":
        handleNewConversation(event.data);
        break;
      case "new_message":
        handleNewMessage(event.data);
        break;
      case "conversation_status_updated":
        handleStatusChange(event.data);
        break;
    }

    res.sendStatus(200);
  });
}

Best Practices

  • Use drift.identify() on every authenticated page load with up-to-date user attributes; this ensures agents see current account context in the sidebar and Playbook targeting rules evaluate correctly.
  • Route conversations using Drift's Playbook targeting rules rather than building custom routing logic; Playbooks can filter by URL, contact attributes, visit count, and time on page.
  • Store the Drift conversationId and contactId in your own system to correlate support interactions with internal user records and enable cross-platform reporting.

Common Pitfalls

  • The Drift JavaScript snippet must load before calling drift.identify(). If you call identify too early, the call is silently dropped. Use drift.on("ready", callback) to defer identity calls until the SDK is initialized.
  • Drift's API rate limits are not publicly documented in detail; implement exponential backoff on 429 responses and avoid polling conversation lists more frequently than once per minute.

Anti-Patterns

Using the service without understanding its pricing model. Cloud services bill differently — per request, per GB, per seat. Deploying without modeling expected costs leads to surprise invoices.

Hardcoding configuration instead of using environment variables. API keys, endpoints, and feature flags change between environments. Hardcoded values break deployments and leak secrets.

Ignoring the service's rate limits and quotas. Every external API has throughput limits. Failing to implement backoff, queuing, or caching results in dropped requests under load.

Treating the service as always available. External services go down. Without circuit breakers, fallbacks, or graceful degradation, a third-party outage becomes your outage.

Coupling your architecture to a single provider's API. Building directly against provider-specific interfaces makes migration painful. Wrap external services in thin adapter layers.

Install this skill directly: skilldb add customer-support-services-skills

Get CLI access →