Route carts between ways to pay
Give a share of new carts other ways to pay, with daily limits, an allow list and a kill switch, and read how each route does.
Payment routing splits your store's carts between two routes, each with its own ways to pay. Most carts take the default route, and a share you choose takes the rollout route, so that you can try a way to pay on real shoppers before you move everyone to it. You can keep your payment page on the default route, for example, and try card and PayPal on a tenth of carts.
Routing is the store's payment_routing setting, which PATCH /commerce/store changes. The Store API then offers each cart only its route's ways to pay, and GET https://api.coritan.com/api/v1/orgs/{org_slug}/commerce/payment-routing/stats reports how each route does. The API reference lists the stats under Commerce. The dashboard does the same on the Payment routing card, as Route payments describes.
Before you begin
Section titled Before you begin- Changing the setting takes an organization API key with the
commerce.store:writescope, or a member's token with the Admin role or above. Reading the stats takescommerce.payments:readorcommerce.finance:read, or the Billing role or above. - A route offers only what the cart's region offers, so list each way to pay you route in the regions'
payment_providers, as Add regions describes. - The daily limits count from midnight in the organization's time zone: the Timezone under Invoicing in the organization's settings. Without one, a day runs in UTC.
Turn routing on
Section titled Turn routing oncurl -X PATCH "https://api.coritan.com/api/v1/orgs/acme/commerce/store" \
-H "X-API-Key: $ORG_API_KEY" \
-H "Content-Type: application/json" \
-d '{"settings": {"payment_routing": {
"enabled": true,
"default_providers": ["external"],
"rollout_providers": ["stripe", "paypal"],
"percent": 10,
"allow_emails": ["alex@example.com"],
"max_order_total": {"USD": 50000},
"daily_order_cap": 40,
"daily_amount_cap": {"USD": 400000}
}}}'
| Key | Takes |
|---|---|
enabled |
true routes carts. false, the default, offers every cart every way to pay its region offers, as without the setting. |
default_providers, rollout_providers |
The ways to pay on each route, from stripe, paypal, manual and external. Each list names at least one, and none is on both. They default to ["external"] and ["stripe", "paypal"]. |
percent |
The share of new carts given the rollout route, from 0 to 100 with up to two decimals. Defaults to 0. |
allow_emails |
Up to 200 email addresses, or *@example.com for a whole domain, whose carts always take the rollout route. List your team's addresses to try the rollout route before shoppers do. |
paused |
true stops the rollout at once, as Pause the rollout describes. Defaults to false. |
max_order_total |
The largest total the rollout route takes in each currency, in minor units, such as {"USD": 50000}. |
daily_order_cap |
How many rollout orders a day can place before new carts take the default route, up to 1,000,000. null, the default, sets no limit. |
daily_amount_cap |
The same for what the day's rollout orders add up to in each currency, in minor units. |
salt |
What the split is worked out with: 16 to 128 letters, digits, - and _. Leave it out, and we make one on the first save and keep it. |
Send the whole setting each time: a key you leave out takes its default, except salt, which keeps its saved value. Sending another salt reshuffles the carts that have no route yet. GET /commerce/store answers the setting as saved, salt included, so you can change it and send it back.
Setting payment_routing to null removes it, turns routing off and drops the salt, so a later save makes a new one. A setting that is wrong answers 422 with invalid, and field names the key, such as settings.payment_routing.percent. A way to pay on both lists names settings.payment_routing.rollout_providers, and an amount names its currency, such as settings.payment_routing.max_order_total.USD.
How a cart gets its route
Section titled How a cart gets its routeA cart is given its route the first time the Store API lists its ways to pay or starts a payment for it, and it keeps that route, even when the shopper signs in.
The route follows the shopper rather than the visit. We work it out from the first of these that the cart has:
- Its signed-in customer.
- Its email address, trimmed and in lower case.
- The
visitor_idin itsmetadata, of 8 to 64 letters, digits and-, such as an ID your storefront keeps in a cookie. Set it when you create the cart, so that a guest who has not given an email address yet keeps one route across carts. - The cart itself.
That key, written as customer:<id>, email:<address>, visitor:<id> or cart:<cart id>, gives the cart a number from 0 to 9,999: the first 8 hex digits of the HMAC-SHA256 of <store id>:<key>, keyed with the salt, read as a number and taken modulo 10,000. The store's id is in GET /commerce/store. The cart takes the rollout route when its number is below the share times 100 and neither daily limit is reached, so a percent of 10 takes the numbers 0 to 999. Otherwise it takes the default route.
- The same key gets the same number for as long as the
saltstays, so a shopper's carts take the same route while the share stays the same. - Changing the share moves only the carts that have no route yet.
- Our staff can set a ceiling for your store, and a higher
percentcounts as the ceiling. The stats answer it asrollout_max_percent, with theeffective_percentthat results. - The daily limits are checked when a cart is given its route. They count the orders of the cart's mode paid on the rollout route since midnight. Once they reach
daily_order_cap, or their total in the cart's currency reaches itsdaily_amount_cap, new carts take the default route, and carts that already have the rollout route keep it.
What is checked each time
Section titled What is checked each timeEach time the Store API lists a cart's ways to pay or starts a payment, the first of these that applies decides the route:
- With
paused: true, the cart is offered the default route's ways to pay. - A cart whose email address, or whose signed-in shopper's address, is on
allow_emailstakes the rollout route, whatever the share, the limits or its total. - A cart whose total is above its currency's
max_order_totalis offered the default route's ways to pay while its total stays above it. - Otherwise, the cart takes the route it was given.
None of these changes the route the cart was given.
What the cart is offered
Section titled What the cart is offeredThe cart is offered the ways to pay that its region offers and its route lists, in the usual order. A route never falls back on the other one: when none of its ways to pay can take the cart, such as a payment page that does not take the cart's currency, the cart is offered nothing. manual takes test carts only, so a route that holds nothing else offers live carts nothing.
Read the route in the Store API
Section titled Read the route in the Store APIGET /store/carts/{cart_id}/payment-providers lists only the route's ways to pay, and its route says which route the cart is on now: default, rollout, or null while routing is off.
{
"payment_providers": [
{"id": "external", "display_name": "Credit / Debit Card", "test": false, "config": {"display_name": "Credit / Debit Card", "description": "Pay with your card, Apple Pay or Google Pay.", "brands": ["visa", "mastercard", "amex", "apple_pay", "google_pay"]}}
],
"payment_required": true,
"total": 5896,
"currency_code": "USD",
"route": "default"
}
Show the shopper only what the listing answers, and list it again when the cart changes, because a new total can take the cart above or below max_order_total. Starting a payment with a way to pay that the region offers but the route does not answers 409 with payment_provider_not_offered, with the cart's route and the ways to pay it offers in available. A way to pay that cannot take the cart at all answers 422 with invalid.
The payment session and the order keep the route. An order's payment_route is default or rollout, or null when routing was off or nothing was paid. GET /commerce/orders?payment_route=rollout lists the orders paid on the rollout route, and payment_route=none those paid with routing off, as Find orders describes.
Pause the rollout
Section titled Pause the rolloutSend the setting again with "paused": true, and the rest as it was:
curl -X PATCH "https://api.coritan.com/api/v1/orgs/acme/commerce/store" \
-H "X-API-Key: $ORG_API_KEY" \
-H "Content-Type: application/json" \
-d '{"settings": {"payment_routing": {
"enabled": true,
"paused": true,
"default_providers": ["external"],
"rollout_providers": ["stripe", "paypal"],
"percent": 10,
"allow_emails": ["alex@example.com"],
"max_order_total": {"USD": 50000},
"daily_order_cap": 40,
"daily_amount_cap": {"USD": 400000}
}}}'
While paused is true, every cart, those on the allow list included, is offered the default route's ways to pay. Each cart keeps the route it was given, so "paused": false puts it back. To end routing, send "enabled": false.
Read the routing stats
Section titled Read the routing statscurl "https://api.coritan.com/api/v1/orgs/acme/commerce/payment-routing/stats?from=2026-09-28&to=2026-10-04&livemode=true" \
-H "X-API-Key: $ORG_API_KEY"
| Parameter | Takes |
|---|---|
from, to |
Days such as 2026-10-01 in the organization's time zone, both included. to defaults to today, and from to 6 days before to. The stats cover up to 92 days. |
livemode |
true for live carts or false for test carts. Defaults to the store's own mode. |
{
"routing": {"enabled": true, "paused": false, "default_providers": ["external"], "rollout_providers": ["stripe", "paypal"], "percent": 10, "rollout_max_percent": null, "effective_percent": 10, "allow_emails": ["alex@example.com"], "max_order_total": {"USD": 50000}, "daily_order_cap": 40, "daily_amount_cap": {"USD": 400000}},
"time_zone": "America/New_York",
"livemode": true,
"from": "2026-09-28",
"to": "2026-10-04",
"today": {"date": "2026-10-04", "rollout_orders": 12, "rollout_amounts": {"USD": 118800}, "daily_order_cap": 40, "daily_amount_cap": {"USD": 400000}, "order_cap_reached": false, "amount_cap_reached": {"USD": false}},
"days": [
{"date": "2026-09-28", "routes": {
"default": {"carts_assigned": 310, "sessions_started": 120, "sessions_failed": 9, "orders": 96, "revenue": {"USD": 951200}},
"rollout": {"carts_assigned": 33, "sessions_started": 14, "sessions_failed": 1, "orders": 11, "revenue": {"USD": 104500}}
}}
]
}
routingis the setting as it acts now, without itssalt, with our staff's ceiling asrollout_max_percent(nullfor none) and theeffective_percentthat results.time_zoneis the time zone the days run in.todaycounts the rollout route's orders since midnight, and their totals per currency, against the daily limits, and says whether each limit is reached.daysholds each day fromfromtoto, oldest first. For each route,carts_assignedcounts the carts given the route that day,sessions_startedthe payments started on it andsessions_failedthose that failed, andordersthe orders paid on it, with their totals per currency inrevenue.
A cart counts on the day it was given its route, a payment on the day it started and an order on the day it was placed.
Result
Section titled ResultEach new cart takes the default or the rollout route and keeps it. The Store API offers it only its route's ways to pay, each order records its payment_route, and the stats count each route's carts, payments and orders from the day they start.
Troubleshooting
Section titled Troubleshooting422withfieldset tosettings.payment_routing.rollout_providers- A way to pay is on both lists. Each one is on one route at most.
409withpayment_provider_not_offered- The way to pay is not on the cart's route. Offer the shopper only what
GET /store/carts/{cart_id}/payment-providerslists. - A cart is offered no way to pay
- None of its route's ways to pay can take it: its region does not offer them, they do not take its currency, or the route holds only
manualfor a live cart. Add a way to pay to the route, or offer one in the region. - The rollout route takes fewer carts than the share
- Our staff's ceiling may be lower, which
effective_percentshows. A daily limit may be reached, whichtodayshows, and a cart abovemax_order_totalis offered the default route. Carts that had a route before you raised the share keep it. - Shoppers changed route
- The
saltchanged, because you sent another one or setpayment_routingtonulland saved it again. Leavesaltout to keep the saved one. 422withfieldset tofromorto- Send days as
2026-10-01, withtoon or afterfrom, and up to 92 days apart.
Related
Section titled RelatedAPI operations on this page
| Method | Path | What it does |
|---|---|---|
GET | /api/v1/orgs/{org_slug}/commerce/payment-routing/stats | How the store's payment routing splits its carts |