Claude-code-plugins-plus-skills intercom-sdk-patterns
install
source · Clone the upstream repo
git clone https://github.com/jeremylongshore/claude-code-plugins-plus-skills
Claude Code · Install into ~/.claude/skills/
T=$(mktemp -d) && git clone --depth=1 https://github.com/jeremylongshore/claude-code-plugins-plus-skills "$T" && mkdir -p ~/.claude/skills && cp -r "$T/plugins/saas-packs/intercom-pack/skills/intercom-sdk-patterns" ~/.claude/skills/jeremylongshore-claude-code-plugins-plus-skills-intercom-sdk-patterns && rm -rf "$T"
manifest:
plugins/saas-packs/intercom-pack/skills/intercom-sdk-patterns/SKILL.mdsource content
Intercom SDK Patterns
Overview
Production-ready patterns for the
intercom-client TypeScript SDK covering client initialization, pagination, error handling, and type safety.
Prerequisites
package installedintercom-client- TypeScript 5.0+ project
- Familiarity with async/await and generators
Instructions
Step 1: Type-Safe Client Wrapper
// 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); }
Step 2: Cursor-Based Pagination
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); }
Step 3: Error Handling with IntercomError
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" );
Step 4: Retry with Exponential Backoff
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"); }
Step 5: Contact Search with Compound Queries
// 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" }, });
Step 6: Multi-Tenant Client Factory
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)!; }
Intercom Search Operators
| Operator | Meaning | Example |
|---|---|---|
| Equals | |
| Not equals | |
| Contains | |
| Not contains | |
| Greater than | |
| Less than | |
| In list | |
| Not in list | |
Error Handling
| Pattern | Use Case | Benefit |
|---|---|---|
wrapper | All API calls | Prevents uncaught exceptions |
| Transient failures (429, 5xx) | Automatic recovery |
| Cursor pagination generator | Large data sets | Memory-efficient streaming |
| Client factory | Multi-tenant apps | Workspace isolation |
Resources
Next Steps
Apply patterns in
intercom-core-workflow-a for contact management workflows.