Install any skill in seconds. Free to start, no credit card required.
Get Started Free →Manage Intercom contacts: create, search, update, merge leads into users. Use when building contact management features, syncing user data, or implementing contact search and segmentation against the Intercom REST API. Trigger with phrases like "intercom contacts", "intercom users", "intercom leads", "create intercom contact", "search intercom contacts", "merge intercom lead".
.claude/skills/jeremylongshore-intercom-core-workflow-a/SKILL.md| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-01 | ✗→✓ | ▲ Improved | 45% | 0% |
| case-02 | ✗→✓ | ▲ Improved | 9% | 0% |
| case-03 | ✗→✓ | ▲ Improved | 7% | 0% |
| case-11 | ✗→✓ | ▲ Improved | -3% | 0% |
| case-13 | ✗→✓ | ▲ Improved | 44% | 0% |
Primary workflow for managing Intercom contacts. Covers creating users and leads, searching with filters, updating custom attributes, merging leads into users, and listing segments. This SKILL.md gives you the high-level workflow and the first example; drill into references/implementation.md for the full step-by-step code and references/examples.md for end-to-end flows.
intercom-install-auth setupThe contact workflow is six operations against the intercom-client SDK. Instantiate the client once, then call the operation you need:
typescriptimport { IntercomClient } from "intercom-client"; const client = new IntercomClient({ token: process.env.INTERCOM_ACCESS_TOKEN!, }); // Create an identified user (has external_id) const user = await client.contacts.create({ role: "user", externalId: "customer-9001", email: "alice@acme.com", name: "Alice Johnson", customAttributes: { plan: "enterprise" }, });
The six operations, in the order you typically reach for them:
contacts.create with role: "user" (identified, needs externalId) or role: "lead" (anonymous).contacts.search with a single filter or a compound AND/OR query, plus pagination and sort.contacts.update by contactId to change name or custom attributes.contacts.merge({ from: leadId, into: userId }); the lead's conversations, events, and tags transfer to the user.contacts.listSegments({ contactId }) to see which segments a contact belongs to.contacts.list with startingAfter cursor to stream the full contact base.Full code for every step, including compound-search filters and the async-generator pagination helper, is in references/implementation.md.
Each operation returns a typed response from the SDK:
create / update — a single contact object: { type: "contact", id, role, email, name, customAttributes, created_at, ... }. The id is the Intercom-generated handle you pass to later calls.search — { data: Contact[], totalCount, pages }. Iterate data; read totalCount for the match count; use pages.next.startingAfter to page.merge — the surviving user contact (the into target); the from lead is deleted.listSegments — { data: Segment[] }, each { id, name }.list — one page { data: Contact[], pages }; follow pages.next.startingAfter until it is absent.| Field | Type | Description | |-------|------|-------------| | id | string | Intercom-generated unique ID | | external_id | string | Your system's user ID | | role | "user" or "lead" | Contact type | | email | string | Email address | | name | string | Full name | | phone | string | Phone number | | custom_attributes | object | Custom key-value data | | created_at | number | Unix timestamp | | last_seen_at | number | Last activity timestamp | | signed_up_at | number | Signup timestamp | | tags | object | Applied tags list | | companies | object | Associated companies | | location | object | GeoIP location data |
| Error | HTTP Code | Cause | Solution | |-------|-----------|-------|----------| | not_found | 404 | Contact ID doesn't exist | Verify with search first | | conflict | 409 | Duplicate external_id or email | Search before creating | | parameter_invalid | 422 | Bad field value or missing required field | Check field types and names | | rate_limit_exceeded | 429 | Over 10,000 req/min (private apps) | Add backoff, batch operations | | merge_not_possible | 400 | Merging user into lead (reversed) | from must be lead, into must be user |
Quick create-then-search flow:
typescriptconst user = await client.contacts.create({ role: "user", externalId: "customer-9001", email: "alice@acme.com", name: "Alice Johnson", }); const found = await client.contacts.search({ query: { field: "email", operator: "=", value: "alice@acme.com" }, }); console.log(`Found ${found.totalCount} match(es)`);
Three fuller worked flows — lead-to-user lifecycle, segmenting recent enterprise signups, and streaming every contact for an export — are in references/examples.md.
For conversation management (creating, replying to, and searching conversations), see the companion skill intercom-core-workflow-b, which builds on the contact IDs produced by this workflow.
| Case | Status | Duration (ms) | Turns | Tokens | Tool calls | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Without | With | Δ | Without | With | Δ | Without | With | Δ | Without | With | Δ | ||
case-01 | fail→pass | 16,795 | 13,281 | -21% | 1 | 1 | 0% | 2,171 | 3,146 | +45% | 0 | 0 | — |
case-02 | fail→pass | 17,495 | 13,433 | -23% | 1 | 1 | 0% | 2,909 | 3,158 | +9% | 0 | 0 | — |
case-03 | fail→pass | 21,307 | 9,492 | -55% | 1 | 1 | 0% | 3,239 | 3,453 | +7% | 0 | 0 | — |
case-04 | fail→fail | 16,884 | 14,023 | -17% | 1 | 1 | 0% | 2,254 | 2,817 | +25% | 0 | 0 | — |
case-05 | fail→fail | 16,728 | 18,827 | +13% | 1 | 1 | 0% | 2,246 | 3,599 | +60% | 0 | 0 | — |
case-06 | pass→pass | 18,546 | 21,779 | +17% | 1 | 1 | 0% | 2,139 | 4,812 | +125% | 0 | 0 | — |
case-07 | pass→pass | 16,640 | 5,338 | -68% | 1 | 1 | 0% | 1,895 | 2,430 | +28% | 0 | 0 | — |
case-08 | pass→pass | 10,216 | 2,904 | -72% | 1 | 1 | 0% | 860 | 2,013 | +134% | 0 | 0 | — |
case-09 | pass→pass | 7,255 | 7,768 | +7% | 1 | 1 | 0% | 1,319 | 1,944 | +47% | 0 | 0 | — |
case-10 | pass→pass | 10,980 | 10,022 | -9% | 1 | 1 | 0% | 912 | 1,932 | +112% | 0 | 0 | — |
case-11 | fail→pass | 13,546 | 9,834 | -27% | 1 | 1 | 0% | 2,432 | 2,352 | -3% | 0 | 0 | — |
case-12 | pass→pass | 5,731 | 8,828 | +54% | 1 | 1 | 0% | 1,051 | 2,120 | +102% | 0 | 0 | — |
case-13 | fail→pass | 18,705 | 16,007 | -14% | 1 | 1 | 0% | 2,512 | 3,607 | +44% | 0 | 0 | — |
case-14 | pass→pass | 11,732 | 8,351 | -29% | 1 | 1 | 0% | 1,157 | 2,021 | +75% | 0 | 0 | — |
case-15 | pass→pass | 9,426 | 4,187 | -56% | 1 | 1 | 0% | 1,378 | 2,111 | +53% | 0 | 0 | — |
case-16 | fail→pass | 9,688 | 8,862 | -9% | 1 | 1 | 0% | 1,461 | 2,146 | +47% | 0 | 0 | — |
case-17 | pass→pass | 8,200 | 2,120 | -74% | 1 | 1 | 0% | 541 | 1,783 | +230% | 0 | 0 | — |
case-18 | pass→pass | 9,778 | 8,814 | -10% | 1 | 1 | 0% | 1,812 | 2,147 | +18% | 0 | 0 | — |
case-19 | pass→pass | 15,095 | 12,891 | -15% | 1 | 1 | 0% | 1,701 | 2,522 | +48% | 0 | 0 | — |
case-20 | pass→pass | 6,206 | 8,266 | +33% | 1 | 1 | 0% | 955 | 2,020 | +112% | 0 | 0 | — |
case-21 | pass→pass | 28,303 | 4,128 | -85% | 1 | 1 | 0% | 5,419 | 2,163 | -60% | 0 | 0 | — |
case-22 | pass→pass | 7,680 | 7,054 | -8% | 1 | 1 | 0% | 1,353 | 1,760 | +30% | 0 | 0 | — |
DecimalAI ran this skill against gemini-3.6-flash twice over the same eval suite — once with the skill loaded and once without — and compared the two runs case by case. 22 cases were attempted. The headline lift of +27 percentage points is the difference between those two pass rates over the 22 comparable cases.
Without the skill loaded, the model failed this case. With it loaded, the same prompt on the same model passed. This is one improved case from the latest verified run; every case, including any that regressed, is in the table above.
Other measured skills in the registry, with their headline benchmark lift.