# auth.md

You are an agent that wants to use Novu on a user's behalf. This file describes when you need credentials at all, how to obtain them today, and how to handle them safely.

Several hosts are relevant:

- **API (US)** — `https://api.novu.co` — REST API base path is `/v1` (for example, `https://api.novu.co/v1/tenants/{identifier}`).
- **API (EU)** — `https://eu.api.novu.co` — same paths under `/v1` for EU Cloud accounts.
- **Dashboard (US)** — `https://dashboard.novu.co` — where the user manages environments, workflows, and keys.
- **Dashboard (EU)** — `https://eu.dashboard.novu.co` — EU Cloud dashboard.
- **MCP (US)** — `https://mcp.novu.co/` — Novu MCP server for agent tooling.
- **MCP (EU)** — `https://eu.mcp.novu.co/` — EU MCP endpoint.
- **Documentation** — `https://docs.novu.co` — guides, API reference, and MCP setup.

Match the user's region across API, dashboard, and MCP. If their dashboard is `eu.dashboard.novu.co`, use the EU API and EU MCP URLs.

## First, decide whether you need credentials at all

Novu now supports **keyless** flows: you can do meaningful work for the user with no Novu account and no key up front. Reach for credentials only when the task needs account-owned, persistent, or production resources. Pick the smallest option that satisfies the task.

**Use no authentication (keyless) when the user is testing, prototyping, or just getting started:**

- **Embedding the `<Inbox />` component to see a working notification UI.** Render the Inbox with no `applicationIdentifier` and no `subscriberId` — keyless mode boots an isolated, temporary environment so the user can evaluate look, feel, and behavior instantly. The data is temporary (expires in ~24h) and is **not** tied to real subscribers, so it is for local testing and demos only, never production.
- **Spinning up a managed Novu agent connected to a channel.** Use the keyless CLI flow (`npx novu@latest connect …`, no `--secret-key`) to create a temporary, hosted agent and connect Slack or email without a Novu account. The full step-by-step runbook for this lives in `https://novu.co/agents.md` — follow that file for the agent-onboarding flow rather than improvising. Keyless agents are temporary until the user **claims** them by signing up through the in-channel link, which migrates the agent, channel, and conversation into their own workspace.

**Use a secret key when the user needs account-owned or production resources:**

- triggering real workflows and notifications to real subscribers
- managing subscribers, preferences, topics, tenants, integrations, or environments
- syncing Framework workflows to Novu Cloud
- the persistent, production-connected `<Inbox />` (with a real `applicationIdentifier` and `subscriberId`)
- anything you would call through the Novu MCP server or the server-side SDKs

If the task is keyless, **stop here** — do not ask the user for a key you do not need. The rest of this file covers the secret-key path.

## Keyless mode in detail

### Keyless `<Inbox />`

Import and render the component with no props to get an instant, isolated notification UI:

```tsx
import { Inbox } from "@novu/react";

export function NotificationInbox() {
  return <Inbox />;
}
```

`@novu/nextjs` exposes the same component. This requires no `applicationIdentifier`, no `subscriberId`, and no key. When the user is ready for real subscribers, switch to the connected form:

```tsx
import { Inbox } from "@novu/react";

export function NotificationInbox() {
  return (
    <Inbox
      applicationIdentifier="APPLICATION_IDENTIFIER"
      subscriber="SUBSCRIBER_ID"
    />
  );
}
```

The `applicationIdentifier` is public and client-side only (see below). For EU Cloud, also pass `backendUrl="https://eu.api.novu.co"` and `socketUrl="wss://eu.socket.novu.co"`. Setup guide: `https://docs.novu.co/platform/inbox/setup-inbox`.

### Keyless agent (`npx novu@latest connect`)

To create a hosted agent without an account, follow the dedicated runbook at `https://novu.co/agents.md`. In short: it runs one non-interactive `npx novu@latest connect "<description>" --ci --channel <slack|email|skip>` command (no `--secret-key`), hands the user a channel-specific action, and explains the demo limit and sign-up-to-claim path. Do not pass `--secret-key` for keyless; only add it when the user wants to use their existing account.

## Current state for keyed access

For account-owned access, Novu does not support agentic key registration today. Secret keys are issued by the user out of band; this file tells you how to pick them up and use them without putting them at risk.

Novu keys are **environment-specific** (Development vs Production). Always use the secret key for the environment the user intends you to work in.

## Use the existing tooling first

Before you do anything credential-shaped, check whether the user has already wired Novu into your environment.

1. **Novu MCP server** — if you are an MCP client (Claude, Cursor, Codex, ChatGPT, etc.), Novu ships a remote MCP server that exposes subscribers, preferences, workflows, triggers, notifications, integrations, and environments as tools. The user connects with OAuth through their client — you call the tools without handling credentials directly. Setup: `https://docs.novu.co/platform/build-with-ai/mcp`. Product page: `https://novu.co/mcp/`.
2. **Novu documentation** — server-side API usage, workflow design, channel providers, and Framework patterns live at `https://docs.novu.co`. Read the relevant guide before calling the API directly (especially authentication, triggers, and subscriber management).
3. **Novu CLI** — for Framework workflows, Local Studio, and syncing workflows to Novu Cloud, prefer `npx novu@latest` over hand-rolled `curl`. It accepts `--secret-key`, `--api-url`, and region-aware `--dashboard-url`. Package: `https://www.npmjs.com/package/novu`. Docs: `https://docs.novu.co/framework/studio` and `https://docs.novu.co/framework/deployment/cli`.

