Add shipping insurance
Quote package protection with the SDK, then register the policy with one server-to-server call.
Shipping insurance on the custom path works like warranties do: the browser SDK quotes and displays the offer, and your server makes the call that creates the policy when the order is placed. For what shipping insurance covers and how the premium works, see Shipping Insurance.
Four pieces make it work:
- Quote and show the offer (browser, SDK). Fetch a quote for the cart subtotal and render the Package Protection widget. When the customer opts in, you add the premium to your own cart as a line item.
- Register the policy (server, REST). When the order is placed, POST it — including the quote's
offerId— toPOST /api/v2/shipping-insurance/orders. This is what creates the policy. - Report the shipment (server, REST). Send the tracking number when the order ships.
- Report a cancellation (server, REST) if the order is cancelled.
Registering the policy is what creates itCharging the customer the premium does not insure anything by itself. Treat a successful
POST /api/v2/shipping-insurance/ordersresponse as the definition of "the policy exists" — if the call fails, retry it or flag the order for follow-up.
Prerequisites
- The SDK is loaded and initialized with your public token. See Initialize the SDK.
- Shipping insurance is enabled on your Mulberry account by the Mulberry team, including your customer rate. Contact your account manager if you're not sure.
- Your server has your API token for the registration and lifecycle calls — the same credential split as Register the sale: public token in the browser, API token server-side only.
Quote and show the offer
After mulberry.core.initCompleted resolves, the SDK exposes mulberry.shippingInsurance:
await window.mulberry.core.init({ publicToken: "<your-public-token>" });
if (window.mulberry.shippingInsurance.isEnabled()) {
const offer = await window.mulberry.shippingInsurance.getOffer({
cartSubtotal: "100.00",
});
const widget = await window.mulberry.shippingInsurance.render({
offer,
selector: ".shipping-insurance-slot",
placement: "cart",
onToggle: (selected, offer) => {
// Add or remove the premium as a line item in YOUR cart.
// Mulberry never touches your cart — this is your code.
},
});
}isEnabled()—trueonce init has completed, shipping insurance is enabled on your account, and the shopper's location is eligible. Check it before quoting.getOffer({ cartSubtotal })— quotescartSubtotal × your customer rateand returns{ offerId, premium, cartSubtotal }(all strings). Pass the product subtotal only — no shipping, tax, or the premium itself.render({ offer, selector, placement, onToggle })— renders the Package Protection card (title, description, terms link, checkbox) into yourselectorelement, inheriting its font and text color.placementpicks between two copy slots,"cart"(default) and"shipping_options"— the names describe where a checkout typically offers protection, but they're just independently configured wording variants; use the slot whose copy Mulberry set up for where your card sits.onToggle(selected, offer)fires on every checkbox change — this is where you add or remove the premium line in your cart.- The returned widget instance has
update(offer)anddestroy().
You should now see the Package Protection card inside your selector element — the title with the premium, a short description with a terms link, and a checkbox. If nothing rendered, check the browser console: every failure logs its reason there.
When the cart changes, quote again and refresh the widget:
const fresh = await window.mulberry.shippingInsurance.getOffer({ cartSubtotal: newSubtotal });
widget.update(fresh);Register with the latest offerId. A quote is valid for 24 hours and each offerId can create one policy; superseded quotes simply go unused.
On failure, the SDK returnsnull
getOfferandrendernever throw — they log the reason to the browser console and returnnull(SDK not initialized, shipping insurance not enabled, invalid subtotal, selector not found). Code defensively: ifgetOfferreturnsnull, don't show the offer.
Register the policy
When the order is placed with protection selected, your server makes one call:
POST https://www.getmulberry.com/api/v2/shipping-insurance/orders
Authorization: Bearer <your-api-token>
Content-Type: application/json
(Use https://staging.getmulberry.com against staging.)
| Field | Type | Required | Notes |
|---|---|---|---|
external_order_id | string | Yes | Your order id. Also the idempotency key — retries with the same id are safe. |
offer_id | string | Yes | The offerId from the quote the customer accepted. |
premium | decimal | Yes | What the customer paid for protection. Must match the quote exactly. |
order_items_subtotal | decimal | Yes | Product subtotal including the premium line — see the warning below. |
order_shipping | decimal | Yes | Shipping charged. Send 0 if none. |
order_tax | decimal | Yes | Tax charged. Send 0 if none. |
customer | object | No | email (required within the object), first_name, last_name, phone, billing address fields. Strongly recommended — the customer receives the confirmation email and is who files claims. |
order_items_subtotalincludes the premiumSend the order's product subtotal as charged, with the protection line still in it — Mulberry subtracts the premium on its side. Sending a premium-exclusive subtotal under-insures the order by exactly the premium, and fails outright when the subtotal is at or below the premium.
Responses
201 Created—{"policy_uuid": "...", "insured_value": "111.11"}. The policy exists.200 OK— replay of anexternal_order_idyou already registered; returns the originalpolicy_uuid. Retries are safe.
| Status | Body | Meaning |
|---|---|---|
400 | {"error": "Invalid payload", "errors": {...}} | Missing or malformed fields; errors maps each field to its problem. |
400 | {"error": "Invalid offer token: expired"} | The quote is older than 24 hours — quote again next time; this order needs Mulberry support. |
400 | {"error": "premium does not match the quoted offer"} | The premium you sent isn't the one this offer_id quoted. |
400 | {"error": "Shipping insurance not configured for this retailer"} | Not enabled on your account. |
409 | {"error": "Offer token already used"} | This offer_id already created a policy for a different order. Each quote registers once. |
401 | — | Wrong or missing token. This endpoint takes the API token, not the public token. |
The insured value is derived by Mulberry, not sent by you. From your breakdown and the signed quote: the products (net of premium) are covered up to the quoted subtotal, and shipping + tax on top, also capped at the quoted subtotal. Example: a $100.00 quote at a 2% rate → premium $2.00; registering order_items_subtotal 102.00, order_shipping 5.00, order_tax 6.11 insures $111.11. Because coverage is capped at the quoted basis, always register with a fresh quote for the final cart — a stale, smaller quote caps what the customer is covered for.
All amounts are plain decimal strings in your account's configured currency; there is no currency field.
Report the shipment
When the order ships, send the tracking so the shipment is registered with the insurer:
POST https://www.getmulberry.com/api/v2/shipping-insurance/shipments
Authorization: Bearer <your-api-token>
| Field | Required | Notes |
|---|---|---|
policy_uuid or external_order_id | One of the two | Identifies the policy. |
tracking_number | Yes | |
carrier | Yes | Carrier name, e.g. "UPS". |
shipment_date | Yes | Any common date format. |
destination_address | Recommended | address1, city, state, postal_code, country. Registered with the insurer as the shipment's destination — omit it and the shipment is on file with no address. |
customer | Recommended | first_name, last_name, email, full_name. Registered with the insurer as the insured party. |
origin_address and shipping_cost are also accepted. 200 returns {"status": "Recorded", ...} with the insurer's shipment id. Retrying is safe: re-sending an already-recorded shipment replays 200 with the same stored result (if a claim has since been filed, status reads "Claimed").
One policy covers one shipment: sending a different tracking_number for an already-recorded policy returns 409, so on a multi-package order only the first recorded package is insured.
Report a cancellation
If the order is cancelled, void the policy:
POST https://www.getmulberry.com/api/v2/shipping-insurance/cancellations
Authorization: Bearer <your-api-token>
Send policy_uuid or external_order_id, plus an optional reason. 200 returns {"status": "Voided"} — including when the policy is already voided, so this call is safe to repeat.
If the order is cancelled before it ships, there's nothing to void: no shipment was registered with the insurer, the policy simply never activates, and the call returns 409 Cannot void a policy in Pending state. That response is expected in this flow — don't retry it.
Next
Test the full loop — quote, opt in, register, ship — against staging before going live: Test and go live.
Updated 1 day ago
