Integrate Captain Shipping Protection with a Shopify Headless Storefront
This guide explains how to add Captain Shipping Protection to a custom Shopify storefront.
It covers two integration options:
React storefronts: use Captain's standard widget and controller hook.
Other storefronts: use the framework-independent SDK and render your own UI.
Both options use the same Shopify cart and checkout flow.
Before you start
Make sure:
Captain Shipping Protection is configured for your Shopify store.
The Captain Shipping Protection product is published to the Shopify Headless sales channel used by your storefront.
Your storefront can read and update a Shopify cart.
You can install npm packages.
Your production storefront uses HTTPS.
You know the active shopper country, locale, and currency.
Important: Captain calculates which protection variant should be used, but your storefront is responsible for adding or removing Shopify cart lines.
Publish Shipping Protection to your Headless sales channel
This step is required. If the Captain Shipping Protection product is not available on the same Headless sales channel as your Storefront API token, Shopify cannot add the protection variant to the cart.
In Shopify Admin:
Go to Products.
Open the Captain Shipping Protection product.
Find Publishing, Sales channels, or Sales channels and apps.
Click Manage.
Enable the Headless sales channel used by your storefront.
Click Done, then save the product if Shopify asks you to save.
The channel name depends on your Shopify setup. Select the channel associated with the Storefront API token used by this headless storefront.
If you have multiple Headless channels, publishing the product to a different channel is not sufficient. The protection product must be available to the exact channel making the cart request.
You can verify the setup by querying the protection product or variant through the same Storefront API token. Shopify must return the product and allow its variant to be used as merchandiseId in cartLinesAdd.
Choose an integration
Use the React package when
Your storefront uses React 18 or later.
You want Captain's standard widget.
You want checked, loading, error, and cleanup state managed for you.
Install:
Use the Core SDK when
Your storefront uses Vue, Svelte, Angular, Solid, or Vanilla JavaScript.
You already have a shipping protection UI.
You want full control over state and presentation.
Install:
How the integration works
Captain recommends adding protection only when checkout begins:
Read the current Shopify cart.
Convert it to Captain's cart format.
Ask Captain for the current price, variant, and eligibility.
Show the protection option when the cart is eligible.
Store the shopper's checked choice in your storefront.
If the cart already contains protection, remove that line and keep the shopper checked.
When checkout begins, request the latest protection information.
Add one unit of the latest variant when the shopper is checked.
Redirect immediately to the checkout URL returned by Shopify.
This approach avoids repeatedly changing the Shopify cart while the shopper edits products or quantities.
Important: Toggling the widget records shopper intent only. It should not immediately add or remove a Shopify cart line.
Prepare your Shopify cart data
Both packages require the same SdkCartData structure.
Shopify Storefront GraphQL returns GIDs and decimal money strings. Captain expects numeric Shopify IDs and integer minor-unit prices.
For USD:
"12.99"from Shopify becomes1299."100.00"from Shopify becomes10000.
Use a cart mapper like this:
Create a new mapped cart whenever products, quantities, discounts, currency, or line prices change.
Option A: React integration
The recommended React integration combines:
ShippingProtectionWidgetfor the standard UI.useShippingProtectionControllerfor Captain state and requests.
Add the controller
Replace the adapter props with your Storefront API or Hydrogen cart implementation.
React state behavior
The controller handles these rules:
Uses the merchant's default checked setting on the first eligible cart.
Keeps the shopper's checked choice when the cart changes.
Sets the widget to checked when an existing protection line is found.
Calls
removeProtection()to clean up that existing line.Prevents duplicate cleanup requests.
Hides the widget when the cart is excluded.
Recalculates the latest price and variant in
prepareCheckout().
React checkout behavior
prepareCheckout() returns:
ShippingProtectionInfowhen protection should be added.nullwhen the shopper is unchecked or the cart is excluded.nullwhen an existing protection line could not be safely cleaned up.
After receiving protection info:
Convert
variantsIdto a Shopify ProductVariant GID.Add exactly one unit to the cart.
Use the checkout URL returned by that mutation.
Redirect immediately.
Do not remain on the cart page after adding protection. The cart-page controller removes existing protection so the next checkout attempt can calculate a fresh variant.
Option B: Core SDK integration
The Core SDK returns plain data and works with any browser framework.
Initialize the SDK
Call init() again if the active shop, country, locale, or currency changes.
Request protection information
The response includes:
Manage your UI state
The Core SDK does not store checked state. Use your framework's normal state management.
Implement removeProtectionLinesByProductId, refreshHostCart, and renderProtection in your application.
Prepare checkout with the Core SDK
Connect the Shopify Storefront Cart API
Both integration options require the host storefront to add and remove cart lines.
Add protection at checkout
Use Shopify's cartLinesAdd mutation:
Variables:
Remove existing protection
Find cart lines whose Shopify product ID matches info.productId, then remove their cart line IDs with cartLinesRemove:
Match by product ID, not only by variant ID. Captain may choose a different variant when the eligible cart total changes.
Always check Shopify userErrors before updating your local cart or redirecting.
SSR storefronts
Run Captain SDK operations in the browser.
Next.js App Router
Place the integration in a Client Component:
Pass serializable cart data from your Server Component to the Client Component.
Hydrogen, Remix, Nuxt, and SvelteKit
You can load the Shopify cart on the server, but initialize Captain after the component mounts or from browser-only cart code.
Do not call init(), getInfo(), or prepareCheckout() during the server render.
Content Security Policy
Captain sends configuration and eligibility requests to:
If your storefront has a Content Security Policy, add this origin to connect-src:
The React widget may display a merchant-configured external icon. If your CSP restricts images, add the icon's exact origin to img-src.
Captain does not require an external script, iframe, or font origin. Do not add broad permissions such as * or 'unsafe-inline' solely for this integration.
Understand common states
The widget is visible and checked
The shopper wants protection. Do not add it yet. Add the latest variant when checkout begins.
The widget is visible and unchecked
Continue checkout without adding protection.
includedProtection is true
The Shopify cart already contains Captain's protection product:
Keep the widget checked.
Remove all matching protection lines.
Refresh your host cart.
Keep checked enabled after the cart refresh.
isExcluded is true
Hide the widget and continue checkout without protection.
Common exclusion reasons include:
empty_cartinvalid_cartonly_shipping_protectionexcluded_variantcheck_display_hidden
Treat exclusion reasons as extensible.
Captain requests fail
Hide or disable the protection UI and allow checkout without protection unless your business requirements say otherwise.
Test your integration
Before deployment, test:
An eligible cart with the widget checked.
An eligible cart with the widget unchecked.
An empty cart.
A cart containing only protection.
A cart containing an excluded variant.
Quantity changes that select a different protection price.
A cart loaded with an existing protection line.
Failure while removing an existing protection line.
Failure while adding protection.
A stale response from an older cart request.
A storefront with CSP enabled.
Checkout redirect with and without protection.
Troubleshooting
The widget does not appear
Check:
The store has an active Captain configuration.
SDK initialization completed.
info.isExcludedandinfo.excludedReason.Cart product and variant IDs are valid numeric IDs.
Browser requests are not blocked by CSP or CORS.
Toggling does not add a Shopify cart line
This is expected. The toggle stores intent only. Add the variant returned by the latest Captain request when checkout begins.
The protection price is incorrect
Verify:
total_priceuses integer minor units.Every
final_line_priceuses integer minor units.The latest cart is passed after products, quantities, or discounts change.
Existing protection is included in mapped cart data so Captain can subtract it from the eligible total.
Shopify cannot add the returned variant
Convert a numeric variant ID to:
Then confirm the Captain Shipping Protection product is published to the exact Headless sales channel associated with your Storefront API token.
When the product is not published to that channel, Shopify commonly returns an error similar to:
The variant may exist in Shopify Admin and still be unavailable to your headless storefront. Open the protection product's publishing settings, enable the correct Headless channel, save, and retry with the same Storefront token.
Duplicate protection lines appear
Before adding protection:
Remove lines matching
info.productId.Refresh the cart.
Request Captain info again.
Add exactly one unit of the returned variant.
Protection is removed immediately after checkout starts
Add protection and redirect immediately using the checkout URL returned by the same mutation. Do not publish the updated cart back to the visible cart page and remain there.
Go-live checklist
Captain Shipping Protection is configured for the store.
The Shipping Protection product is published to the exact Headless sales channel used by the Storefront API token.
The correct npm package or packages are installed.
Shopify GIDs and money values are mapped correctly.
The active country, locale, and currency are passed to Captain.
The UI updates after every relevant cart change.
Shopper toggle behavior changes intent only.
Existing protection lines are removed by product ID.
Checkout waits for protection cleanup to finish.
The latest variant is requested immediately before checkout.
Exactly one protection line is added.
Shopify
userErrorsare handled.Checkout redirects using the updated cart URL.
CSP allows
https://insurance.captaintop.com.Error paths allow the shopper to continue checkout safely.
Package reference
For complete API and type details:
React package:
captain-shipping-protection-reactFramework-independent SDK:
captain-shipping-protection-sdk
Last updated