Skip to content

Set up RoutingCat

This tutorial takes an Astro site on Vercel or Cloudflare Workers from nothing to a working install, in four steps. Your agent does most of the work; you approve a sign-in, choose what to track, deploy and add a card, with nothing charged. One real conversion of your own, recommended and your choice, confirms your tracking end to end. Your visitors see no change, because no page is routed until you declare one.

It follows Lumen, a photo-editing app whose site is built with Astro, set up on Vercel and on Cloudflare Workers. What your site needs lists the requirements.

1. Install with init

In your Astro site’s folder, beside its Astro config, you or your agent run:

Terminal window
npx routingcat init

It checks your project first, and stops before changing anything when something needs you:

  • Astro 7.3.1 or later, installed. Upgrading is your call; the message gives Astro’s upgrade command.
  • Where the site deploys, read from the folder: a Vercel link or vercel.json, a Wrangler configuration, or the adapter in your Astro config. When the folder names neither host, or both, init asks at a terminal, and otherwise stops: run it again with --host vercel or --host cloudflare-workers.
  • On Vercel, a folder linked to a Vercel project, and the Vercel CLI signed in: run npx vercel link or npx vercel login, then init again.
  • On Cloudflare Workers, Astro’s Cloudflare adapter, whose custom entrypoint RoutingCat’s Worker wraps: run npx astro add cloudflare, then init again. Wrangler must be signed in, with npx wrangler login, unless account_id in the Wrangler configuration or CLOUDFLARE_ACCOUNT_ID names the account. And Workers Cache off for that Worker, which init refuses: see Cloudflare’s settings.

It signs you in. A browser page opens, where you sign in, or sign up, and allow the command on this machine. Where no browser can open, init prints the link, waits 50 seconds and exits with code 3: open the link, approve, and run init again, which continues with the same link.

It shows its plan, then applies it. At a terminal it asks whether to add a note to AGENTS.md, then whether to apply the plan; run by an agent, it asks nothing, applies the plan and prints what changed. --dry-run stops after the plan. On Lumen’s site on Vercel, run by an agent and signed in already:

routingcat 1.0.0 · <project>
Checks
✓ Astro 7.3.8 · pnpm
✓ Vercel project lumen · Vercel CLI signed in as emma
✓ no middleware.ts yet: init writes one
✓ signed in to RoutingCat
Plan
1 create the site lumen, bound to Vercel project lumen
2 pnpm add routingcat@1.0.0
3 astro.config.mjs add routingcat() from routingcat/astro to integrations
4 src/layouts/Base.astro add <RoutingCat /> first in <head>, from routingcat/astro/components
5 middleware.ts new: middleware() from routingcat/vercel on Node.js, and RoutingCat's matcher
6 .gitignore new: .routingcat/, where the build writes RoutingCat's credential
7 routingcat.config.ts new: the site and its public key
8 the skill .agents/skills/routingcat/, copied to .claude/skills/routingcat/
Nothing reaches visitors until you deploy.
✓ site lumen created (op_01m3yesth0fjfsctr17tbq1z82), bound to Vercel project lumen
✓ routingcat 1.0.0 added · 3 files edited, 3 files and 2 folders created (see git diff)
Ask the person: "Add a two-line note to AGENTS.md so coding agents always know RoutingCat is here? (recommended)" If yes, run npx routingcat init --agents-md.
Dashboard: https://app.routingcat.com/lumen
Next: read .agents/skills/routingcat/setup.md and follow it

On Cloudflare Workers, the same site gets a Worker in place of the middleware, and a note:

routingcat 1.0.0 · <project>
Checks
✓ Astro 7.3.8 · pnpm
✓ production address https://www.lumen.com, from site in astro.config.mjs
✓ Cloudflare Workers project lumen · account Lumen · Wrangler signed in as emma@lumen.com
✓ no src/worker.ts yet: init writes one
✓ wrangler.jsonc: init sets main src/worker.ts, run_worker_first and the version metadata binding CF_VERSION_METADATA
✓ signed in to RoutingCat
Plan
1 create the site lumen, bound to Cloudflare Workers project lumen
2 pnpm add routingcat@1.0.0
3 astro.config.mjs add routingcat() from routingcat/astro to integrations
4 src/layouts/Base.astro add <RoutingCat /> first in <head>, from routingcat/astro/components
5 src/worker.ts new: worker() from routingcat/cloudflare, which decides before the page
6 wrangler.jsonc set main src/worker.ts, run_worker_first and the version metadata binding CF_VERSION_METADATA
7 .gitignore new: .routingcat/, where the build writes RoutingCat's credential
8 routingcat.config.ts new: the site and its public key
9 the skill .agents/skills/routingcat/, copied to .claude/skills/routingcat/
Nothing reaches visitors until you deploy.
✓ site lumen created (op_01m3yesth0fjfsctr17tbq1z82), bound to Cloudflare Workers project lumen
✓ routingcat 1.0.0 added · 4 files edited, 3 files and 2 folders created (see git diff)
· RoutingCat needs Workers Paid: on the Free plan, past 100,000 requests a day, every page the Worker runs first on answers 429 until midnight UTC.
Ask the person: "Add a two-line note to AGENTS.md so coding agents always know RoutingCat is here? (recommended)" If yes, run npx routingcat init --agents-md.
Dashboard: https://app.routingcat.com/lumen
Next: ask the site's owner to run npx routingcat keys deploy --branch <your production branch> in their own terminal, and to set what it prints as ROUTINGCAT_DEPLOY_KEY in Cloudflare's build variables; read .agents/skills/routingcat/setup.md and follow it

