createAtuneMiddleware()
Auto-capture behavior from every server request, fire-and-forget.
createAtuneMiddleware() returns a middleware that records attributed server requests
as signals. npx atune init installs it for you; add it by hand when you want
control or aren't on Next.js.
// 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);
}What it captures
- Each request is recorded as
http.<method>.<normalized-route>, resolved to the account and user from your auth session. - Fire-and-forget. The call is never awaited on your request path — a slow or down Atune can never delay or break your app.
- Bodies are never read. No passwords, tokens, or card numbers ever leave your app.
Chaining with an existing middleware
Already running Clerk (or any other) middleware? Call atune() inside your
handler so both run. With Clerk, pass the account from the auth your callback
receives (atune 0.1.5 or later):
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's session can't be read from inside a proxy any other way, so without
clerkAccount(auth) capture sends no events. For a custom response, return
atune(request, response, { account: clerkAccount(auth) }) after your existing
handler. It preserves that response while scheduling capture. Do not replace
your auth policy with this example. npx atune init does not reliably chain
all existing middleware; inspect its changes before running the app.
Account resolution
- Clerk: pass
{ account: clerkAccount(auth) }as shown above. - Supabase: detected automatically from the request's session cookies.
- NextAuth: Atune tries to read the session. If no events arrive, pass
getAccount.
If your app has teams or organizations, the team/org is the account;
otherwise the signed-in user. See the
Next.js guide for details and the escape hatch when your
auth can't be auto-detected. Supabase detection needs @supabase/ssr,
NEXT_PUBLIC_SUPABASE_URL and either NEXT_PUBLIC_SUPABASE_PUBLISHABLE_KEY or
NEXT_PUBLIC_SUPABASE_ANON_KEY.
If no account is resolved, capture sends no event. For B2B apps, verify that
members of one company share an account ID. Override resolution with
getAccount using your verified server session when auto-detection does not
match your customer model; never trust an unsigned client account header.
getAccount can also return a subscription (stage, plan, seats, mrr,
currency, trialEndsAt). It is sent with each event, so Atune knows who pays
and can see downgrades. See Trial, paying and subscription value.