# Audit a Next.js Repository per Customer Request

Send each customer's Next.js repo to an agent with one API call and get back JSON on outdated packages, build and test results, and risky code, by webhook.

[Run this on Rush](https://cloud.getrush.ai/run/audit-customer-repos/nextjs/), in a Project that has your repository.

## What the agent does

Your backend starts a session per customer repository, and the agent reads what the project declares before running anything. In a Next.js codebase like nextjs/saas-starter, `package.json` pins `next` to `15.6.0-canary.59`, a canary build rather than a release, alongside `react` and `react-dom` `19.1.0`, `tailwindcss` `4.1.7`, `drizzle-orm` `^0.43.1`, `stripe` `^18.1.0` and `zod` `^3.24.4`, all under `dependencies` with no `devDependencies` at all. `pnpm-lock.yaml` is `lockfileVersion: '9.0'`, so it installs with pnpm. It runs `pnpm outdated` and keeps the direct packages whose latest release is a major version or more ahead of the locked one; a canary pin is reported in its own entry, since "behind" means something different for a prerelease.

Next it looks for tests and finds none. The scripts are `dev`, `build`, `start` and five `db:` scripts for Drizzle and seeding, with no `test` or `lint` script and no test files. The prompt covers that case: when a project defines no tests, the agent reports `pnpm build` and `pnpm exec tsc --noEmit` as the checks it could run, and says plainly in the `tests` array that no test command exists. The build needs configuration. `lib/db/drizzle.ts` throws `POSTGRES_URL environment variable is not set` at import, and `lib/payments/stripe.ts` constructs a Stripe client from `STRIPE_SECRET_KEY` the same way, so the agent reads `.env.example`, sets placeholder values for `POSTGRES_URL`, `STRIPE_SECRET_KEY`, `STRIPE_WEBHOOK_SECRET`, `BASE_URL` and `AUTH_SECRET` in the sandbox only, and records that it did.

For risky patterns it reads the code paths that matter in a SaaS app. It opens every route handler under `app/api/` and checks whether each one checks the session: `app/api/user/route.ts` returns whatever `getUser()` gives, so the agent follows `getUser` into `lib/db/queries.ts` to see what an unauthenticated request receives. It reads `app/(login)/actions.ts`, where server actions are wrapped in `validatedAction` or `validatedActionWithUser`, and checks that each action changing team membership or the account uses the second. It reads `lib/auth/session.ts`, which builds the JWT key from `process.env.AUTH_SECRET`, and `middleware.ts`, which only protects paths starting with `/dashboard`. The Stripe webhook in `app/api/stripe/webhook/route.ts` calls `stripe.webhooks.constructEvent`, so it is verified and does not go on the list.

No file is changed. The agent works on its own clone, the placeholder environment exists only in its sandbox, and the turn ends with the JSON as the final answer.

## Start it with one API call

`POST https://api.prix.dev/o/{org}/sessions`

```json
{
  "agent": "claude",
  "input": "Audit this Next.js repository without changing any file. Return only JSON with three arrays. outdated_dependencies: direct dependencies whose latest release is at least one major version ahead of the locked version, with name, locked and latest; report canary or prerelease pins with a note. tests: each test command in package.json and the CI workflows, with passed true or false and the last 20 lines of output when it failed; if there are none, say so and report pnpm build and tsc --noEmit as the checks instead, using placeholder values from .env.example in the sandbox. risky_patterns: up to ten issues such as API routes or server actions that do not check the session, secrets read without a check that they are set, unverified webhooks, or user input reaching SQL or HTML unescaped, each with file, line, pattern and a one-sentence reason.",
  "resources": [
    {
      "url": "https://github.com/your-org/your-app"
    }
  ]
}
```

1. **Get an API key.** In the console, open Settings, then API keys, and create a key for your org. Send it in the Authorization header as a Bearer token.
2. **Send the request.** POST the body in request.json to https://api.prix.dev/o/{org}/sessions, with your repository as the resources URL. The run starts in its own cloud sandbox.
3. **Read what came back.** Follow the run on its live stream or wait for the run.completed webhook, then read the result and its trajectory.

## What comes back

The session's `result` is the JSON, and the `run.completed` webhook includes it as `data.result` up to 16 KiB; for a larger audit the webhook leaves it out and your server reads `data.session_url` with the key. For a repository like this one the `tests` array is honest about what exists: an entry saying no test command is defined, then the build and type-check results with a note that placeholder environment values were used. A customer reading the audit learns both that the code compiles and that nothing tests it.

Your product validates the three arrays before saving them. Someone reviewing audits checks that each `risky_patterns` entry names a concrete path (this route, this action, this line) and a reason tied to it, and that a build failure is not just a placeholder value the code rejected. The transcript keeps the `pnpm outdated` output, the build log and every file the agent read, so each finding can be traced back.

## Run it on every new customer onboarding

When a customer finishes onboarding in your product and picks a repository, start the audit from the same request handler. Send an `Idempotency-Key` derived from the customer and repository so a double-submitted form returns the run already started, and keep the `execution_id` from the `202` with the customer record.

The key's webhook receives `run.completed` with the `execution_id` when the audit ends. Verify the Standard Webhooks signature with the `whsec_` secret, find the customer, store `data.result`, and show the audit on their first dashboard. If the event is `run.failed`, show that the audit could not finish and offer a retry with a new idempotency key.

## Why run it on Rush?

Building a customer's Next.js app means running `pnpm install` on their dependency tree, including any install scripts, and executing their build. Doing that in your own backend puts every customer's code on the same machines as your product. A Rush session runs it in a sandbox of its own, and the clone and the placeholder environment stay there; what reaches your servers is the JSON.

The audit is evidence, not an opinion. The outdated list comes from the lockfile and the registry, the build result from a build that ran, and each risky pattern from a file the agent opened, all recorded in the session transcript. Many customers can be audited at the same time, one sandbox each, up to the key's `max_concurrent` limit.

## Frequently asked questions

### What does the audit do when a repository has no tests?

It says so. The `tests` array gets an entry stating that no test command is defined, followed by the results of `pnpm build` and `tsc --noEmit`, which are the checks a project with no tests still has. That gap is often the most useful line in the audit.

### Does it need my customer's real environment variables?

No. The agent reads `.env.example` and sets placeholder values in the sandbox so the build can run, and the audit notes that it did. Real secrets are never needed for an audit, and you should not send them.

### Does it work with npm or Yarn projects?

Yes. The agent picks the package manager from the lockfile in the repository (`package-lock.json`, `yarn.lock` or `pnpm-lock.yaml`) and the `packageManager` field when there is one, and uses that manager's own outdated command.

### How many audits can run at once?

As many as the key's `max_concurrent` allows, 4 by default. Starting one more answers `429`; queue the request on your side and start it when a webhook tells you another audit finished.
