Introduction
You can run a Cashback Offer on a Hydrogen or custom storefront by capturing the customer’s opt-in and carrying the offer details through the cart to the order. You create and manage the offer in Rivo exactly as you would for a theme storefront.
On a standard Shopify theme, Rivo’s storefront script does this work for you: it records the opt-in and writes the cashback attributes onto the cart. On a headless storefront that script isn’t running, so your app has to perform both steps itself.
Cashback estimation in checkout is a separate integration surface. It is authenticated by Shopify’s checkout session, so you cannot call it with the storefront token you use for opt-ins.
Before You Begin
Rivo Plus. The Developer Toolkit, including the storefront API token used here, is available on the Plus plan. You’ll see an upgrade prompt under Settings > Developer Toolkit if your plan doesn’t include it.
Cashback enabled for your store. Ask your Rivo account manager or support to confirm Cashback is turned on, and make sure Cashback Program Status is enabled under Programs > Cashback > Settings.
Headless storefront access. Ask Rivo to enable headless storefront API access and to allowlist your store’s myshopify.com domain. Without the allowlist, token-authenticated requests are rejected.
A storefront API token. Once headless access is enabled, go to Settings > Developer Toolkit > Rivo Hydrogen Setup and select Generate Storefront API Key. The same section shows the API base URL to use. You can regenerate the key from there at any time — regenerating invalidates the previous one.
Your storefront token is not the same thing as a REST API key. The REST API key on the same settings page is for server-to-server calls and should stay on your server.
For general setup, see Guide to using Rivo with Shopify Hydrogen Headless setups. For key management, see Manage API keys.
1. Create and Activate Your Offer
In Rivo, go to Programs > Cashback > Offers and select New Cashback Offer.
Set the offer name, identifier, Shopify Segment, and holding period, and save.
On the saved offer’s page, configure its earning rule to set the cashback amount. The earning rule is a separate step from the offer details form.
Set the offer to Active before your end-to-end test and at launch. Only active offers are used for checkout estimates.
Two settings matter more than the rest for a headless build:
Identifier. This is the value you send to the API and write onto the cart — not the offer’s name. It’s the same value that appears in the offer’s Cashback URL. Sharing a campaign URL does not implement opt-in handling for you; your storefront still has to do the work below.
Shopify Segment. An offer with no segment selected will not award cashback on the order, even when the opt-in and cart attribution are perfect. Segment membership is what decides eligibility at order time.
When you create an offer, Rivo also creates a Rivo: Cashback Eligible segment in Shopify for you to use — but it does not select it on the offer. You still have to pick a segment on the offer yourself. Saving an offer with the segment left empty is the most common reason a correct headless integration produces no cashback.
See Cashback Offers for the standard offer settings and approval flow.
2. Capture the Customer’s Opt-In
Send a JSON request to:
POST https://loyalty-api.rivo.io/api/cashback_opt_ins?shop=example.myshopify.com
Use these headers:
Authorization: Bearer YOUR_STOREFRONT_API_TOKENContent-Type: application/json
Example request body:
{ "visitor_token": "YOUR_VISITOR_TOKEN", "visit_token": "YOUR_VISIT_TOKEN", "cashback_offer": "YOUR_OFFER_IDENTIFIER", "email": "[email protected]"}visitor_token: A token you generate for this visitor. Keep it stable — Rivo matches repeat opt-ins on the visitor token plus the offer.visit_token: The token for this visit. Carry the same value into the cart.cashback_offer: The offer’s identifier from Rivo.email: The customer’s email, if you have it. Rivo uses it to match an existing customer and stores it on the opt-in.logged_in_customer_id: Optional. The authenticated customer’s numeric Shopify customer ID. Only send an ID you have verified through your own sign-in flow.
A captured opt-in returns an id along with success: true. Save that ID for the next step. Sending the same visitor token and offer again returns the existing opt-in’s ID rather than creating a duplicate, so retries are safe.
Check for the returned id, not just HTTP 200 or success: true. If Cashback is switched off for the store, or cashback_offer is missing or doesn’t match an existing offer identifier, the endpoint still answers success: true — with no id. That is not a completed opt-in. A success: false response with an errors list means the opt-in itself was rejected. The shop parameter must match the store the token belongs to, or the request is rejected as unauthorized.
3. Carry Cashback Attribution Through the Cart
After you have the opt-in ID, save these attributes on the Shopify cart the customer will check out with. Preserve any attributes already on the cart.
Cart attribute | Value |
| The same |
| The same |
| The offer identifier sent as |
| The |
Write these through your headless cart integration, wait for the cart update to succeed before sending the customer to checkout, and confirm the completed order still carries them. These attributes are what Rivo reads on the order:
_rivo_cashback_offerselects which offer’s earning rule calculates the cashback._rivo_cashback_opt_in_idlinks the order back to the opt-in and starts the holding period. If the offer has no holding delay the credit is approved immediately; otherwise the opt-in is held until the configured time passes. Orders with a linked opt-in are taggedrivo-cashback-opt-inin Shopify, which is a quick way to confirm attribution landed.
Saving the opt-in alone is not enough. Cashback is awarded from the order’s attribution plus the offer’s eligibility rules, so an opt-in on its own is not a promise of credit. The checkout cashback display reads this same attribution; it doesn’t replace this step.
4. Handle Cashback Estimates in the Correct Context
The checkout estimation endpoint is:
POST https://loyalty-api.rivo.io/api/checkout/cashback_estimation
This endpoint is authenticated by a Shopify checkout session token in the Authorization: Bearer header. A storefront API token or a REST API key will not authenticate it. Rivo’s checkout cashback extension obtains that session token from within Shopify checkout.
For a custom integration running inside Shopify checkout, the request body includes:
order.subtotal_price,order.total_discounts,order.total_tax,order.total_shipping: the checkout totals.order.cart_attributes: an array of{ "key": "...", "value": "..." }entries. It must include_rivo_cashback_offerwith the offer identifier — that’s how the endpoint knows which offer to price. If the array is there but the key is missing, you get a zero estimate; if you omitcart_attributesentirely, the request fails.order.checkout_line_items: the checkout lines, each withquantity,merchandise.product.id, andcost.totalAmount.amount, so product-specific earning rules can be applied.cart_currency_code: the checkout currency code. The returned formatted amount is in this currency.customer_email: the checkout customer’s email, when available, used to match an existing customer.
A calculated response returns success: true with cashback, cashback_formatted, formatted_cashback_usd_to_currency, and cashback_offer_identifier.
Handle these non-calculated responses before you display anything:
success: truewithcashback: 0and no formatted fields. This is returned when the identifier doesn’t match an active offer, the offer has no earning rule, the customer already has an earned store credit balance (from cashback, loyalty, or a manual adjustment), or the offer is limited to new customers and this customer has ordered before.success: false. Cashback is switched off for the store, or the request could not be processed.
Estimates are informational. They don’t guarantee final eligibility or approval of the credit.
If you want to show an estimate on the headless storefront, before Shopify checkout, talk to your account manager or support about the supported approach for your build. Don’t call the checkout endpoint with your storefront token, and don’t present your own calculation as guaranteed cashback.
Note that a headless storefront and Shopify checkout are separate surfaces. Using Hydrogen does not by itself rule out checkout extensions — confirm your checkout setup with support rather than assuming.
5. Test Before Launch
Run a test customer through a qualifying order and check each link in the chain:
The opt-in request returns an
id.The checkout cart carries all four attribution attributes, with values matching what you sent.
The completed order still carries them, and picks up the
rivo-cashback-opt-intag.The customer is a member of the offer’s customer segment and meets its conditions.
The opt-in and order appear in Rivo’s cashback activity, and the credit follows the configured approval and holding-period rules.
Troubleshooting
The opt-in request returns 401 or 403.
Confirm you’re sending a current storefront API token for this store, that shop is the matching myshopify.com domain, and that Rivo has enabled headless access and allowlisted that domain. Regenerating the key invalidates the old one, so check your deployed environment variables too.
The opt-in request succeeds but returns no ID.
Cashback is switched off for the store, or cashback_offer doesn’t exactly match an existing offer identifier. Identifiers are matched exactly — check for a stale or mistyped value.
The opt-in exists, but the order got no cashback.
Check the cart-to-order attribution first, then the offer’s customer segment — an offer with no segment set will never award cashback. Then check the earning rule and approval conditions. Keep the opt-in response ID and the test order details when you contact support.
The estimate returns zero.
Confirm the offer is Active, that cart_attributes is present and contains _rivo_cashback_offer, and that the customer has no existing earned store credit balance. Note that store credit from loyalty or a manual adjustment counts here, not just cashback.
The estimate request fails with my storefront token.
The estimation endpoint needs a Shopify checkout session token. Contact support for guidance on showing estimates outside Shopify checkout.