The site is named after your Vercel or Cloudflare project and bound to it. It already shows in the dashboard, at the address init prints: Lumen’s is https://app.routingcat.com/lumen. Your plan covers it: Hobby, at no cost, holds 3 sites, and init refuses a 4th with the way out. Each edit has a purpose:

File What init does So that
package.json adds routingcat, at init’s own version one package carries the command, the code and the skill
Your Astro config adds routingcat() from routingcat/astro to integrations each build registers what changed, and your pages render the variants RoutingCat picks
Your layout adds <RoutingCat /> first in <head>, after the charset and viewport tags each page shows its visitor’s variants before it paints, and the browser reports what it showed
middleware.ts, on Vercel creates it, or composes yours, on Node.js, with RoutingCat’s matcher RoutingCat picks each visitor’s variants before Vercel’s cache and your pages, and browser reports reach RoutingCat through your own domain, at /_rc
src/worker.ts and the Wrangler configuration, on Workers creates the Worker around Astro’s own, runs it first on every path but Astro’s built assets, and adds the version metadata binding RoutingCat picks each visitor’s variants before your pages, browser reports reach RoutingCat at /_rc, and each deployment registers its version
.gitignore adds .routingcat/ the RoutingCat credential your build writes there stays out of your repository
routingcat.config.ts, or .mjs without TypeScript creates it, with your site and its public key the key names your site; it is public and can only write
.agents/skills/routingcat/, .claude/skills/routingcat/ installs the skill your agent knows the commands and the words
AGENTS.md, and CLAUDE.md if you have one with your agreement, adds a two-line note every coding agent knows RoutingCat is here, and reads the skill before changing its code

Every edit shows in git diff. On Workers, init also names your production address in routingcat.config.ts, as origin, the address doctor checks and preview links open: from --origin, else Wrangler’s routes and custom domains, else site in your Astro config. Where none gives it, init asks you for it at a terminal. Run by an agent, it stops instead, with exit code 2: you or your agent run it again with your address, as --origin https://www.example.com. The note concerns your Cloudflare account: Cloudflare’s settings explains it.

The note in AGENTS.md is your choice, and recommended. Coding agents read AGENTS.md in every session, but may open a skill only when a request matches it; the note tells them RoutingCat is here and to read the skill first. At a terminal, init asks, yes by default. Run by your agent, it leaves the choice to you: your agent asks, then runs npx routingcat init --agents-md if you agree. See Add the note to AGENTS.md.

A file too unusual to edit safely is left as it is, and init prints the exact edit: a layout whose <head> opens with a component that may write the charset tag, several files that hold <head>, or, on Workers, a custom entrypoint of your own, around which it prints RoutingCat’s Worker. Make the edit, then run npx routingcat doctor, which checks it.

Run init again at any time. It marks with ✓ what is done and does only what is missing. It never rewrites a file it edited or a config you changed, except to correct a publicKey that isn’t your site’s public key. It refreshes the skill unless you edited it, and keeps the note in AGENTS.md up to date once you have it, without asking again.

2. Choose what to track

RoutingCat counts the conversions your code reports. Your agent reads your code, starting from the tools init detected, and asks you two questions:

  • The main conversion, pick one. This is the objective: what routing learns to get more of. It counts at most once per visit, when it happens within its window after the visitor first saw the page, 24 hours by default.
  • Also track, pick any. These outcomes are counted and reported beside it, and never steer routing.

Answer in your own words when the list misses something. A main conversion can be several events where any one counts, such as any plan start; it still counts once per visit.

For a SaaS with trials, choose sign-up or trial start as the main conversion, and track purchase beside it. A payment at a trial’s end comes days or weeks after the visit: the default 24-hour window misses it, and a longer window, up to the visit’s length, delays learning by as much. See Conversions and visits.

Lumen chooses sign-up, and tracks its newsletter beside it. Its trials and payments are Stripe’s, which RoutingCat doesn’t count on an Astro site yet, so they stay untracked rather than sent as your server’s own events.

3. Wire and declare them

Your agent wires each choice where it happens, with two calls that RoutingCat’s integration gives your server code on each request, in Astro.locals.routingcat:

  • Astro.locals.routingcat.identify(accountId) links the visitor to your own account id, at sign-up and at every sign-in, so that a conversion on another device still counts;
  • Astro.locals.routingcat.track(name, options) reports each conversion. A server event needs an id, because providers deliver some events twice.

