> ## Documentation Index
> Fetch the complete documentation index at: https://www.courier.com/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Courier JS

> The API client for Courier's browser SDKs.

<Tip>
  `@trycourier/courier-js` exposes the Courier APIs for web browser-based applications.

  If you're looking for full-featured UI components,
  check out [Courier React](/sdk-libraries/courier-react-web/) or [Courier UI Web Components](/sdk-libraries/courier-ui-inbox-web).
</Tip>

## Installation

Available on
<Link href="https://github.com/trycourier/courier-web/tree/main/%40trycourier/courier-js"><Icon icon="github" iconType="solid" /> GitHub</Link>
and <Link href="https://www.npmjs.com/package/@trycourier/courier-js"><Icon icon="npm" iconType="solid" /> npm</Link>.

```bash theme={null}
npm install @trycourier/courier-js
```

## Usage

Instantiate the Courier client.

```ts theme={null}
const courierClient = new CourierClient({
    userId: 'my-user-id',
    jwt: 'eyJ.mock.jwt',
});
```

### `CourierClient` options

| Option   | Type             | Required | Description                                                                                                                                                                                                                                        |
| -------- | ---------------- | -------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| userId   | string           | Yes      | The user to authenticate and whose messages should be fetched. This is the same user ID to which you should send a message.                                                                                                                        |
| jwt      | string           | Yes      | The access token (JWT) minted for the user.                                                                                                                                                                                                        |
| tenantId | string           | No       | Optional: The tenant to which messages should be scoped. See [Inbox and Tenants](/platform/tenants/inbox-with-tenants) for more information.                                                                                                       |
| apiUrls  | `CourierApiUrls` | No       | Optional: REST/GraphQL and inbox GraphQL/WebSocket base URLs. Defaults to **US** hosts. For EU, see [EU and regional endpoints](#eu-and-regional-endpoints) at the end of this page. Type shape: [`CourierApiUrls`](#courierapiurls) under Models. |
| showLogs | boolean          | No       | Optional: Enable debugging console logs from the Courier SDK. Defaults to `process.env.NODE_ENV === 'development'`.                                                                                                                                |

## Authentication

To use the SDK, you need to generate a JWT (JSON Web Token) for your user. **This JWT should always be generated by your backend server, never in client-side code.**

### JWT Authentication Flow

<Steps>
  <Step title="Your client calls your backend">
    When your app needs to authenticate a user, your client
    should make a request to your own backend (ex. `GET https://your-awesome-app.com/api/generate-courier-jwt`).
  </Step>

  <Step title="Your backend calls Courier">
    In your backend endpoint, use your [Courier API Key](/platform/workspaces/environments-api-keys/) to call the [Courier Issue Token Endpoint](/api-reference/authentication/create-a-jwt) and generate a JWT for the user.
  </Step>

  <Step title="Your backend returns the JWT to your client">
    Having received the JWT from Courier, your backend should return it to your client and pass it to the Courier SDK.
  </Step>
</Steps>

<Tip>
  See [all available user scopes](/api-reference/authentication/create-a-jwt) for the Courier APIs.
</Tip>

### Development Authentication with cURL

To quickly test JWT generation for development only, you can use cURL to call the [Courier Issue Token Endpoint](/api-reference/authentication/create-a-jwt) directly.

<Note>
  Do not call the Issue Token API from client-side code. Always keep your Courier API keys secure.
</Note>

```bash wrap theme={null}
curl --request POST \
     --url https://api.courier.com/auth/issue-token \
     --header 'Accept: application/json' \
     --header 'Authorization: Bearer $YOUR_API_KEY' \
     --header 'Content-Type: application/json' \
     --data \
 '{
    "scope": "user_id:$YOUR_USER_ID write:user-tokens inbox:read:messages inbox:write:events read:preferences write:preferences read:brands",
    "expires_in": "$YOUR_NUMBER days"
  }'
```

## Inbox APIs

Use the Courier Inbox APIs to read and update inbox messages.

Pass an optional `filter` to `getMessages()` (and the same shape to `getUnreadCounts()` — see [Unread Counts](#unread-counts)) to narrow results by tags, archive state, read status, or a **creation time lower bound** (`from`, an ISO 8601 datetime string).

```ts theme={null}
import { CourierClient } from '@trycourier/courier-js';

const courierClient = new CourierClient({
    userId: 'my-user-id',
    jwt: 'eyJ.mock.jwt',
});

// Fetch inbox messages for the authenticated user
const inboxMessages = await courierClient.inbox.getMessages();

// Messages created on or after a given time (e.g. last 7 days)
const since = new Date(Date.now() - 7 * 24 * 60 * 60 * 1000).toISOString();
const recent = await courierClient.inbox.getMessages({
  filter: { from: since },
});

// Fetch archived messages for the authenticated user
const archivedMessages = await courierClient.inbox.getArchivedMessages();

// Get the message id and tracking ids for the first message
const { messageId, trackingIds } = inboxMessages.data.messages.nodes[0];

// Mark a message "clicked"
await courierClient.inbox.click({
    messageId,
    trackingId: trackingIds.clickTrackingId,
});

// Mark a message "read"
await courierClient.inbox.read({ messageId });

// Mark a message "unread"
await courierClient.inbox.unread({ messageId });

// Mark a message "opened"
await courierClient.inbox.open({ messageId });

// Archive a message
await courierClient.inbox.archive({ messageId });

// Unarchive a message
await courierClient.inbox.unarchive({ messageId });

// Mark all messages "read"
await courierClient.inbox.readAll();

// Archive all "read" messages
await courierClient.inbox.archiveRead();

// Archive all inbox messages
await courierClient.inbox.archiveAll();
```

## Unread Counts

You can show unread badges or totals in a custom UI in three ways: a single global count, batched counts per filter (for tabs or feeds), or the `unreadCount` field returned with `getMessages()`. For live updates, connect the inbox WebSocket and refetch counts when message events arrive.

### `getUnreadMessageCount()`

Returns a `Promise<number>` with the total number of unread inbox messages for the authenticated user. When you pass `tenantId` to `CourierClient`, the count is scoped to that tenant.

```ts icon="square-js" theme={null}
const unreadCount = await courierClient.inbox.getUnreadMessageCount();
```

### `getUnreadCounts(filtersMap)`

Fetches unread counts for several named filters in **one** GraphQL request. Use this for per-tab or per-feed badges without multiple round trips.

| Argument     | Type                                                 | Description                                                                                                                          |
| ------------ | ---------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------ |
| `filtersMap` | `Record<string, CourierGetInboxMessagesQueryFilter>` | Map from your own dataset ID (e.g. tab key) to the same filters you use with `getMessages()` (`tags`, `archived`, `status`, `from`). |

Returns `Promise<Record<string, number>>` — the same keys as `filtersMap`, each mapped to an unread count.

If a filter sets `status: 'read'`, the client returns `0` for that key **without** calling the server (there are no unread messages in a read-only view).

```ts icon="square-js" theme={null}
const counts = await courierClient.inbox.getUnreadCounts({
  all: {},
  important: { tags: ['important'] },
  billing: { tags: ['billing'] },
});
// counts.all, counts.important, counts.billing
```

You can combine `from` with other fields so per-tab unread badges only count messages after a cutoff (same ISO 8601 string as `getMessages()`).

```ts icon="square-js" theme={null}
const since = new Date(Date.now() - 30 * 24 * 60 * 60 * 1000).toISOString();

const counts = await courierClient.inbox.getUnreadCounts({
  last30Days: { from: since },
});
```

### Unread count from `getMessages()`

`getMessages()` responses include both `count` (total messages matching the list filter) and `unreadCount`. Unless your filter already sets `status`, `unreadCount` reflects the unread subset that matches your `tags`, `archived`, `from`, and tenant constraints.

```ts icon="square-js" theme={null}
const response = await courierClient.inbox.getMessages({
  filter: { tags: ['important'] },
});

const unreadInImportantTab = response.data?.unreadCount;
const page = response.data?.messages;
```

### Real-time updates

The SDK does not push a numeric unread total over the socket. Subscribe to inbox message events, then refetch counts (or reload your message list) when relevant events fire.

1. Call `courierClient.inbox.socket.connect()` so the WebSocket is open.
2. Register listeners with `addMessageEventListener`. It returns a function — call it to unsubscribe.
3. Optionally compare `envelope.event` to `InboxMessageEvent` (exported from `@trycourier/courier-js`).

```ts icon="square-js" lines theme={null}
import {
  CourierClient,
  InboxMessageEvent,
  type InboxMessageEventEnvelope,
} from '@trycourier/courier-js';

const courierClient = new CourierClient({
  userId: 'my-user-id',
  jwt: 'eyJ.mock.jwt',
});

await courierClient.inbox.socket.connect();

const removeListener = courierClient.inbox.socket.addMessageEventListener(
  async (envelope: InboxMessageEventEnvelope) => {
    if (
      envelope.event === InboxMessageEvent.Read ||
      envelope.event === InboxMessageEvent.Unread ||
      envelope.event === InboxMessageEvent.NewMessage ||
      envelope.event === InboxMessageEvent.MarkAllRead
    ) {
      const updated = await courierClient.inbox.getUnreadMessageCount();
      // Update your UI (e.g. nav badge)
      console.log('Unread count:', updated);
    }
  }
);

// When you no longer need updates:
removeListener();
```

`InboxMessageEvent` includes: `NewMessage` (`'message'`), `Read`, `Unread`, `Archive`, `ArchiveAll`, `ArchiveRead`, `Clicked`, `MarkAllRead`, `Opened`, `Unarchive`, and `Unopened`. Listen to whichever events should trigger a refresh in your app.

<Tip>
  Building with React? [Courier React](/sdk-libraries/courier-react-web/) components and the `useCourier()` hook expose `totalUnreadCount`, per-tab unread counts, and real-time inbox updates without wiring the socket yourself. See [CourierInboxPopupMenu](/sdk-libraries/courier-react-web/) and the `useCourier()` section on that page.
</Tip>

## Preferences APIs

Use the Courier Preferences APIs to read and update user notification preferences per topic. See [Preferences](/api-reference/user-preferences/get-users-preferences) for more information.

```jsx theme={null}
// Get list of preferences
const preferences = await courierClient.preferences.getUserPreferences();

// Get a preference topic
const topic = await courierClient.preferences.getUserPreferenceTopic({
    topicId: 'HVS...'
});

// Update a preference topic
const topic = await courierClient.preferences.putUserPreferenceTopic({
    topicId: 'HVS...',
    status: 'OPTED_IN',               // 'OPTED_IN' | 'OPTED_OUT' | 'REQUIRED'
    hasCustomRouting: true,           // true | false
    customRouting: ['inbox', 'push'], // Array of: 'direct_message' | 'inbox' | 'email' | 'push' | 'sms' | 'webhook'
});
```

## Brands APIs

Use the Courier Brands APIs to read custom brand settings. See [Brands](/api-reference/brands/list-brands) for more information.

```jsx theme={null}
// Gets a brand by id
const brand = await courierClient.brands.getBrand({
    brandId: 'YF1...'
});
```

## Lists

Use the Courier Lists APIs to subscribe and unsubscribe users to lists. See [Lists](/api-reference/lists/get-all-lists) for more information.

```jsx theme={null}
// Subscribes the authenticated user to listId
await courierClient.lists.putSubscription({
    listId: 'your_list_id'
});

// Unsubscribes the authenticated user from listId
await courierClient.lists.deleteSubscription({
    listId: 'your_list_id'
});
```

## Models

### Inbox

#### `InboxMessage`

```ts theme={null}
export interface InboxMessage {
    messageId: string;
    title?: string;
    body?: string;
    preview?: string;
    actions?: InboxAction[];
    data?: Record<string, any>;
    created?: string;
    archived?: string;
    read?: string;
    opened?: string;
    tags?: string[];
    trackingIds?: {
        archiveTrackingId?: string;
        openTrackingId?: string;
        clickTrackingId?: string;
        deliverTrackingId?: string;
        unreadTrackingId?: string;
        readTrackingId?: string;
    };
}
```

#### `InboxAction`

```ts theme={null}
export interface InboxAction {
    content?: string;
    href?: string;
    data?: Record<string, any>;
    background_color?: string;
    style?: string;
}
```

#### `CourierGetInboxMessagesQueryFilter`

Optional filters for `getMessages()` and entries in `getUnreadCounts()`. Fields combine with **AND** logic.

```ts theme={null}
export interface CourierGetInboxMessagesQueryFilter {
  tags?: string[];
  archived?: boolean;
  status?: 'read' | 'unread';
  /** ISO 8601 datetime; only messages created at or after this time are included */
  from?: string;
}
```

#### `CourierApiUrls`

Shape used for `CourierClient` `apiUrls` and for `signIn` in React / UI packages. For presets, use `EU_COURIER_API_URLS` or `getCourierApiUrlsForRegion()` — see [EU and regional endpoints](#eu-and-regional-endpoints).

```ts theme={null}
export interface CourierApiUrls {
  courier: {
    rest: string;
    graphql: string;
  };
  inbox: {
    graphql: string;
    webSocket: string;
  };
}

export type CourierApiRegion = 'us' | 'eu';
```

### Preferences

#### `CourierUserPreferencesTopic`

```ts theme={null}
export interface CourierUserPreferencesTopic {
    topicId: string;
    topicName: string;
    sectionId: string;
    sectionName: string;
    status: CourierUserPreferencesStatus;
    defaultStatus: CourierUserPreferencesStatus;
    hasCustomRouting: boolean;
    customRouting: CourierUserPreferencesChannel[];
}
```

#### `CourierUserPreferencesStatus`

```ts theme={null}
export type CourierUserPreferencesStatus =
  | "OPTED_IN"
  | "OPTED_OUT"
  | "REQUIRED"
  | "UNKNOWN";
```

#### `CourierUserPreferencesChannel`

```ts theme={null}
export type CourierUserPreferencesChannel =
  | "direct_message"
  | "inbox"
  | "email"
  | "push"
  | "sms"
  | "webhook"
  | "unknown";
```

### Brands

#### `CourierBrand`

```jsx theme={null}
export interface CourierBrand {
    id: string;
    name: string;
    created: number;
    updated: number;
    published: number;
    version: string;
    settings?: CourierBrandSettings;
}
```

## EU and regional endpoints

Most apps use Courier’s default **US** API and inbox hosts. Use this section only if your workspace is on the [EU datacenter](/platform/workspaces/eu-datacenter).

By default, `CourierClient` uses `getCourierApiUrlsForRegion('us')` when you omit `apiUrls`. To target EU, use:

* **`EU_COURIER_API_URLS`** — frozen preset (`api.eu.courier.com`, `inbox.eu.courier.io`, `realtime.eu.courier.io`)
* **`getCourierApiUrlsForRegion(region)`** — returns a **copy** for `'us'` or `'eu'` (safe to tweak one host)
* **`DEFAULT_COURIER_API_URLS`** — US preset (same as `'us'`)

```ts icon="square-js" lines theme={null}
import { CourierClient, EU_COURIER_API_URLS } from '@trycourier/courier-js';

const courierClient = new CourierClient({
  userId: 'my-user-id',
  jwt: 'eyJ.mock.jwt',
  apiUrls: EU_COURIER_API_URLS,
});
```

```ts icon="square-js" theme={null}
import { CourierClient, getCourierApiUrlsForRegion } from '@trycourier/courier-js';

const courierClient = new CourierClient({
  userId: 'my-user-id',
  jwt: 'eyJ.mock.jwt',
  apiUrls: getCourierApiUrlsForRegion('eu'),
});
```

<Note>
  Mint JWTs on your backend against the **same** region as the client: for EU, use [`https://api.eu.courier.com/auth/issue-token`](https://api.eu.courier.com/auth/issue-token) (see [Create a JWT](/api-reference/authentication/create-a-jwt) and [EU Datacenter](/platform/workspaces/eu-datacenter)).
</Note>

<Tip>
  [Courier React](/sdk-libraries/courier-react-web/) and [Courier UI Web Components](/sdk-libraries/courier-ui-inbox-web/) re-export these helpers — pass the same `apiUrls` into `signIn({ ... })`. See the **Advanced** section on the React page.
</Tip>

Check out the full list of [Courier's API endpoints](/reference/get-started).
