Low-level Intercom transport: regional base URL, auth and Intercom-Version headers, HttpError for non-2xx responses, and the cursor (starting_after) pagination helpers the endpoint namespaces share.

Per-call credentials. Every API-calling function in this package takes an optional trailing opts Map, resolved by resolve: opts first, then context variables, then defaults. Nothing from opts is written to ctx, so one run can act for many workspaces and parallel flows don't share credentials. A token from opts is masked in call records. A token present but blank or null is an error, never a fallback to intercom.token.

  • token: the access token for this call
  • region: us, eu or au for this call
  • version: the Intercom-Version for this call
  • admin-id: the admin to act as. It's required for acting calls that pass a token, since intercom.admin.id belongs to the workspace of intercom.token.

Context Vars

Optional
  • intercom.token
  • intercom.admin.id
  • intercom.region
  • intercom.version

Functions

admin-id

fn (): Str
fn (opts: Map): Str

The admin to act as (see optional-admin-id), or an Err with a hint when there is none: look it up with ::intercom::admins/me().

Context Vars: intercom.admin.id

api-version

fn (): Str

The Intercom-Version to send: intercom.version when set, otherwise the pinned ::intercom/API_VERSION.

Context Vars: intercom.version

base-url

fn (): Str
fn (region: Str?): Str

The regional API host. The 1-arity form takes "us", "eu" or "au" (case-insensitive; null means us) and returns an Err for anything else. The 0-arity form reads intercom.region.

Context Vars: intercom.region

build-headers

fn (token: Str, version: Str, has-body: Bool): Map

Request headers: Bearer auth, Intercom-Version, Accept: application/json, and Content-Type only when there is a body. Exposed for testing.

build-search-body

fn (query: Map, options: Map): Map

Build a POST /…/search body: {query, pagination?, sort?}. options: per_page (max 150), starting_after (the cursor from the previous page's pages.next.starting_after), and sort ({field, order: "ascending"|"descending"}, contacts only). The pagination object is omitted when neither is set. Exposed for testing.

collect-pages

fn (fetch: Fn, key: Str, cursor: Str?, limit: Int, acc: Vec): Vec

Follow starting_after cursors, concatenating each page's key Vec (e.g. "conversations", "contacts", "tickets", "data"), until limit items or the last page. fetch is a Fn taking the cursor (null for the first page) and returning one page. Exposed for testing; the search-all-* functions use it. A failed page comes back as its Err.

compact

fn (body: Map): Map

Drop null-valued keys so unset optionals are omitted rather than sent as JSON null.

next-cursor

fn (page: Any): Str?

The starting_after cursor for the page after page (from pages.next.starting_after), or null on the last page. Works for list and search responses.

non-blank

fn (value: Any): Any

optional-admin-id

fn (opts: Map): Str?

The admin in opts, else intercom.admin.id, else null. intercom.admin.id belongs to the workspace of intercom.token, so it isn't used when opts has its own token.

Context Vars: intercom.admin.id

page-size

fn (limit: Int): Int

The per_page to request when collecting up to limit items: limit clamped to 1..150 (Intercom's maximum).

path-segment

fn (value: Any): Str

Percent-encode one path segment (an id). Ints pass through as text.

query-string

fn (options: Map): Str

Build a ?a=1 suffix from a Map, dropping null values; "" when nothing remains.

request

fn (method: Str, path: Str, body: Any): Any
fn (method: Str, path: Str, body: Any, token: Str): Any
fn (method: Str, path: Str, body: Any, opts: Map): Any

Make an authenticated Intercom request. path is relative to the regional host (e.g. /contacts); body is a Map, or "" for none. Returns the decoded body on 2xx (null when empty), or an Err carrying HttpError (Intercom's errors list is in body). The 4th argument is per-call opts (see resolve) or an access token.

Example

segments ::intercom::api/request("GET", "/segments", "")
tenant ::intercom::api/request("GET", "/segments", "", {token: "tenant-b-token", region: "eu"})
Context Vars: intercom.token

resolve

fn (opts: Map): Map

Credentials and settings for one call (see resolve-from). A token from opts is masked in call records with ::hot::ctx/mark-secret, and opts values are never written to ctx.

Example

::intercom::api/resolve({token: "tenant-b-token", region: "eu"}).base-url  // "https://api.eu.intercom.io"
Context Vars: intercom.token, intercom.region, intercom.version

resolve-from

fn (opts: Map, ctx-values: Map): Map

The one resolver, pure: {token, base-url, version} for a call from opts first, then ctx-values (a Map of context variables), then defaults. An Err when no token is set, when token is in opts but blank or null (that never falls back to context), or when the region isn't us, eu or au. Exposed for testing; resolve feeds it the context.

response-body

fn (body: Any): Any

Normalize a decoded 2xx body: empty text (202/204 with no content, e.g. data events) becomes null; anything else passes through. Exposed for testing.

send

fn (method: Str, path: Str, body: Any, resolved: Map): Any

with-admin

fn (opts: Map, action: Fn): Any

Run action with the admin to act as. A problem with the call's credentials, or a missing admin, comes back as an Err before anything is sent.

Types

HttpError

HttpError type {
    status: Int,
    headers: Map,
    body: Any
}