If any of these is already configured, use it and stop. Do not ask the user for a secret key you do not need.

## Supported login option: secret key, supplied out of band

The credential for server-side API access is a **Secret Key** from the Novu dashboard. It grants administrative access to the linked environment. The user issues it from **Developer → API Keys** (`https://dashboard.novu.co/settings/api-keys` or `https://eu.dashboard.novu.co/settings/api-keys`) and supplies it through a secure channel — never by pasting it into chat.

Novu also exposes an **Application Identifier**, which is public and used only to initialize the Inbox and other client-side SDKs. It is **not** a substitute for the secret key on server-side API calls.

### How to pick the secret key up

Look for it in this order. Stop at the first one that exists:

1. `--secret-key` on `npx novu@latest` commands (`dev`, `sync`, `init`), if the user passed one for this invocation.
2. `NOVU_SECRET_KEY` in your process environment (common in SDK and backend apps).
3. A project `.env` file the user has told you to read.
4. `secretKey` (or equivalent) in an initialized `@novu/api` / `@novu/node` client config.

If none of the above is set and you genuinely need a key, do not ask the user to paste it into the conversation. Instead, tell them to:

- Open **API Keys** in the dashboard for the correct environment (Development or Production).
- Copy the **Secret Key** for that environment.
- Put it in `NOVU_SECRET_KEY` in their shell, `.env`, or pass it to the Novu CLI via `--secret-key` — whichever matches how they invoke you.
- Resume the task once it is set.

This keeps the key out of your transcript, out of any logs the user shares, and out of the model provider's training data.

### How to use the secret key

**REST API and server SDKs** — present it as an API key in the `Authorization` header:

```http
POST /v1/events/trigger HTTP/1.1
Host: api.novu.co
Authorization: ApiKey $NOVU_SECRET_KEY
Content-Type: application/json
```

For EU Cloud, use host `eu.api.novu.co` instead of `api.novu.co`.

With the Node SDK:

```typescript
import { Novu } from "@novu/api";

const novu = new Novu({
  secretKey: process.env.NOVU_SECRET_KEY,
});
```

Read the key from the environment at the moment of the call. Do not copy it into variables you log, do not echo it back to the user, and do not include it in commit messages, PR descriptions, error reports, or screenshots. If you are running a shell command, never interpolate the key inline — reference the environment variable so it does not appear in command history.

The Novu API and backend SDKs are **server-side only**. Using the secret key in browser code will fail with CORS errors and exposes the key publicly.

**Novu MCP** — connect through OAuth in the user's MCP client. Point the client at `https://mcp.novu.co/` (US) or `https://eu.mcp.novu.co/` (EU) with no `Authorization` header — the client handles sign-in. Run the `whoami` tool to verify the session. OAuth sessions default to the Development environment; call `get_environments` and pass an `_id` as `environmentId` to act on Production or another environment. See `https://docs.novu.co/platform/build-with-ai/mcp` for Cursor, Claude Code, ChatGPT, Codex, and other client-specific setup.

Regenerating the secret key in the dashboard invalidates the previous value. Treat a `401` on a previously-working key as revocation: drop it from memory and ask the user to refresh whichever source you read it from. For MCP OAuth sessions, ask the user to reconnect through their client if a `401` appears.

### Application identifier (public, client-side only)

The Application Identifier is safe to use in frontend code (for example, `NEXT_PUBLIC_NOVU_APPLICATION_IDENTIFIER`). Use it only to initialize the Inbox or other client SDKs. Never send it as the sole credential for workflow triggers, subscriber management, or other administrative API calls. If you only need to demo the Inbox UI, prefer keyless mode and skip this identifier entirely.

## Errors

| Status | Meaning | What to do |
| --- | --- | --- |
| `401` on first use | Key is malformed, revoked, or for a different environment or region. | Ask the user to confirm the secret key in their dashboard matches the environment and region (US vs EU) you are calling. |
| `401` on a previously-working key | Revoked or rotated. | Drop the cached value and ask the user to refresh it from their secret store, not from chat. |
| `403` | Authenticated but not allowed for this resource or environment. | Confirm you are using the correct environment key and that the user's role permits the operation. |
| `429` | Rate limited. | Back off and retry. Honor `Retry-After` if present. |

## Revocation

The user regenerates secret keys under **Developer → API Keys** in the dashboard. You will discover revocation as a `401` on a previously-working credential — drop it and re-read from the same source you loaded it from.

## Related files

- Agent onboarding (keyless agent flow): `https://novu.co/agents.md`
- Product and doc index for agents: `https://novu.co/llms.txt`
- Inbox setup (keyless and connected): `https://docs.novu.co/platform/inbox/setup-inbox`
- MCP setup and tool list: `https://docs.novu.co/platform/build-with-ai/mcp`
- API keys reference: `https://docs.novu.co/platform/developer/api-keys`
