Docs Go to app →

Agent setup

This page is written for a coding agent (Cursor, Claude Code, Codex, and similar) to wire Letter into an application end to end. Follow the steps in order. The goal: install the SDK, authenticate without ever putting a secret in chat, and start reporting users and events so onboarding sequences can fire.

No secrets appear in this document. The API key is provisioned by a browser confirmation during npx @letterapp/cli and written directly to the project’s environment file. Never paste an API key into a prompt or commit it to source.

1. Install and authenticate (one command)

Run the Letter CLI from the project root:

npx @letterapp/cli

What it does, interactively:

  1. Prints a short confirmation code and opens your browser to the Letter dashboard (press Enter, or use the printed URL on a headless machine).
  2. You confirm the code matches and pick the project to connect.
  3. The CLI receives a freshly minted API key over a secure back channel and writes LETTER_API_KEY to .env.local (plus ~/.letter/credentials.json for tooling). The key is never printed to the terminal.
  4. It detects your package manager and installs @letterapp/node.

If @letterapp/node is not installed automatically, add it manually:

npm install @letterapp/node
# or: pnpm add @letterapp/node / yarn add @letterapp/node / bun add @letterapp/node

Confirm LETTER_API_KEY exists in .env.local. Do not echo its value.

2. Create a server-side client module

Letter is a server-side SDK. Create one shared client and import it where you need it. Example (lib/letter.ts):

import { Letter } from "@letterapp/node";

export const letter = new Letter({
  apiKey: process.env.LETTER_API_KEY!,
  // Only set baseUrl for self-hosted or local Letter instances. Omit in prod;
  // the SDK defaults to https://api.letter.app. The CLI writes LETTER_BASE_URL
  // to the env file when it is needed.
  ...(process.env.LETTER_BASE_URL ? { baseUrl: process.env.LETTER_BASE_URL } : {}),
});

Keep the key in the environment. Never inline the raw key in source.

3. Identify users

Call identify wherever a user signs up or logs in, so Letter knows who they are. Use a stable userId (your primary key), include email, and pass any traits you segment on.

await letter.identify({
  userId: user.id,
  email: user.email,
  traits: { name: user.name, plan: user.plan },
});

4. Track key events

Call track on the 2-3 actions that matter most for lifecycle messaging (activation, first value, upgrade). Start small.

await letter.track({
  userId: user.id,
  event: "Signed Up",
  properties: { source: "web" },
});

Good first events for most products: Signed Up, the core “aha” action, and a conversion/upgrade event.

5. Group users into accounts (B2B, optional)

If the product is B2B, associate users with their account/workspace:

await letter.group({
  userId: user.id,
  accountId: workspace.id,
  name: workspace.name,
  traits: { plan: workspace.plan },
});

6. Flush in serverless and scripts

On long-running servers, the calls above auto-batch in the background. In serverless handlers and one-off scripts there is no background time, so flush before the function returns or the process exits:

await letter.flush();
// or, in a script: await letter.close();

For serverless, you can also construct the client with flushAt: 1 and use the *Sync methods (identifySync, trackSync).

7. Verify

Trigger a real signup (or call identify once from a script), then confirm the contact/event landed. The onboarding “waiting” screen lights up automatically on the first call; you can also use the @letterapp/mcp check_connection tool.

Reference

Summary for the agent

When done, summarize for the human: which files you created or edited, where you placed identify/track/group calls, the events you instrumented, and that LETTER_API_KEY lives in .env.local. Do not commit unless asked, and never print or commit the API key.