Troubleshooting
Billing — Troubleshooting
The most common failures, and the one-line fix.
400 "No workspace selected"
Every user billing endpoint needs an active workspace on the user. Select a
workspace (so the JWT/user carries workspaceId) before calling billing. See
Getting started.
402 on a write that isn't a metered action
The workspace is read-only — its subscription is past_due/suspended
(read_only_since set). Writes are blocked by WorkspaceReadOnlyGuard; reads and
billing/payment endpoints stay open. Fix: pay via POST /checkout to
recover; the webhook lifts read-only.
402 PLAN_LIMIT_EXCEEDED
A metered resource (workflow_executions, ai_credits, storage) hit its
effective limit. The body carries { metric, limit, used }. Fix: upgrade the
plan or buy an add-on / one-time pack, then retry. Counters are keyed by month, so
they reset on the 1st (UTC); packs top up the balance immediately.
/change-plan rejected with PAYMENT_METHOD_REQUIRED
You tried to change the plan on a lapsed/canceled subscription
(past_due/suspended/canceled). A paid plan is never handed out for free
outside the trial. Fix: add a card via POST /checkout first (which
reactivates), then change the plan.
/change-plan didn't return a redirect
By design — POST /change-plan never opens Paymob. Only POST /checkout
returns a redirect_url. A trial swap and a downgrade apply immediately; an active
upgrade returns charged:true and applies on the webhook (poll /usage).
The charge "succeeded" but the plan/state didn't change
The Paymob redirect and the immediate HTTP response are not proof of success. The
real state lands on the async webhook. Fix: confirm with
GET /payment-status?payment_id=… and poll GET /usage for a few seconds until it
reflects the change.
Subscribing to Enterprise returns 400
Enterprise is provisioned by the team, not self-serve — 400 "The Enterprise plan is set up by our team. Please contact support." Choose a shared-tier plan or
contact sales.
Add-on purchase returns 400
Add-ons need an active subscription with a saved card
(status:"active" and has_payment_method:true). Check /usage and gate the
buy button on those two fields.
Paymob webhook returns 401
The HMAC signature did not verify. Confirm PAYMOB_HMAC_SECRET matches the Paymob
merchant account and that Paymob posts to /api/v1/webhooks/paymob with the raw body
intact. See Webhooks.
The usage bar doesn't update live
usage:updated rides the /data/events socket (records module), not a billing
namespace. Confirm the socket is connected with the user JWT. It covers only
ai_credits / workflow_executions — plan/card changes still need /usage
polling. See Events.
More error codes: Errors.