Skip to content
← Community packages

Read Intercom contacts, conversations, and articles, with dry-run writes.

Browse files

  • Other
  • intercom
  • contacts
  • conversations
  • articles
  • help-center
  • oauth
  • access-token
  • multi-account
License
MIT
Published
August 22, 2026
Pinned commit
ecda1ec
Rating
No ratings yet
Forks
0
Stars
0
Adaptation effort

README

@kody/intercom

community-icon.svg is Intercom's official logomark from Simple Icons (product mark, unmodified path, Intercom brand black #1A1A19 on Intercom cream #F4F4EC so the smile stays legible at 56px). Intercom® is a trademark of Intercom, Inc. This package is not affiliated with or endorsed by Intercom.

Intent

Provide reusable, account-agnostic Intercom helpers so Kody agents can read contacts, conversations, and Help Center articles through a saved intercom / intercom-* OAuth integration or a workspace access token — without hand-rolling REST. Mutations are previewable with dryRun: true and only run live after confirm: true.

This listing is meant to be forked. After you fork, connect your Intercom workspace. Do not treat the live @kody/intercom package storage as yours. No workspace ids, admin ids, or contact ids are baked in.

Share this package as https://kody.codes/@kody/intercom (never a /community/{listing_id} URL).

Agent setup

Intercom has no built-in Kody OAuth app. Choose one lane:

LaneWhen to useCredential
Access token (fastest)Private app for your workspaceUser secret intercomAccessToken (or intercomAccessToken-<purpose>)
OAuthPublic app that accesses another workspaceSaved integration intercom or intercom-<purpose>

Both lanes send Authorization: Bearer … plus Intercom-Version: 2.14. Required API hosts: api.intercom.io, api.eu.intercom.io, api.au.intercom.io. Approve them in the account secrets UI. Never paste tokens into chat.

Lane A — Access token

  1. Create a private app in the Intercom Developer Hub and copy the access token from Configure → Authentication. Docs: https://developers.intercom.com/docs/build-an-integration/learn-more/authentication
  2. Save the token (do not paste the value in chat):

https://kody.codes/account/secrets/new?name=intercomAccessToken&description=Intercom%20workspace%20access%20token&allowedHosts=api.intercom.io%2Capi.eu.intercom.io%2Capi.au.intercom.io&scope=user

  1. Approve hosts api.intercom.io, api.eu.intercom.io, and api.au.intercom.io
  2. Run the smoke test below

For a second workspace/token, use a distinct secret name such as intercomAccessToken-work and pass secretName: 'intercomAccessToken-work' (or account: 'work', which resolves to intercom-work / intercomAccessToken-work).

EU or AU workspaces: pass region: 'eu' or region: 'au' (or apiBaseUrl) on each call. Hosts stay generic — never hard-code a workspace id.

Lane B — BYO OAuth

  1. Create a public Intercom app from https://developers.intercom.com/docs/build-an-integration/learn-more/authentication/setting-up-oauth
  2. Set the redirect URI exactly to https://kody.codes/connect/oauth
  3. Enable the permissions listed below on the app (Intercom configures these in the Developer Hub; they are not OAuth query scopes)
  4. Connect while signed in to Kody:

https://kody.codes/connect/oauth?provider=intercom&authorizeUrl=https%3A%2F%2Fapp.intercom.com%2Foauth&tokenUrl=https%3A%2F%2Fapi.intercom.io%2Fauth%2Feagle%2Ftoken&apiBaseUrl=https%3A%2F%2Fapi.intercom.io&flow=confidential&pkce=false&allowedHosts=api.intercom.io%2Capi.eu.intercom.io%2Capi.au.intercom.io&dashboardUrl=https%3A%2F%2Fdevelopers.intercom.com%2Fdocs%2Fbuild-an-integration%2Flearn-more%2Fauthentication%2Fsetting-up-oauth

  1. Paste the Intercom client id and client secret into the Kody wizard (never into chat). Approve the API hosts.
  2. Reconnect later with https://kody.codes/connect/oauth?provider=intercom

Authorize URL: https://app.intercom.com/oauth (use app.eu.intercom.com or app.au.intercom.com for those regions). Token URL: https://api.intercom.io/auth/eagle/token. Flow: confidential (client secret). Intercom access tokens do not use refresh tokens; reconnect if a token is revoked.

