Invite-only betaFirst 100 SaaS founders: 3 months freeNo credit card · Zero dark patterns
BoolBul
BOOLBUL.The Retention Interceptor

Integration

Dodo Payments cancellation flow

BoolBul adds an in-app cancellation flow to your Dodo Payments SaaS. It sits on your existing cancel button, asks why the customer wants to leave, shows one reason-matched offer (pause or discount), and keeps a cancel link visible on every screen. If BoolBul doesn't respond within 1.5 seconds it steps aside and your normal cancellation runs. See the quickstart or try the live demo.

What you get

  • Exit survey with reasons that match Dodo Payments' cancellation_feedback values.
  • One reason-matched pause or discount offer per session.
  • Cancel link visible on every screen, single click, WCAG AA contrast.
  • Fails open after 1.5 seconds to your normal cancellation.

How it works with Dodo Payments

  1. The widget opens on your cancel button; BoolBul's edge returns a session and the customer picks an offer.
  2. BoolBul signs an offer token with your signing secret: subscription ID, offer terms, single-use nonce, 5-minute expiry.
  3. Your server verifies the token, checks the subscription belongs to the logged-in user, and only then calls Dodo Payments to apply the pause, discount, or cancellation.

What happens in your Dodo Payments account

ActionDodo Payments API call
Pause N daysMoves next_billing_date N days later (no charge, access kept, nothing to resume). Opt into Dodo's native status: "paused" with pauseStrategy: "status_paused" and call resumeDodoSubscription later.
Discount X% for M monthsCreates a single-use, product-restricted code (subscription_cycles: M) and re-applies the same product via Change Plan with do_not_bill.
Cancelcancel_at_next_billing_date + cancellation_feedback + comment (or immediate).
Dodo has no direct "add discount to subscription" endpoint yet, so the discount path uses Change Plan on the same product. Try it once with a test_mode subscription before going live.

Add it to your app

Paste the script on your settings page and add two server routes. The server adapters guide has the full breakdown.

Cancel button
<script src="https://boolbul.com/v1/boolbul.js" data-public-key="bb_pub_your_key" defer></script>
<script>
  document.getElementById("cancel-subscription").addEventListener("click", () => {
    BoolBul.open({
      publicKey: "bb_pub_your_key",
      customerId: currentUser.customerId,         // your billing customer id
      subscriptionId: currentUser.subscriptionId, // sub_... from Dodo or Stripe
      mrrCents: 4900,                             // optional: powers "saved MRR"

      // Customer took an offer -> apply it on YOUR server
      onOfferAccepted: (offer) =>
        fetch("/api/boolbul/apply", {
          method: "POST",
          headers: { "Content-Type": "application/json" },
          body: JSON.stringify({ offerJwt: offer.offerJwt }),
        }).then((res) => { if (!res.ok) throw new Error("apply failed"); }),

      // Customer chose to cancel (or BoolBul was unreachable for 1.5s)
      onCancel: ({ reasonId, comment }) =>
        fetch("/api/boolbul/cancel", {
          method: "POST",
          headers: { "Content-Type": "application/json" },
          body: JSON.stringify({ reasonId, comment }),
        }).then(() => location.reload()),
    });
  });
</script>
Your server (Dodo Payments)
// app/api/boolbul/apply/route.ts  (Next.js; same idea in Express, Hono, Remix...)
import { redeemOffer } from "@boolbul/sdk/server";

const gateway = { gateway: "dodo_payments", apiKey: process.env.DODO_PAYMENTS_API_KEY!, environment: "live_mode" } as const;

export async function POST(req: Request) {
  const user = await requireUser(req); // YOUR auth: never trust ids from the body
  const { offerJwt } = await req.json();

  await redeemOffer(offerJwt, {
    signingSecret: process.env.BOOLBUL_SIGNING_SECRET!, // Dashboard -> Settings
    expected: { subscriptionId: user.subscriptionId },
    // Optional replay guard: return true if this nonce was already used
    seenNonce: async (nonce) => !(await redis.set(`boolbul:${nonce}`, 1, { nx: true, ex: 300 })),
    gateway,
  });
  return Response.json({ ok: true });
}

// app/api/boolbul/cancel/route.ts
import { cancelSubscription } from "@boolbul/sdk/server";

export async function POST(req: Request) {
  const user = await requireUser(req);
  const { reasonId, comment } = await req.json();
  // Cancels at period end and records the exit reason in your gateway's churn analytics
  await cancelSubscription(user.subscriptionId, gateway, { reasonId, comment });
  return Response.json({ ok: true });
}

Your Dodo Payments API key stays on your server

BoolBul never sees your gateway keys. The edge signs a short-lived JWT with your signing secret; your server verifies the signature, confirms the subscription belongs to the current user, and only then calls Dodo Payments. The token cannot be forged or enlarged — changing any byte breaks the signature.

FAQ

Does BoolBul replace my Dodo Payments cancel button?

No. You keep your existing cancel button. BoolBul opens a modal on top of your app when the customer clicks it. If BoolBul is slow or down, it steps aside after 1.5 seconds and your normal cancellation runs.

What happens when a customer accepts a pause offer?

BoolBul returns a signed token. Your server verifies it and calls Dodo Payments to move the next billing date forward (or set the subscription status to "paused"). The customer keeps access and is not billed during the pause.

How does the discount work on Dodo Payments?

Dodo has no "add discount to subscription" endpoint, so BoolBul creates a single-use discount code restricted to the product and re-applies the same product via Change Plan with do_not_bill. Test it once with a test_mode subscription before going live.

Can I customize the exit survey reasons?

You can turn reasons on or off and change their labels in the customizer. The value behind each reason stays the same, and it is what your server sends to Dodo Payments as cancellation_feedback.

Add a cancel flow to your Dodo Payments SaaS

First 100 SaaS founders get BoolBul Pro free for 3 months — no credit card.