The recipes are in Track conversions, with Clerk and other auth providers and PostHog on their own pages. A conversion in another app or backend is reported through the HTTP events API.

Then it declares them in routingcat.config.ts, beside what init wrote. An example Astro site’s, whose main conversion is joining its waitlist:

routingcat.config.ts
import { config } from 'routingcat/astro'
export default config({
site: 'routingcat-astro-example',
publicKey: 'pk_routingcat-astro-example_Vb7Nq2Ks', // public: names the site; write-only
objective: 'waitlist', // the main conversion: joining the waitlist
outcomes: ['contact_sales'], // tracked beside it: "Talk to us" on the about page, from the browser
})
  • Outcomes tracked beside it go in outcomes, a list of event names: Lumen’s are ['newsletter'], beside its objective, 'signup'.
  • Events under names you haven’t declared are recorded, but not analyzed until you declare them.
  • Declare no page yet: a page comes with your first idea.
  • In development, RoutingCat records nothing, and track and identify log that they weren’t recorded.

4. Deploy and verify

Deploy to production the usual way, or let your agent do it where your repository’s rules allow: on Vercel, a production deployment; on Cloudflare, a build of your production branch, in Cloudflare’s own builds or GitHub Actions. How the build proves itself to RoutingCat depends on the host:

  • On Vercel, with Vercel’s own signed token, so there is nothing to set.
  • On Cloudflare Workers, with the site’s deploy key. The site’s owner runs npx routingcat keys deploy --branch <your production branch>, which shows the key once, and sets it as ROUTINGCAT_DEPLOY_KEY in Cloudflare’s build variables, or in the secrets of the CI that builds the site. It proves production only for builds of that branch, as their CI reports the branch. To deploy from your own terminal instead, as astro build then wrangler deploy, run the build with ROUTINGCAT_ENV=production: it registers under your sign-in once you confirm its plan, as Deploy from your machine describes. Without it, such a build registers nothing, and the Worker it deploys decides nothing.

The build log confirms that the build registered, with a line that starts routingcat registered and names your objective and outcomes. When a line starting with routingcat: takes its place, this build registered nothing: visitors get your defaults, and the build ships as usual. The line says what to fix, such as turning on OIDC federation in your Vercel project’s settings, or setting the deploy key.

Run doctor and wait. Once the deploy is live:

Terminal window
npx routingcat doctor --wait

It requests your production page, so the new deployment starts and registers, then waits up to 5 minutes, printing each check as it passes. It stops early when only a person can move the rest: the main conversion’s first event and, on Hobby, the card on file.

Make one real conversion, recommended. It is the only end-to-end proof that a conversion reaches RoutingCat and joins its visit. Lumen’s is a sign-up, so you sign up once on the production site, with a second email address. This sign-up is recorded as a real one, credited to no variant until a page is routed, and shows within a minute. Where converting costs money, as an order does, you may prefer to skip it: the main conversion’s check then passes with your site’s first real conversion, and until then doctor waits on it, with exit code 3, which is expected. Nothing else waits on it.

Add a card to start routing. On Hobby, routing waits for a card on file, and nothing is charged: doctor shows card on file waiting, with the link. You add it in the dashboard’s Settings → Billing, from the account menu, or start Pro’s 14-day trial instead; one card serves every site you own. Routing starts within about 2 minutes of it, with no new deploy. Until then, a deploy that routes a page logs routingcat NOT LIVE routing waits for a card on file: add one at …, nothing charged, and visitors see your defaults. See Add a card to start routing.

Then run npx routingcat doctor again. It prints one line per check, and each passes in turn: the build and its deployment registered; on Vercel, the middleware’s runtime; on Workers, the Worker’s version; RoutingCat on your production page, as middleware on / or Worker on /; /_rc; the head snippet <RoutingCat /> writes; your pages’ policy; the records of your production address; the first arrival; your main conversion; and the card on file. Read doctor check by check explains each one.

Exit code Means
0 Every check passed.
1 A check failed. Its line gives the message and its id; Troubleshooting has the fix.
3 A check is still waiting, usually for that first conversion or the card on file.
4 A request couldn’t get through from here, as in a sandbox without network access.

Until a page is routed, doctor and status note that conversions are recorded but not yet credited to a variant. That is expected.

Close the setup. When your agent ran it, it ends by telling you, in plain words:

  • that RoutingCat works, naming the conversion that came through; without one, that the main conversion’s check passes with your site’s first real conversion, and that nothing else waits on it;
  • that your visitors see no change until a page is routed;
  • what RoutingCat now records, and which conversion routing will learn to get more of, which you can change by asking;
  • how it goes from here: you say what you’d like to try, at any level of detail, ask for ideas, or hand your agent the whole program;
  • that 10% of visitors keep seeing your current site, which is how the gain is measured;
  • your dashboard’s address: Lumen’s is https://app.routingcat.com/lumen, which Read the dashboard walks through.

Next