Revenue Deduplication and Refunds

A vs B stores one record per (projectId, orderId) pair. This page explains how that deduplication works, how refunds reduce net revenue, and what the Gross / Net toggle on the Results page controls.

Order identity

Every purchase record is identified by two fields:

  • projectId: the project the purchase belongs to.
  • orderId: the unique order identifier sent with the order, whether that's from avsb.track.purchase in the browser, the Node SDK's trackPurchase, or a connected store.

The combination (projectId, orderId) must be unique. If an order arrives twice with the same orderId, the second write replaces the first. This is intentional. You can correct an order total after the fact. You can also send the order again to record a refund, as the next section explains.

Refunds

Neither the browser snippet's avsb.track.purchase nor the Node SDK's trackPurchase has a refund field today. Calling either one does not let you report a refund.

Refunds are recorded a different way, depending on where your orders come from:

  • A connected store. If you connect BigCommerce or WooCommerce as a store connection, A vs B reads the store's own refund data and computes the refund automatically. There's nothing to send yourself.

  • Your own server. Send the order again, directly to A vs B's ingestion endpoint, with a refunded amount added. This is a lower-level call than the SDK helpers. You build and send the request yourself, from your own backend code.

curl https://ingest.avsb.cloud/v1/collect/orders \  -H "Content-Type: application/json" \  -d '{    "sdkKey": "sdk_live_your_server_key",    "orders": [      {        "orderId": "ORDER-8842",        "total": 49.99,        "currency": "USD",        "refunded": 49.99      }    ]  }'
Shell13 lines

A successful call answers with a 200 and counts what it accepted:

200 response
{ "accepted": 1, "rejected": [] }
JSON1 line

total and refunded are plain decimal amounts in your currency, for example 49.99, the same way you'd send them with avsb.track.purchase. You don't need to convert to minor units (cents) yourself. A vs B does that conversion when it stores the order. That's why the Results page and the read-only Orders API report totalMinor and refundedMinor as whole numbers instead.

The refund is capped at the order total. A refund larger than the order total is stored as a full refund, not a negative value.

Reading orders back

You can look up a recorded order read-only, through the public API at GET /api/v1/projects/{projectId}/orders. This needs the orders:read scope (a named permission on an API key that controls what it's allowed to read or change). That endpoint only reads orders; it doesn't accept writes.

Net vs gross revenue

Revenue metrics on the Results page can be viewed in two modes:

ModeFormula
Net (default)totalMinor - refundedMinor
GrosstotalMinor (refunds ignored)

Use the Net / Gross toggle at the top of the Revenue panel on the Results page to switch between modes. The toggle triggers a fresh fetch, so all revenue figures on the page update together.

Net revenue is the recommended default: it reflects the revenue your business actually kept. Gross revenue is useful when you want order volume without your refund rate mixed in. It's also useful when refunds are processed asynchronously and haven't been recorded yet.

Currency mismatch exclusion

Each project has a single project currency (set in Project settings). Orders recorded in a different currency are stored but excluded from revenue statistics, because mixing currencies would produce meaningless totals.

When at least one order was excluded, the Results page shows:

N orders in other currencies excluded

This note appears on each affected revenue metric card. The count is combined across every variation (the control and every challenger) in the experiment.

Why exclusion instead of conversion?

Converting between currencies needs a reliable, point-in-time exchange rate, and rates change daily and differ between providers. Excluding mismatched orders is the only approach that keeps experiment results reproducible and auditable over time.

To avoid exclusions, make sure the currency field on every order matches the project currency. If you operate in multiple currencies, create a separate A vs B project per currency.

Project currency setting

Set the project currency once in Project settings → General → Currency. The setting controls:

  • Which currency code is displayed next to all money values on the Results page.
  • Which orders are included vs excluded from revenue calculations.

The default project currency is USD.

Was this helpful?