Developers
Last updated 2 October 2026
You can read and post messages, list channels and members, and receive events from your own workspace with a small REST API and signed webhooks. Everything uses JSON over HTTPS.
Keys
An Owner or Admin makes keys under Admin → API keys. A key is shown once and can be revoked at any time. It acts as the person who made it, with that person’s current role, so demoting or deactivating them narrows or stops the key straight away. When you create a key you choose its scopes; it can never do more than its owner can.
Send the key as a bearer token. The key decides which workspace a request is for, so there is no workspace address to pass.
curl https://intercomchat.com/api/v1/me \
-H "Authorization: Bearer YOUR_KEY"Limits and errors
- Each key may make up to 600 requests a minute. Beyond that you get
429. - Errors look like
{ "error": { "code": "invalid_key", "message": "…" } }with a matching HTTP status:401(bad or missing key),403(the key lacks a scope or the person lacks the permission),402(your plan does not include it),404,422,429. - Messages posted through the API go through the same data-protection rules as messages typed by a person.
Endpoints
GET /me
Who the key belongs to: the workspace, its plan, and the key’s scopes. A good first call.
GET /channels
The channels the key’s owner can see. Scope: channel.read.public.
{ "data": [ { "id": "…", "slug": "general", "name": "general", "visibility": "public", "topic": null } ] }POST /channels
Create a channel. Scope: channel.create.
curl -X POST https://intercomchat.com/api/v1/channels \
-H "Authorization: Bearer YOUR_KEY" -H "Content-Type: application/json" \
-d '{ "name": "build-alerts", "visibility": "private", "topic": "CI results" }'GET /channels/{id}/messages
Messages in a channel, newest last. Query: limit (up to 200, default 50) and before (an ISO time; pass the nextBefore from the previous page to go further back). Scope: channel.read.public. Private channels need the key’s owner to be a member.
POST /channels/{id}/messages
Post a message as the key’s owner. Body: body (up to 4,000 characters) and optionally parentId to reply in a thread. Scope: message.send.
curl -X POST https://intercomchat.com/api/v1/channels/CHANNEL_ID/messages \
-H "Authorization: Bearer YOUR_KEY" -H "Content-Type: application/json" \
-d '{ "body": "Deploy finished" }'GET /members
The people in the workspace: id, email, name, role, status. Scope: user.invite, so it is for keys made by someone who is allowed to invite people.
GET /audit
The workspace’s audit log for your own security tools, oldest first. Query: after (the nextCursor from the previous call; leave it out for the first page) and limit (1 to 500, default 100). Each call returns data, nextCursor and hasMore. Entries from the last few seconds are not shown yet. Scope: audit.view, for keys made by someone who may read the audit log. Enterprise+ plans only (402 otherwise).
{ "data": [ { "id": "…", "at": "2026-10-14T09:12:03.123456Z", "action": "user.role_changed",
"actor": { "id": "…", "name": "Ada" }, "target": { "type": "user", "id": "…" },
"metadata": { }, "ip": "203.0.113.7" } ],
"nextCursor": "…", "hasMore": false }Building an app for other companies
To make something other workspaces can install, with permissions their administrators approve and a public listing in the marketplace, see Build an app.
Incoming messages from other tools
If you only need a tool to post into one channel, use an app under Admin → Apps instead: you get a secret address to send { "text": "…" } to, with ready-made versions for GitHub, GitLab, Jira, Sentry and Linear. No key is involved.
Webhooks (events sent to you)
Under Admin → Webhooks you can register an address to receive events. The events are: message.created, message.deleted, channel.created, channel.archived, user.invited, user.deactivated, call.started and call.ended. Failed deliveries are retried, and you can re-send one from the admin screen.
On Enterprise+ plans there is one more event, audit.entry, which sends your audit log in batches as it is written: { "entries": [ … ], "nextCursor": "…" }, the same entries as GET /audit. An endpoint only receives it if you tick it; leaving every event unticked does not include it. Streaming starts from the moment the endpoint is made, and earlier history is available from GET /audit.
Each delivery is a POST with these headers:
x-ic-event: the event namex-ic-delivery: a unique id for the deliveryx-ic-signature:t=TIMESTAMP,v1=SIGNATURE
To check a delivery, compute the HMAC-SHA256 of `${timestamp}.${rawBody}` using your webhook’s secret, write it as lowercase hexadecimal, and compare it to v1 in constant time. Reject deliveries whose timestamp is more than a few minutes old.
import { createHmac, timingSafeEqual } from 'node:crypto';
function valid(secret, rawBody, header) {
const [t, v1] = header.split(',').map((p) => p.split('=')[1]);
const expected = createHmac('sha256', secret).update(t + '.' + rawBody).digest('hex');
return Math.abs(Date.now() - Number(t)) < 5 * 60_000 &&
expected.length === v1.length && timingSafeEqual(Buffer.from(expected), Buffer.from(v1));
}Help
Questions, or something you need that is not here? Email support@intercomchat.com.
Xypno Business Solutions Ltd. Questions? Email support@intercomchat.com.