Skip to content

Conversions and visits

RoutingCat judges a page by its conversions, counted per visit. This page explains what counts, for which visit, and when a figure is final, so you can choose an objective and a window and know what each count means. Track conversions shows the code that sends them.

The objective and the outcomes

Each page has one objective: the main conversion that routing learns to get more of.

You declare it in routingcat.config.json, as the example site does:

"objective": "waitlist",
"pages": {
"home": { "routes": ["/"], "url": "/" },
"pricing": { "routes": ["/pricing"], "url": "/pricing" }
}
  • The objective counts once per visit. A visit converts or it doesn’t; a second sign-up in the same visit adds nothing.
  • Several events can make one objective. A list of them, such as trial_started and monthly_started, counts whichever happens first, once.
  • A page can have its own objective, declared as objective in its entry under pages, which replaces the site’s on that page. Its name starts with the page’s key, as pricing/trial_started.
  • Outcomes, the other events you track, are declared beside the objective, as a list in outcomes. They are counted and reported, and never steer routing: show gives each variant’s rate for them, and sources counts them by where visits came from. Revenue is reported the same way.
  • A page with two or more live variants needs an objective; plan refuses it otherwise, and so does the build. Events under names you haven’t declared are recorded, but not analyzed until you declare them.

Choose an event that happens soon and often, because routing learns only from conversions inside the window below. For a SaaS with trials, make sign-up or trial start the objective and track the 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.

Changing a page’s objective in code, its events or its window, starts it afresh: its figures start a new series, the page’s learning starts over, and each live variant gets a fresh scorecard. A changed window keeps the objective’s name and makes a new version of it, as signup@2.

Visits

A visit starts the first time a visitor reaches a routed page, and lasts 7 days by default. RoutingCat counts in visits: a visitor who comes back after their visit has ended starts a new one.

On each routed page, a visitor keeps the same variants for the whole visit. A later visit may show different ones, as routing learns. The part of a slot under a fixed share is the exception: it keeps each of its visitors on one variant across visits, even as other variants are added, paused or removed. A change reaches a visitor who has already seen the page in this visit like this:

  • a variant edited under its key shows its new version;
  • a variant paused or retired is replaced by the slot’s default for the rest of the visit;
  • a new variant, or a slot added to the page, waits for the visitor’s next visit.

A visitor is a browser, known by its rc_vid cookie, not a person.

One person on a phone and a laptop is two visitors, until your server links both to one account through the HTTP events API, or a page does with the browser script’s routingcat.identify().

The window

A conversion counts for a page when it happens within the objective’s window after the visitor’s first view of that page in the visit: 24 hours by default, and never past the end of the visit.

A longer window catches slower conversions, and slows learning by as much: a visit teaches routing only once its window has closed, so a 7-day window delays every lesson by a week. A window can’t be longer than the visit: lengthen the visit first, in Routing settings.

How a conversion finds its visit

Each conversion is credited to at most one visit per page, within that visit’s window:

  1. By visitor: the visit of the browser whose id the event carries.
  2. Otherwise by account: the most recent visit, still within its window, of any visitor linked to the event’s account, in whichever order the link and the event arrive.

So someone who saw the page on their phone and signed up on a laptop still counts for the phone’s visit, once your code has linked both browsers to their account. A conversion counts once, never for both a routed visit and a control visit. Conversions that reach no routed visit are still counted, in sources and in the tracking line of status, which says how the objective’s conversions joined their visits.

Matured and incomplete figures

A visit has matured once its window has closed: its outcome is known.

  • A rate over matured visits is settled. Learning and the measured gain use only matured visits, so a sign-up an hour into a visit still waits for its window to close.
  • A rate over all visits so far is marked incomplete. It reads low, since recent visits can still convert.
  • A figure with no matured visit yet reads “not matured”, with the day the first ones mature.

A conversion that arrives late still counts if it happened within its window, and the figures it belongs to are updated.

Where events come from, and how far to trust them

Source Sent by How far to trust it
server Your server code, or any backend through the HTTP events API, with a key only your servers hold The most: it comes from your code, with your key. It keeps the time the event happened, and its id drops duplicates.
browser A call in the page, with the site’s public key As a claim. The key is public, so such events can be forged, and each takes the time it arrives.
posthog A PostHog destination forwarding an event PostHog captured As far as PostHog’s own capture and delivery: what PostHog misses, RoutingCat misses too.
connector A connection to your payment provider or store, which reads each payment or order from it As far as the provider’s own records: each is read from the provider, keyed on its own object so that a repeat counts once, and timed when the buyer acted.

Your server’s calls to the events API send server events, and the browser script’s routingcat.track() sends browser events.

Send the objective from your server, or through a connection, where you can, since every other figure rests on it. status --json lists the sources each objective’s events came from.

Next