The Faustine adapter for Next.js
The Faustine adapter for Next.js lets a Web Agent call your Server Actions and server-only data functions. It is two files in your repository: a generated registry of the functions you expose and a route handler that runs them in the user's session.
The adapter needs the App Router. The generator stops when the project has no app/ or src/app/ directory.
When an app needs it
The agent runs in the browser and calls what has a URL. Route handlers and external APIs work without the adapter. Server Actions and server-only functions have no URL that a caller outside the app can use.
When the analysis of a Next.js app finishes, its summary shows a Data surface card that sorts what the app does with its data into three groups.
| Group | What is in it | What the agent can do |
|---|---|---|
| Reachable now | Route handlers and client-side fetches. | Call them from the browser. |
| Reachable with the adapter | Server Actions and server-only data functions. | Call them once the adapter is installed. |
| Not reachable | Reads written inline in a Server Component. | Nothing. The card suggests a data function to extract, and once it exists it joins the group above. |
When the second group has entries, the Install the assistant card on the agent's Overview says how many operations are behind Server Actions or server-only functions and adds the adapter to the install command.
How it works
npx faustine-next init reads the operations the analysis found and writes two files. Faustine does not write to your repository. You, or your coding agent, run the generator in your checkout and review the diff.
At runtime the agent sends POST /api/faustine/<operation> from the browser to your own app, same origin, with the user's cookies. The handler runs the bound function inside the request, so cookies(), headers() and revalidatePath() work as they do in a Server Action. Your auth checks and your validation run as they do for your own forms.
The handler runs an operation only when it is in the registry and enabled in the version of the agent that the environment serves. It asks Faustine for that list and keeps it for 60 seconds. If Faustine cannot be reached, the handler reuses the last list for up to 10 minutes. After that it runs nothing and answers 503. Your app's own pages are not affected.
The registry
faustine/registry.ts (or src/faustine/registry.ts) has one line per operation: the name the agent knows and an import of your function.
import { createRegistry } from '@faustine-ai/next'
export default createRegistry({ webAgent: '<AGENT_ID>', formatVersion: 1 }, {
// Changes an invoice's status (draft, sent, paid, overdue, void).
'invoices.setInvoiceStatus': { fn: () => import('@/lib/actions/invoices').then((m) => m.setInvoiceStatus), params: ['invoiceId', 'status'] },
// Reads one invoice with its line items and totals.
'invoices.getInvoice': { fn: () => import('@/lib/dal/invoices').then((m) => m.getInvoice), params: ['id'] },
})Commit the file. To keep a function private, remove its line. sync does not add a removed line back.
The operation names belong to Faustine and the imports belong to you. When you rename or move a function, only the import changes. Do not edit an operation name by hand, because it is the name the agent calls.
params is the function's parameter list, read from its signature. It decides how the agent's input becomes the call.
| The function takes | The handler calls it with |
|---|---|
| One value | The input itself. |
| Several values | Each one read from the input by parameter name. |
A FormData |
A FormData built from the input's fields. |
The previous state of a useActionState action |
undefined. The agent never sends it. |
A line without params means the generator could not read the signature. The function then receives the input as its one argument.
The route handler
app/api/faustine/[...op]/route.ts mounts the handler.
import { createFaustineHandler } from '@faustine-ai/next'
import registry from '@/faustine/registry'
export const { GET, POST } = createFaustineHandler(registry)GET /api/faustine/manifest answers what the build binds: the registry hash, the bound operations and the ones enabled at that moment. The handler refuses a POST whose Origin header names another host, the same rule Next.js applies to Server Actions. See the @faustine-ai/next reference for the responses.
Setup codes and adapter tokens
The generator does not sign in to your Faustine account. Create install command on the Install the assistant card puts a setup code in the command. The code starts with fns_, can be used once and expires after 30 minutes.
The first run that uses the code exchanges it for an adapter token and stores the token on that machine, under ~/.config/faustine/adapters/. The token can read this agent's data surface and report its registry, nothing else. Each machine keeps its own token. A new install command adds a token and does not replace the others.
To list or revoke tokens, open the agent's Settings tab and select Faustine adapter. Revoke takes effect immediately, and that machine needs a new setup code.
The state of the adapter
The Faustine adapter section in Settings and the install card, which is titled Faustine in this app once the adapter is installed, show three states.
| State | How Faustine knows |
|---|---|
| Generated | faustine-next init or sync reported the registry from a machine. |
| In the repo | Faustine reads the registry in every push or uploaded ZIP: which operations it binds and whether every import still points at an exported function. |
| Deployed | The running app names its registry and its commit when the handler asks Faustine which operations are enabled. |
When the repository and the analysis disagree, the card lists what is missing. See Keep the adapter in sync.
What to know before you install it
| Consideration | Description |
|---|---|
| App Router only | <FaustineAgent> and faustineIdentityToken work with both routers. The registry, the route handler and the faustine-next CLI need the App Router. |
| Enabling is done in the portal | A bound operation runs only after it is enabled in the Actions tab and the version is published. Turning an operation off stops the handler from running it, without a deploy of your app. |
| Binding needs a deploy | A new line in the registry reaches users when you deploy your app. |
AGENTS.md |
init adds a block to AGENTS.md at the project root that tells coding agents to run npx faustine-next sync after they change a Server Action. |
| Outbound network | Your server calls webagent.faustine.ai to read the enabled operations. |
Next steps
- Let your agent call Server Actions: the adapter from start to finish on the Next.js version of Nimbus CRM.
- Keep the adapter in sync: what to do after you add, rename or remove a Server Action.
- faustine-next CLI:
init,syncanddoctor. - Agent skills: have your coding agent install and sync the adapter.
Last updated November 13, 2026