@kody/intercom
README.md
235 lines · 9.4 KB · Markdown@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:
| Lane | When to use | Credential |
|---|---|---|
| Access token (fastest) | Private app for your workspace | User secret intercomAccessToken (or intercomAccessToken-<purpose>) |
| OAuth | Public app that accesses another workspace | Saved 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
- 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
- Save the token (do not paste the value in chat):
- Approve hosts
api.intercom.io,api.eu.intercom.io, andapi.au.intercom.io - 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
- Create a public Intercom app from https://developers.intercom.com/docs/build-an-integration/learn-more/authentication/setting-up-oauth
- Set the redirect URI exactly to
https://kody.codes/connect/oauth - Enable the permissions listed below on the app (Intercom configures these in the Developer Hub; they are not OAuth query scopes)
- Connect while signed in to Kody:
- Paste the Intercom client id and client secret into the Kody wizard (never into chat). Approve the API hosts.
- 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 permission | Needed 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)account—work→intercom-work;intercom-workused as-is; omitted →intercomsecretName— access-token secret overrideauth—'oauth'or'accessToken'when both existregion—'us'(default),'eu', or'au'apiBaseUrl— absolute regional API origin when you do not wantregion
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
| Export | Description |
|---|---|
. | Root dispatcher. Defaults to the read-only smoke test |
./accounts | Resolve integration/secret names and report what is connected |
./smoke-test | Local helper checks plus optional live /me (no email) |
./me | Connected admin id / inbox seat / region |
./contacts / ./list-contacts / ./get-contact / ./search-contacts | Contact reads |
./create-contact / ./update-contact | Contact writes (dryRun / confirm) |
./conversations / ./list-conversations / ./get-conversation / ./search-conversations | Conversation reads |
./reply-conversation / ./close-conversation | Conversation writes |
./articles / ./list-articles / ./get-article | Article reads |
./create-article / ./update-article | Article writes |
./request | Generic REST escape hatch |
./scheduled-workspace-health | Disabled job wrapper |
./types | Shared 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./requestwhen you need full conversation parts or article HTML. - Search uses Intercom's query object, or
email/stateshortcuts. - Reply and close resolve
adminIdfromGET /mewhen omitted. Create article does the same forauthorId. - This package is not affiliated with or endorsed by Intercom, Inc.