SubblySubbly
Log inGet started
  • Get started
  • Developer resources
Developer resources
Subbly.js SDK
    InstallConfigureKeep it up to dateBuild a checkoutHandle errorsRun a funnel
SubblyCart.js widget
    InstallConfigureKeep it up to dateWire it into your pageDrive it from your codeStyle it
Subbly.js SDK

Run a funnel

A funnel is a sequence of offers you set up in Subbly. Each offer is a step: a headline, a description, and one or more variants or plans, often at a discount. The Subbly.js SDK gives you the next step for a cart; you draw it, and the customer takes it or skips it.

The three phases

A funnel has three phases, and each phase has its own set of methods:

  • Pre-purchase offers come before checkout, while the customer still edits the cart.
  • Mid-purchase offers come during checkout.
  • Post-purchase offers come after the customer pays. They run only after subbly.checkout.purchase succeeds, on the thank-you page.

The loop

Every phase works the same way:

  1. Fetch the next step for the cart with the phase's Fetch method. The response is { step }.
  2. If step is null, the customer has seen every offer. Stop.
  3. Show the offer: step.title, step.description, and the variants and plans in step.offer.products. step.offer.type says how many the customer may take: single, multiple or one_click.
  4. Call the phase's Accept method with step.id and the productId of each product taken, or the phase's Reject method with step.id to skip.
  5. Fetch again.

Accepting a pre-purchase or a mid-purchase offer adds the products to the cart and returns a plain cart, not the subbly.cart model. subbly.cart still holds the old contents, so call subbly.cart.load() afterwards.

Accepting a post-purchase offer charges a new purchase on the card the customer already used. It returns the invoice, the subscription and the orders it created, not a cart.

The pre-purchase phase also has prePurchaseRefresh, which clears what the customer accepted or skipped so the sequence starts again from the first step.

Pre-purchase

Run this loop on the cart page, or wherever the customer reviews the cart before checkout.

Offer before checkout
async function runPrePurchase() { const cartId = subbly.cart.id while (true) { const { step } = await subbly.funnels.prePurchaseFetch(cartId, { expand: ['parent'] }) if (!step) break const productId = await showOffer(step) // resolves with an id, or null if (productId) { await subbly.funnels.prePurchaseAccept(cartId, step.id, { offers: [{ productId }] }) await subbly.cart.load() } else { await subbly.funnels.prePurchaseReject(cartId, step.id) } } }

showOffer is your own function. It draws the step and resolves with the id of the product the customer picked from step.offer.products, or null when they skip. With expand: ['parent'], each product also carries its parent product, so you can show its name and images.

Mid-purchase

Run the same loop during checkout, with the midPurchase methods. The payload and the return value are the same as in the pre-purchase phase, so reload subbly.cart after each accept here too.

Offer during checkout
async function runMidPurchase() { const cartId = subbly.cart.id while (true) { const { step } = await subbly.funnels.midPurchaseFetch(cartId) if (!step) break const productId = await showOffer(step) if (productId) { await subbly.funnels.midPurchaseAccept(cartId, step.id, { offers: [{ productId }] }) await subbly.cart.load() } else { await subbly.funnels.midPurchaseReject(cartId, step.id) } } }

Post-purchase

Run this loop after subbly.checkout.purchase resolves. Pass the ID of the cart that was paid. The accept needs a signed-in customer, and each accepted offer becomes its own purchase, so the result carries its own invoice, subscription and orders.

When the checkout did not set a shipping address or a shipping method, send shippingAddressId and shippingMethodId in the payload. When the charge asks for 3-D Secure, the call rejects with payment_requires_action; finish the verification with the payment intent methods, then call the accept again with paymentIntentId.

Offer after checkout
async function runPostPurchase(cartId) { while (true) { const { step } = await subbly.funnels.postPurchaseFetch(cartId) if (!step) break const productId = await showOffer(step) if (productId) { const { invoice, subscription, orders } = await subbly.funnels.postPurchaseAccept(cartId, step.id, { offers: [{ productId }] }) showReceipt(invoice, subscription, orders) } else { await subbly.funnels.postPurchaseReject(cartId, step.id) } } } const cartId = subbly.cart.id await subbly.checkout.purchase(cartId) await runPostPurchase(cartId)

Next steps

  • Funnels reference — every method, with the fields of a step and of the accept payloads.
  • Cart reference — subbly.cart.load and the other cart methods.
  • Checkout reference — subbly.checkout.purchase, which the post-purchase phase follows.
Last modified on September 12, 2026
On this page
  • The three phases
  • The loop
  • Pre-purchase
  • Mid-purchase
  • Post-purchase
  • Next steps
Javascript
Javascript
Javascript