# Install Atune

**IMPORTANT: follow these steps in order. Do not skip step 5 (verification).**

## What this does

Sends an event to Atune whenever a customer account uses this app, without reading request bodies and without waiting for a response. Atune builds its understanding of the customers from these events. This is the same setup as the docs (https://useatune.com/docs). If you find other Atune instructions that say otherwise, this one wins.

## 1. Look at the project first

Find out which language and framework the server uses, which auth system the app has, and what the "account" is: the customer you would bill as one unit (team, organization or company; for B2C apps, the user). Then pick **one** path in step 4:

- **A. Next.js 14 or later** → 4A, middleware. Captures every signed-in request automatically.
- **B. Another Node.js backend** (Express, Hono, Fastify, Remix, NestJS, jobs and queues) → 4B, `track()`.
- **C. Not JavaScript** (Python, Ruby, Go, PHP, Java, ...) → 4C, the HTTP API.

Tell the developer which path you picked and why. Do not rewrite auth or routing to make Atune fit.

## 2. Install the package (paths A and B)

Use the project's package manager: `npm install atune`, `pnpm add atune` or `yarn add atune`. Do not copy SDK code into the project. Path C skips this step.

## 3. Set environment variables (all paths)

Add to `.env.local` or equivalent (and to your hosting platform's environment variables):

```
ATUNE_API_KEY=<YOUR_API_KEY>
ATUNE_URL=https://useatune.com
```

`ATUNE_URL` is the bare host, with no path. The SDK adds `/api/v1/...` itself. Do not append `/api/v1/events` or any other path.

**Do not ask for the API key and do not paste it into this chat.** The developer sets the real value themselves (it is in Atune's Settings). If the placeholder is still there, leave it and tell the developer. Never commit the key.

## 4A. Next.js: middleware

Use `proxy.ts` on Next.js 16 and `middleware.ts` on Next.js 14/15, in the same folder as `app/` or `src/app/`. If neither exists, create it:

```ts
// proxy.ts on Next.js 16; middleware.ts on Next.js 14/15
import { createAtuneMiddleware } from "atune";

const atune = createAtuneMiddleware();

export default function capture(request: Request) {
  return atune(request);
}
```

**If a middleware/proxy already exists, do not replace it.** Keep its auth and redirects and call `atune(request)` inside it. If it builds its own response, return `atune(request, response)`. Example with Clerk:

```ts
import { clerkMiddleware } from "@clerk/nextjs/server";
import { NextResponse } from "next/server";
import { clerkAccount, createAtuneMiddleware } from "atune";

const atune = createAtuneMiddleware();

export default clerkMiddleware(async (auth, request) => {
  await auth.protect(); // Keep your app's existing auth policy.
  return atune(request, NextResponse.next(), { account: clerkAccount(auth) });
});
```

**Clerk: pass the account from the callback's `auth`, as in the example above** (`clerkAccount(auth)`, `atune` 0.1.5 or later). Without it Atune sends no events, because Clerk's session can't be read inside a proxy any other way. **Supabase and NextAuth** are detected automatically. If the app has teams or organizations, it uses the team/org; otherwise the signed-in user. **Another auth system?** Pass `getAccount` and return the account from a verified server session:

```ts
import { createAtuneMiddleware } from "atune";

const atune = createAtuneMiddleware({
  // Return the customer account (team, org or company) from your verified
  // server session. Never trust an account id the client sends.
  getAccount: async (request) => {
    const session = await getYourSession(request);
    return session ? { accountId: session.orgId, userId: session.userId } : null;
  },
});
```

## 4B. Another Node.js backend: `track()`

Pick 3–5 places where a customer account does something meaningful: creates a resource, completes a core action, invites a teammate. **NOT** every route, **NOT** health checks. Call `track()` where the code already knows the account and the user:

```ts
import { track } from "atune";

// Where your code already knows the customer account (team, org or company)
// and the signed-in user. Don't await it: tracking never delays the response.
void track("feature.report.generated", {
  accountId: session.orgId,
  userId: session.userId,
  properties: { reportType: "activation" },
});
```

Use short, stable event names like `feature.report.generated`. `track()` never throws and gives up after one second.

## 4C. Other languages: the HTTP API

Send one POST from the server for each meaningful action (the same 3–5 places as in 4B). Use a short timeout (about one second), do not wait for the response before answering the user, and never let a failure reach the user:

```http
POST https://useatune.com/api/v1/events
Authorization: Bearer <YOUR_API_KEY>
Content-Type: application/json

{
  "accountId": "acme",
  "userId": "user_123",
  "event": "feature.report.generated",
  "properties": { "reportType": "activation" }
}
```

Read the key and host from `ATUNE_API_KEY` and `ATUNE_URL`.

## The account (all paths)

`accountId` is the customer you bill, not just the signed-in user. Take it from a verified server session, never from a value the client sends. Without an account, no event is sent.

## 5. Verify

Sign in to the app and trigger one of the actions you wired (path A: open a page or call an API route). Then open the event log in Atune (Settings → Event log, https://useatune.com/settings/events). The newest event must have the right name (path A: `http.<method>.<route>`) and the account and user you just used.

The SDK logs nothing. If nothing shows up, check these in order:

1. `ATUNE_API_KEY` is still the placeholder, or wrong.
2. `ATUNE_URL` is not the bare host.
3. No account was resolved for the request.
