CallwiseDocs

Billing, terms and lapse

The free week, the 12-month term, cancelling the renewal, no refunds, which modules a workspace has, and what happens when a payment fails.

A workspace subscribes to one base, a plan or an industry pack, plus add-ons. Together they decide which modules it has. Each module then has a billing state, written only by billing (the service role), never by a person in the app.

The free week

Every purchase starts with a 7-day free trial, one per customer. The dates come from trialSchedule(purchaseDay) in @callwise/pricing:

  • At checkout the customer enters payment details and ticks the consent box. A payment method is saved and $0 is charged today. Checkout shows Today: $0 · First charge on <date> and the amount.
  • Day 7 (the day after the last free day): the annual total is charged once, and the 12-month term starts that day. A purchase on 16 Oct 2026 is free 16–22 Oct, charged on 23 Oct 2026, and its term runs 23 Oct 2026 to 22 Oct 2027, renewing 23 Oct 2027.
  • Cancelling during the trial costs nothing. On the site this is Cancel a trial (/account/trial, order number + email), which calls cancelTrial() in @callwise/store: allowed only while the order is trialing and before the first charge day; it voids the saved method and charges nothing. After the first charge the term is non-refundable and only the renewal can be cancelled.
  • One trial per customer, counted by normalized email across stored orders (lowercase, trimmed, +tags dropped, Gmail dots ignored). A second purchase has no trial: it is charged at checkout and its term starts that day (termFrom(today)), and checkout says "Your free trial was used on an earlier order: charged today".

Order statuses in @callwise/store: trialing (method saved, nothing charged) → active (first charge taken), or trialing → trial_cancelled (nothing charged, ever). Each order stores trial: { start, end, firstChargeOn }, amountToday, firstCharge: { amount, on, idempotencyKey, status } and the term dates.

The day-7 charge job

runDueTrialCharges(now) charges every trialing order whose first charge day is today or earlier, exactly once, and moves it to active. Cancelled trials are skipped; orders whose day 7 is still ahead are left alone. It is idempotent: a charged order is no longer trialing, and every charge carries one idempotency key per order (first-charge:<orderId>), so running it twice, or after a crash between the charge and the save, never charges twice. A failed charge stays trialing and is retried on the next run.

pnpm --filter @callwise/store charge-due                          # against apps/site/.data/orders.json
pnpm --filter @callwise/store charge-due -- --now 2026-10-23      # pretend it is day 7

Payment providers

The PaymentProvider interface has setupPaymentMethod (saves a method, $0), charge (charge now, for a purchase without a trial), chargeFirst(order) (the day-7 charge) and voidOnCancel(order). It has no refund call. This build ships only the demo provider: the checkout's demo card form (4242 4242 4242 4242) keeps only the brand, last 4 digits and expiry, never the full number or the CVC, and no money moves.

Not built and not verified (verified: false in the code):

  • Stripe: a subscription with trial_period_days=7 on a method saved with a SetupIntent; card fields would be Stripe Elements, so card numbers never reach our server.
  • Banks without recurring billing: a 3D Secure pre-authorisation for the annual total at checkout, captured on day 7 by the scheduled job and voided on cancel.

The term

  • Every plan, pack and add-on is a 12-month minimum contract, billed annually in advance, starting after the free week (or at checkout for a customer who already had one). There is no month-to-month option.
  • Prices are shown per month: list price minus 12%. The charge for the term is twelve of those, taken up front. See Pricing.
  • termFrom(start) in @callwise/pricing gives the dates: the term ends the day before the first anniversary, and renews on the anniversary. A term starting 16 Oct 2026 ends 15 Oct 2027 and renews 16 Oct 2027. Month ends are clamped (31 Jan → renews 31 Jan, ends 30 Jan).
  • cw_subscriptions stores term_start, term_end and renewal_cancelled_at.

Cancelling the renewal

Cancelling means cancelling the renewal, nothing else. An owner does it from Settings → Billing → Cancel renewal. The action:

  • sets renewal_cancelled_at (the renewal date becomes empty),
  • keeps every module active until term_end,
  • charges and refunds nothing.

After term_end a cancelled workspace is meant to become read-only, the same as a lapsed one below. In this build nothing flips the status on that date yet (no scheduled billing job); the rule is written down so the job can follow it.

No refunds

Once the first annual charge is taken, payments are non-refundable (cancelling during the free week costs nothing, so there is nothing to refund): no full or partial refunds for unused months, unused credits, add-ons no longer used, or moving to a smaller plan (which takes effect at the next renewal). The code has no refund path: @callwise/pricing exports REFUNDS = 'none' and no refund function, @callwise/store and its PaymentProvider have no refund function at all, and orders carry refund: 'never'.

The site's checkout has a required checkbox that starts unticked. Its sentence names the free week, the first charge date and amount, that cancelling during the trial costs nothing, the 12-month minimum starting after the trial, annual billing in advance, automatic renewal and no refunds, and links the Terms and the Cancellation & refund policy. The sentence is built per order by consentText(offer); for a returning customer it says the trial was used and the charge is today. The server recomputes the offer, refuses an order without the box or with a different sentence, and stores on the order:

{ "accepted": true, "at": "2026-10-16T09:30:00.000Z", "termsVersion": "2026-10-16", "text": "I agree to the Terms and …" }

The terms version in this build is 2026-10-16b. An order placed from a page that showed older text or an older version is refused.

Billing states

StateComes fromPeople can
lockedModule is not in the plan, pack or an add-onNothing. The page shows which plan unlocks it.
activePaid, or in the free weekEverything their access level allows
gracePayment failed recentlyEverything, while payment is fixed
read_onlyPayment failed a while ago, or the term ended without renewalView only. No edits, no publishing.

An owner can also switch an active module off in Settings, which hides it for everyone. Switching a locked module on is refused by the database.

When a payment fails

The numbers below come from LAPSE_RULES in @callwise/pricing.

Days after a failed paymentStatusModule stateWhat happens
0activeactiveFull access.
1–7past_duegraceFull access while the payment is fixed.
8–30read_onlyread_onlyData visible, no edits. Calls forward to the fallback number.
after 30lapsedread_onlyStill read-only. Data is kept for 90 days.

While the workspace is read-only, calls are not dropped: they forward to the fallback number set in Settings → Phone numbers. Data is kept for 90 days after a lapse or after a term ends without renewal, so it can be exported.

Credits

Each plan and pack has one shared pool of credits per month, used by every module and every AI model (see Pricing and AI models and credits). Calls use credits at the rate of the models their agent uses. Credits do not roll over to the next month. Owners get an alert at 80% and at 100%. Past 100%, calls forward to the fallback numbers unless the workspace has opted in to overage at $0.03 a credit.

Who can change the plan

Nobody from inside the app. Plan, status, term and the pool allowance live in cw_subscriptions, which signed-in users can read but not write; the only billing write an owner can make is cancelling the renewal. In this build there is no real payment: the site's store records demo orders, in demo mode a plan change is simulated, and in local database mode it is written with the service role.

On this page