From intercom-pack
Applies intercom-client TypeScript SDK patterns: type-safe wrappers, cursor pagination generators, error handling. For Intercom integrations, refactoring, or standards.
npx claudepluginhub jeremylongshore/claude-code-plugins-plus-skills --plugin intercom-packThis skill is limited to using the following tools:
Production-ready patterns for the `intercom-client` TypeScript SDK covering client initialization, pagination, error handling, and type safety.
Creates minimal TypeScript examples for Intercom contacts, conversations, and messages. For starting integrations, testing setups, or learning core API data model.
Applies @hubspot/api-client SDK patterns in TypeScript for HubSpot CRM integrations: typed wrappers, error handling, batching, and pagination.
Provides expert patterns for HubSpot CRM integration including OAuth authentication, CRM objects, associations, batch operations, webhooks, and custom objects using Node.js and Python SDKs.
Share bugs, ideas, or general feedback.
Production-ready patterns for the intercom-client TypeScript SDK covering client initialization, pagination, error handling, and type safety.
intercom-client package installed// src/intercom/client.ts
import { IntercomClient } from "intercom-client";
import { Intercom } from "intercom-client";
let instance: IntercomClient | null = null;
export function getClient(): IntercomClient {
if (!instance) {
instance = new IntercomClient({
token: process.env.INTERCOM_ACCESS_TOKEN!,
});
}
return instance;
}
// Type-safe contact creation helper
export async function createContact(
params: Intercom.CreateContactRequest
): Promise<Intercom.Contact> {
return getClient().contacts.create(params);
}
// Type-safe search helper
export async function searchContacts(
query: Intercom.SearchRequest
): Promise<Intercom.ContactList> {
return getClient().contacts.search(query);
}
Intercom uses cursor-based pagination. The starting_after parameter points to the next page.
// Generic paginator for any list endpoint
async function* paginateContacts(
client: IntercomClient,
perPage = 50
): AsyncGenerator<Intercom.Contact> {
let startingAfter: string | undefined;
do {
const page = await client.contacts.list({
perPage,
startingAfter,
});
for (const contact of page.data) {
yield contact;
}
// Cursor for next page
startingAfter = page.pages?.next?.startingAfter ?? undefined;
} while (startingAfter);
}
// Usage
const client = getClient();
for await (const contact of paginateContacts(client)) {
console.log(contact.email);
}
The SDK also supports built-in iteration:
// SDK auto-pagination (articles, contacts, etc.)
const response = await client.articles.list();
for await (const article of response) {
console.log(article.title);
}
import { IntercomError } from "intercom-client";
async function safeIntercomCall<T>(
operation: () => Promise<T>,
context: string
): Promise<{ data: T | null; error: IntercomError | null }> {
try {
const data = await operation();
return { data, error: null };
} catch (err) {
if (err instanceof IntercomError) {
console.error(`[Intercom:${context}] ${err.statusCode}: ${err.message}`, {
requestId: err.body?.request_id,
errors: err.body?.errors,
});
// Specific error handling
switch (err.statusCode) {
case 401:
console.error("Token invalid or expired. Regenerate access token.");
break;
case 404:
console.error("Resource not found. Verify the ID.");
break;
case 409:
console.error("Conflict: resource already exists.");
break;
case 422:
console.error("Validation failed:", err.body?.errors);
break;
case 429:
console.error("Rate limited. Back off and retry.");
break;
}
return { data: null, error: err };
}
throw err; // Re-throw non-Intercom errors
}
}
// Usage
const { data: contact, error } = await safeIntercomCall(
() => client.contacts.find({ contactId: "abc123" }),
"findContact"
);
async function withRetry<T>(
operation: () => Promise<T>,
config = { maxRetries: 3, baseDelayMs: 1000 }
): Promise<T> {
for (let attempt = 0; attempt <= config.maxRetries; attempt++) {
try {
return await operation();
} catch (err) {
if (err instanceof IntercomError) {
// Only retry on rate limits and server errors
if (err.statusCode !== 429 && (err.statusCode ?? 0) < 500) {
throw err;
}
if (attempt === config.maxRetries) throw err;
// Use Retry-After header if available, otherwise exponential backoff
const retryAfter = err.headers?.["retry-after"];
const delay = retryAfter
? parseInt(retryAfter) * 1000
: config.baseDelayMs * Math.pow(2, attempt) + Math.random() * 500;
console.log(`Retry ${attempt + 1}/${config.maxRetries} in ${delay}ms`);
await new Promise((r) => setTimeout(r, delay));
} else {
throw err;
}
}
}
throw new Error("Unreachable");
}
// Search with multiple conditions (AND/OR)
const results = await client.contacts.search({
query: {
operator: "AND",
value: [
{ field: "role", operator: "=", value: "user" },
{ field: "custom_attributes.plan", operator: "=", value: "pro" },
{
operator: "OR",
value: [
{ field: "email", operator: "~", value: "@acme.com" },
{ field: "email", operator: "~", value: "@bigcorp.com" },
],
},
],
},
pagination: { per_page: 25 },
sort: { field: "created_at", order: "descending" },
});
const clientCache = new Map<string, IntercomClient>();
export function getClientForWorkspace(
workspaceToken: string
): IntercomClient {
if (!clientCache.has(workspaceToken)) {
clientCache.set(
workspaceToken,
new IntercomClient({ token: workspaceToken })
);
}
return clientCache.get(workspaceToken)!;
}
| Operator | Meaning | Example |
|---|---|---|
= | Equals | email = "test@example.com" |
!= | Not equals | role != "lead" |
~ | Contains | email ~ "@acme.com" |
!~ | Not contains | name !~ "test" |
> | Greater than | created_at > 1700000000 |
< | Less than | last_seen_at < 1700000000 |
IN | In list | tag_id IN ["tag1", "tag2"] |
NIN | Not in list | segment_id NIN ["seg1"] |
| Pattern | Use Case | Benefit |
|---|---|---|
safeIntercomCall wrapper | All API calls | Prevents uncaught exceptions |
withRetry | Transient failures (429, 5xx) | Automatic recovery |
| Cursor pagination generator | Large data sets | Memory-efficient streaming |
| Client factory | Multi-tenant apps | Workspace isolation |
Apply patterns in intercom-core-workflow-a for contact management workflows.