Skip to main content
The Billing endpoints manage subscriptions, surface invoices, and let you set budget caps to prevent runaway costs.

Plan reference

PlanMonthlyMonthly FCUAnnual priceFeatures
Free$010KNo credit card, evaluate WAVIS
PAYG$0 baseUnlimited$2.00/1M FCU overage
Starter$91M$7.47/moSDK GPU acceleration (BYO GPU)
Pro$4920M$40.67/moPriority compute queue, webhook alerts, batch discount
Scale$199100M$165.17/moDedicated compute allocation, 99.9% SLA
EnterpriseCustomUnlimitedCustomSLA, audit logs, self-hosted GPU option, on-prem
Annual billing is 17% off the monthly rate.

FCU weights

OperationFCU
add, negate, rescale, add-plain1
multiply-plain2
multiply, rotate, key_switch5
poly-eval10
matmul20
bootstrap50
infer_7b100
infer_70b1000
PAYG overage rate: **2.00per1MFCU(2.00 per 1M FCU** (0.000002/FCU).

POST /api/v1/billing/subscribe

Subscribe to a plan or change plans. Auth: BILLING+ (account_admin)

Request

POST /api/v1/billing/subscribe
{
  "plan": "pro",
  "email": "billing@example.com",
  "billing_cycle": "monthly"
}
FieldTypeRequiredNotes
planstringYes"starter", "pro", "scale", "enterprise"
emailstringYesBilling contact (Stripe customer email)
billing_cyclestringNo"monthly" (default) or "annual" (17% off)

Response — 200 OK

{
  "plan": "pro",
  "status": "active",
  "current_period_end": "2026-05-28T12:00:00Z",
  "monthly_price_usd": 49.00,
  "monthly_fcu_limit": 20000000,
  "billing_cycle": "monthly",
  "annual_discount_pct": 17
}
If you don’t have a payment method on file, the response includes a checkout_url (Stripe Checkout) instead of status: "active".

GET /api/v1/billing/subscription

Get the current subscription. Auth: BILLING+

Response — 200 OK

{
  "plan": "pro",
  "status": "active",
  "current_period_end": "2026-05-28T12:00:00Z",
  "monthly_price_usd": 49.00,
  "monthly_fcu_limit": 20000000,
  "billing_cycle": "monthly",
  "annual_discount_pct": 17
}
status values:
  • "active" — paid, in good standing
  • "trial" — trial period, no charge yet
  • "cancelled" — cancelled but still in paid period
  • "past_due" — payment failed, grace period
  • "unpaid" — past grace, account suspended

DELETE /api/v1/billing/subscription

Cancel the subscription. Service continues until the end of the current billing period. Auth: BILLING+

Response — 200 OK

{ "status": "cancelled" }
To reactivate before the period ends, call /billing/subscribe again with the same plan.

GET /api/v1/billing/invoices

List past invoices. Auth: BILLING+

Response — 200 OK

{
  "invoices": [
    {
      "invoice_id": "in_a1b2c3",
      "amount_usd_cents": 4900,
      "status": "paid",
      "period": "2026-04",
      "paid_at": "2026-04-01T08:00:00Z",
      "stripe_invoice_url": "https://invoice.stripe.com/..."
    }
  ]
}
status: "paid", "open", "void", "uncollectible".

POST /api/v1/billing/budget

Set a monthly budget cap. Operations exceeding the cap are either rejected (hard cap) or alerted on (soft cap). Auth: BILLING+

Request

POST /api/v1/billing/budget
{
  "monthly_cap_usd": 500.00,
  "alert_email": "ops@example.com",
  "hard_cap": true,
  "allow_soft_overage": false
}
FieldTypeRequiredNotes
monthly_cap_usdfloatYes>0
alert_emailstringNoWhere to send 80%/100% alerts
hard_capboolNoIf true, block ops once cap reached. Default false
allow_soft_overageboolNoIf false, also block when soft cap is reached. Default true

Response — 200 OK

{
  "monthly_cap_usd": 500.00,
  "current_spend_usd": 84.50,
  "alert_email": "ops@example.com",
  "hard_cap": true,
  "alerts_sent": ["50%"],
  "next_alert_threshold_pct": 80
}

GET /api/v1/billing/budget

Get current budget settings and spend. Auth: BILLING+

Response — 200 OK

Same shape as POST response.

GET /api/v1/billing/usage

Current cycle usage breakdown. Requires Clerk JWT (dashboard session token), not a WAVIS API key — used by the dashboard’s billing page. Auth: Clerk JWT

Response — 200 OK

{
  "period_start": "2026-04-01T00:00:00Z",
  "period_end": "2026-05-01T00:00:00Z",
  "monthly_fcu_limit": 20000000,
  "monthly_fcu_used": 14300000,
  "operations_total": 152300,
  "by_operation": {
    "add": 50000,
    "multiply": 30000,
    "matmul": 800,
    "bootstrap": 50
  },
  "estimated_cost_usd": 0.00,
  "overage_fcu": 0,
  "overage_cost_usd": 0.00
}

POST /api/v1/billing/portal

Get a one-time URL to the Stripe Customer Portal (manage payment method, update billing details, download receipts). Auth: Clerk JWT

Request

POST /api/v1/billing/portal
{
  "return_url": "https://dashboard.wavis.xyz/billing"
}
return_url must match an allow-list configured server-side (currently *.wavis.xyz and localhost).

Response — 200 OK

{
  "url": "https://billing.stripe.com/p/session/..."
}
The URL is valid for 5 minutes.

POST /api/v1/billing/checkout

Create a Stripe Checkout Session — the easiest way to onboard a new subscriber, supports all payment methods Stripe enables for your account. Auth: Clerk JWT

Request

POST /api/v1/billing/checkout
{
  "plan": "pro",
  "billing_cycle": "annual"
}

Response — 200 OK

{
  "session_id": "cs_test_a1b2c3...",
  "client_secret": "..."
}
Use client_secret with Stripe’s embedded checkout in the browser.

POST /api/v1/billing/stripe_webhook

Stripe → WAVIS event ingestion. Not for direct customer use. Stripe POSTs subscription/payment events here, signed with the Stripe webhook secret. WAVIS verifies the Stripe-Signature header before processing. Returns 200 OK on success. You don’t call this endpoint — Stripe does, when you configure api.wavis.xyz/api/v1/billing/stripe_webhook in your Stripe Dashboard (automatic for managed Enterprise tenants).

Common errors

CodeStatusMeaning
INVALID_PLAN400plan not recognized
BUDGET_HARD_CAP_HIT402Operation rejected, monthly cap reached
NO_PAYMENT_METHOD402Subscribe attempted without saved card; use /checkout
STRIPE_UNAVAILABLE502Upstream Stripe API error; retry idempotent
RETURN_URL_NOT_ALLOWED400return_url not in allow-list
BILLING_PORTAL_DISABLED403Portal disabled for this tenant (Enterprise managed)

Lifecycle examples

Upgrade plan mid-month

curl -X POST https://api.wavis.xyz/api/v1/billing/subscribe \
  -H "Authorization: Bearer wvs_live_<admin_key>" \
  -d '{"plan": "scale", "email": "ops@x.com", "billing_cycle": "annual"}'
Stripe prorates the upgrade automatically — you’re credited for the unused portion of the old plan and charged for the rest of the new plan.

Set a hard budget cap of $1000/month

curl -X POST https://api.wavis.xyz/api/v1/billing/budget \
  -H "Authorization: Bearer wvs_live_<admin_key>" \
  -d '{"monthly_cap_usd": 1000, "hard_cap": true, "alert_email": "ops@x.com"}'
Operations are blocked with 402 BUDGET_HARD_CAP_HIT once spend hits $1000 that month. Resets at UTC midnight on the month boundary.

Subscribe to billing webhooks

curl -X POST https://api.wavis.xyz/api/v1/webhooks \
  -H "Authorization: Bearer wvs_live_<admin_key>" \
  -d '{
    "url": "https://your-app.com/billing-events",
    "events": ["invoice_created", "invoice_paid", "invoice_failed", "budget_alert"],
    "secret": "min-32-char-shared-secret-..."
  }'
See Webhooks for verification.

Best practices

  1. Always set a budget cap. Even with metered PAYG, cap your monthly exposure. A leaked key cannot exceed the cap.
  2. Use wvs_test_* keys in CI. Test keys don’t trigger Stripe charges, but they consume quota — keep an eye on it.
  3. Subscribe to invoice_failed. Don’t let a card decline silently suspend your account.
  4. Use annual billing for predictable workloads. 17% discount.
  5. Negotiate Enterprise early. Custom SLA, dedicated allocation, and on-prem deployment options are negotiable for >$5K/month commitments.

Next Steps

Webhooks

Async events for billing notifications

Authentication

API key roles and rotation