To connect a second workspace, change provider (for example provider=intercom-work) and pass integrationName: 'intercom-work' on every call.

Permissions

Developer Hub permissionNeeded for
Read admins./me, ./smoke-test
Read and list users and companies./list-contacts, ./get-contact, ./search-contacts
Read and write users./create-contact, ./update-contact
Read conversations./list-conversations, ./get-conversation, ./search-conversations
Write conversations./reply-conversation, ./close-conversation
Read and List articles./list-articles, ./get-article
Read and Write Articles./create-article, ./update-article

If Intercom returns 401/403, helpers throw a message that names the missing permission and the next setup URL (reconnect OAuth, or save an access token).

Multiple accounts

Every export accepts:

  • integrationName / integration — exact saved OAuth name (intercom-work)
  • accountworkintercom-work; intercom-work used as-is; omitted → intercom
  • secretName — access-token secret override
  • auth'oauth' or 'accessToken' when both exist
  • region'us' (default), 'eu', or 'au'
  • apiBaseUrl — absolute regional API origin when you do not want region

Do not hard-code a personal workspace id, admin id, or alias.

Safety

Mutating helpers require confirm: true. Pass dryRun: true to inspect the REST payload without calling Intercom. ./request treats GET and search POST as read-only; other POST / PUT / PATCH / DELETE need confirmation.

Do not write live Intercom records from a smoke test. The optional workspace-health job is disabled by default and only reads /me plus first-page counts.

Exports

ExportDescription
.Root dispatcher. Defaults to the read-only smoke test
./accountsResolve integration/secret names and report what is connected
./smoke-testLocal helper checks plus optional live /me (no email)
./meConnected admin id / inbox seat / region
./contacts / ./list-contacts / ./get-contact / ./search-contactsContact reads
./create-contact / ./update-contactContact writes (dryRun / confirm)
./conversations / ./list-conversations / ./get-conversation / ./search-conversationsConversation reads
./reply-conversation / ./close-conversationConversation writes
./articles / ./list-articles / ./get-articleArticle reads
./create-article / ./update-articleArticle writes
./requestGeneric REST escape hatch
./scheduled-workspace-healthDisabled job wrapper
./typesShared TypeScript types

Smoke test

import intercom from 'kody:@kody/intercom'

export default async function main() {
	return await intercom({ action: 'smoke-test' })
}

Without credentials this returns { ok: true, live: false } plus the connect and access-token URLs. After OAuth or an access token is saved it reads GET /me and returns { live: true, hasAdminId } without email.

Preview a mutation without credentials:

import createContact from 'kody:@kody/intercom/create-contact'

export default async function main() {
	return await createContact({
		email: 'pat@example.com',
		name: 'Pat',
		dryRun: true,
	})
}

Examples

import listContacts from 'kody:@kody/intercom/list-contacts'
import listConversations from 'kody:@kody/intercom/list-conversations'
import listArticles from 'kody:@kody/intercom/list-articles'

export default async function main() {
	const contacts = await listContacts({ perPage: 10 })
	const conversations = await listConversations({ perPage: 10 })
	const articles = await listArticles({ perPage: 10 })
	return {
		contactCount: contacts.items.length,
		conversationCount: conversations.items.length,
		articleCount: articles.items.length,
	}
}
import replyConversation from 'kody:@kody/intercom/reply-conversation'

export default async function main() {
	const preview = await replyConversation({
		id: conversationIdFromCaller,
		body: 'Thanks — looking into this.',
		dryRun: true,
	})
	// After the user confirms the exact conversation id and reply text:
	return await replyConversation({
		id: conversationIdFromCaller,
		body: 'Thanks — looking into this.',
		confirm: true,
	})
}

Unwrapped REST:

import request from 'kody:@kody/intercom/request'

export default async function main() {
	return await request({
		path: '/contacts',
		query: { per_page: 5 },
	})
}

Notes

  • REST base: https://api.intercom.io (US), https://api.eu.intercom.io (EU), https://api.au.intercom.io (AU)
  • Helpers project slim records (id, role/state/title, timestamps). Use ./request when you need full conversation parts or article HTML.
  • Search uses Intercom's query object, or email / state shortcuts.
  • Reply and close resolve adminId from GET /me when omitted. Create article does the same for authorId.
  • This package is not affiliated with or endorsed by Intercom, Inc.

Docs

Report this listing

Log in to report this listing.