Help Scout
"Help Scout: conversation management, mailbox API, customer profiles, Beacon widget, webhooks, Docs knowledge base, REST API v2"
You are an expert in integrating Help Scout for shared inbox management, customer communication, and knowledge base workflows. ## Key Points - Push customer context (plan tier, account age, recent errors) into Help Scout customer properties via the API so agents see it in the sidebar without leaving the inbox. - Prefer the Beacon widget's JavaScript API for in-app support to pre-fill user identity and suggest relevant Docs articles before the customer opens a conversation. - Help Scout's API returns `201 Created` with no body for most creation endpoints; the new resource ID is in the `Location` response header. Trying to read the response body will fail. - Rate limits are 400 requests per minute per access token. Batch syncs that exceed this will receive `429` responses; implement exponential backoff and queue large imports.
skilldb get customer-support-services-skills/Help ScoutFull skill: 232 linesHelp Scout — Customer Support Integration
You are an expert in integrating Help Scout for shared inbox management, customer communication, and knowledge base workflows.
Core Philosophy
Overview
Help Scout is a customer support platform built around shared email inboxes (Mailboxes), threaded Conversations, and a Beacon widget for in-app help. It emphasizes a personal, email-like support experience. The Mailbox API 2.0 provides programmatic access to conversations, customers, mailboxes, tags, workflows, and the Docs knowledge base.
Setup & Configuration
Authenticate using an OAuth2 access token or API key:
import axios, { AxiosInstance } from "axios";
function createHelpScoutClient(accessToken: string): AxiosInstance {
return axios.create({
baseURL: "https://api.helpscout.net/v2",
headers: {
Authorization: `Bearer ${accessToken}`,
"Content-Type": "application/json",
},
});
}
const helpscout = createHelpScoutClient(process.env.HELPSCOUT_ACCESS_TOKEN!);
// Verify connection by listing mailboxes
async function verifyConnection(): Promise<void> {
const response = await helpscout.get("/mailboxes");
const mailboxes = response.data._embedded.mailboxes;
console.log(`Connected. Mailboxes: ${mailboxes.map((m: any) => m.name).join(", ")}`);
}
For OAuth2 token refresh (required for long-running integrations):
async function refreshAccessToken(
clientId: string,
clientSecret: string,
refreshToken: string
): Promise<string> {
const response = await axios.post("https://api.helpscout.net/v2/oauth2/token", {
grant_type: "refresh_token",
client_id: clientId,
client_secret: clientSecret,
refresh_token: refreshToken,
});
return response.data.access_token;
}
Core Patterns
Create a Conversation
interface NewConversation {
subject: string;
customerEmail: string;
mailboxId: number;
text: string;
tags?: string[];
assignTo?: number; // user (agent) ID
}
async function createConversation(params: NewConversation): Promise<string> {
const response = await helpscout.post("/conversations", {
subject: params.subject,
type: "email",
mailboxId: params.mailboxId,
customer: { email: params.customerEmail },
threads: [
{
type: "customer",
customer: { email: params.customerEmail },
text: params.text,
},
],
tags: params.tags ?? [],
assignTo: params.assignTo,
});
// Help Scout returns the conversation URL in the Location header
const location = response.headers.location as string;
const conversationId = location.split("/").pop()!;
return conversationId;
}
Reply to a Conversation
async function replyToConversation(
conversationId: string,
text: string,
options: { draft?: boolean; asAgent?: boolean } = {}
): Promise<void> {
const threadType = options.draft ? "drafts" : "reply";
await helpscout.post(`/conversations/${conversationId}/${threadType}`, {
text,
status: "active",
});
}
// Add an internal note (visible to agents only)
async function addNote(conversationId: string, text: string): Promise<void> {
await helpscout.post(`/conversations/${conversationId}/notes`, { text });
}
Search and List Conversations
async function searchConversations(query: string): Promise<any[]> {
const response = await helpscout.get("/conversations", {
params: { query },
});
return response.data._embedded?.conversations ?? [];
}
// Find all open conversations for a customer
const openTickets = await searchConversations(
"email:customer@example.com AND status:active"
);
// Find conversations by tag
const billing = await searchConversations("tag:billing");
Manage Customer Profiles
async function upsertCustomer(email: string, data: {
firstName?: string;
lastName?: string;
phone?: string;
properties?: Record<string, string>;
}): Promise<void> {
// Search for existing customer
const search = await helpscout.get(`/customers?email=${encodeURIComponent(email)}`);
const customers = search.data._embedded?.customers ?? [];
if (customers.length > 0) {
const customerId = customers[0].id;
await helpscout.put(`/customers/${customerId}`, {
firstName: data.firstName,
lastName: data.lastName,
});
} else {
await helpscout.post("/customers", {
firstName: data.firstName ?? "Unknown",
lastName: data.lastName ?? "",
emails: [{ type: "work", value: email }],
phones: data.phone ? [{ type: "work", value: data.phone }] : [],
});
}
}
Webhook Handler
import express, { Request, Response } from "express";
import crypto from "crypto";
function verifyHelpScoutSignature(
payload: string,
signature: string,
secret: string
): boolean {
const hmac = crypto.createHmac("sha1", secret).update(payload).digest("base64");
return crypto.timingSafeEqual(Buffer.from(hmac), Buffer.from(signature));
}
function setupWebhooks(app: express.Application): void {
app.post("/webhooks/helpscout", express.text({ type: "*/*" }), (req: Request, res: Response) => {
const signature = req.headers["x-helpscout-signature"] as string;
if (!verifyHelpScoutSignature(req.body, signature, process.env.HELPSCOUT_WEBHOOK_SECRET!)) {
res.sendStatus(403);
return;
}
const event = JSON.parse(req.body);
switch (event.type) {
case "convo.created":
handleNewConversation(event.record);
break;
case "convo.customer.reply.created":
handleCustomerReply(event.record);
break;
case "convo.assigned":
handleAssignment(event.record);
break;
}
res.sendStatus(200);
});
}
Best Practices
- Use Help Scout's tag system extensively to categorize conversations by topic, product area, or urgency; tags integrate with workflows and reporting and are the primary mechanism for automation triggers.
- Push customer context (plan tier, account age, recent errors) into Help Scout customer properties via the API so agents see it in the sidebar without leaving the inbox.
- Prefer the Beacon widget's JavaScript API for in-app support to pre-fill user identity and suggest relevant Docs articles before the customer opens a conversation.
Common Pitfalls
- Help Scout's API returns
201 Createdwith no body for most creation endpoints; the new resource ID is in theLocationresponse header. Trying to read the response body will fail. - Rate limits are 400 requests per minute per access token. Batch syncs that exceed this will receive
429responses; implement exponential backoff and queue large imports.
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
Related Skills
Crisp
"Crisp: live chat widget, chatbot scenarios, CRM contacts, campaigns, helpdesk, REST API, JavaScript SDK, webhooks"
Drift
"Drift: conversational marketing, live chat, chatbots, meeting scheduling, contact management, REST API, webhooks, JavaScript SDK"
Freshdesk
"Freshdesk: ticket management, contact CRUD, automations, canned responses, REST API v2, webhooks, satisfaction surveys"
Intercom
"Intercom: Messenger widget, conversations API, custom bots, product tours, help center, user/company data, events, webhooks, Node SDK"
LiveChat
"LiveChat: real-time chat, ticket system, agent management, chat archives, customer SDK, REST API v3, webhooks, rich messages"
Tawk.to
"Tawk.to: free live chat widget, JavaScript API, visitor monitoring, triggers, REST API, webhooks, customization"