SenseCrypt Docs
Guides

Billing and subscriptions

View pricing and plans, manage the free trial and subscription, add and update payment methods, and download invoices for the hosted SenseCrypt service.

Billing is an account-level concern on the hosted SenseCrypt service: the account you created at sign-up is the entity that carries the trial, the subscription, the saved card, and the invoices. This guide is for the account owner (or a delegated admin with billing access) working in the admin console. It covers viewing pricing, understanding the trial and subscription lifecycle, adding and managing payment methods, and downloading invoices.

This applies only to the hosted SenseCrypt service. If you run SenseCrypt yourself (self-hosted), there is no billing surface at all — you manage your own infrastructure and the entire Stripe integration stays dormant. See Self-hosting. The rest of this page assumes the managed, hosted service.

Before you start. Reading the billing surface (subscription, saved cards, invoices) needs the billing:read capability; changing anything (adding or switching a card, closing the account) needs billing:manage. The account owner holds both implicitly. Closing or reopening the account is owner-only — a delegated billing:manage admin can manage payment but cannot delete the account.

How billing works

SenseCrypt bills per account, usage-based, monthly. Usage is metered per tenant and summed across all the tenants your account owns, so one card and one invoice cover everything under the account. Three dimensions are metered per tenant:

  • Workforce seats — one per provisioned workforce seat, a flat $1.00/seat/month by default.
  • External MAU — one per monthly-active external (CIAM) user, a flat $1.00/MAU/month by default, configured independently of the seat rate.
  • KMS keys — a flat monthly fee per active KMS-custody signing key, with no minimum.

Two further resources are account-level and ship with an included allowance rather than a per-unit rate from the first unit:

  • Tenants — three included. Each additional tenant is $10.00/month.
  • Custom domains — three included. Each additional verified domain is $10.00/month; a domain still pending DNS verification serves no traffic and is not billed.

Both allowances are counted across the whole account, not per tenant, so spreading tenants or domains around does not multiply the free quota. While you are inside an allowance no line appears on the invoice at all.

During the free trial you are held to the included three of each. Until a card is on file, creating a fourth tenant or a fourth custom domain is refused with 402 resource_requires_payment — you cannot be billed for the extra, so you cannot create it, and an evaluation can never run up a bill that lands the moment the trial converts. Save a payment method and the limit lifts immediately; the extras then bill as normal. The same applies to an account whose payment has lapsed.

Pricing is flat per user: one uniform rate per dimension, applied to every tenant's line of that dimension. There are no volume bands to cross, so your whole total bills at the rate your account is on. (Volume discounts for larger footprints are arranged with us and applied to that rate.) A workforce-seat minimum is always committed (20 seats by default) — if your account occupies fewer, a top-up line bills the shortfall. External MAU and KMS keys never count toward that seat floor.

Every new account starts on a 30-day free trial with full features and unlimited usage at $0 — no card required to begin, and no trial usage is ever billed. Add a payment method before the trial ends to convert to a paid subscription seamlessly.

Card data never touches SenseCrypt. Cards are collected by Stripe's embedded Payment Element and stored at Stripe against your account's Stripe customer. SenseCrypt only ever sees non-sensitive metadata (brand, last four digits, expiry). There is no Stripe-hosted Checkout and no Customer Portal — card entry and all card management happen in-app.

Viewing pricing and plans

The live rate schedule is public and config-derived — the same numbers power the pre-login pricing page, the in-app rate schedule, and the invoice math. Fetch it any time from GET {tenant-host}/v1/billing/pricing (no authentication), or read it in the console.

Open the admin console and go to Billing.

The Rate schedule panel shows the flat workforce-seat and external-MAU rates, the per-KMS-key fee, the committed seat minimum, and the free-trial length. Each rate is a single number that applies at every volume. Below it, Included with your account tracks the two allowance-based resources, showing how many tenants and verified custom domains you hold against the three included, and what any excess costs.

Screenshot: the Billing page Rate schedule, showing the flat workforce-seat and external-MAU per-user rates, the KMS-key fee, the seat minimum, and the tenant / custom-domain allowance panel.

The trial and subscription lifecycle

Your account is always in exactly one billing state. GET {tenant-host}/v1/admin/billing/subscription is the single source of truth, and the Billing page renders it as a status ribbon:

StatusWhat it meansEnd-user sign-insConsole
trialingOn the free trialWorkFull access
activePaid subscription, card on fileWorkFull access
past_dueTrial lapsed with no card, or a payment ultimately failedKeep workingLogin and pay open; creating or growing anything is blocked
pending_deletionSelf-service deletion requestedBlocked (403)Billing, session, and auth only

The subscription payload also carries a live cost estimate for the open period at your current usage and rates (estimate_lines + estimate_total, in the currency's minor units — cents), plus a per-tenant usage breakdown, so you can see what the next invoice will look like before it is cut.

Screenshot: the Billing page status ribbon (the gold "Free trial" ribbon with days remaining) above the live cost estimate and per-tenant usage breakdown.

Add a card before the trial ends. If the trial lapses with no payment method, the account moves to past_due: end-user OIDC/SAML sign-ins keep working (non-payment never breaks production SSO), but any attempt to create or grow — a new app, user, or key — is refused with 402 payment_required until you add a card. Non-payment has a long grace window before an account is purged, but the create/grow block bites immediately.

When a card is on file at trial end, SenseCrypt charges it immediately for the first period's seats, KMS keys, seat minimum, and any tenant or custom-domain overage (external MAU is billed in arrears, and trial MAU is free). Adding a card during a live trial does not end the trial early — the trial is honored to its end date.

Adding a payment method

Cards are added through Stripe's embedded Payment Element, rendered directly in the Billing page — there is no redirect to a hosted checkout.

On the Billing page, click Add payment method. The console requests a Stripe SetupIntent (POST /v1/admin/billing/setup-intent) and renders the embedded Payment Element in place.

Enter the card details and confirm. Stripe saves the card against your account's customer and makes it the default invoice card. The console then refreshes the subscription so the saved card and any state change (for example, recovering from past_due) appear immediately.

Screenshot: the Add payment method panel with the embedded Stripe Payment Element open in the Billing page.

Indian cards and the RBI e-mandate. For a card issued in India, the same add-card form additionally completes on-session authentication (AFA/3DS) and registers an RBI e-mandate so future invoices can be auto-debited off-session. The saved card shows an "auto-pay on" indicator (mandate_active) once the mandate is active. Cards from every other country are unaffected. A charge above the mandate cap or the RBI AFA threshold needs a one-time on-session confirmation — see the "Confirm your payment" banner below.

Managing saved cards

The Billing page lists every saved card with its brand, last four digits, expiry, default flag, and (for Indian cards) mandate status. From the list you can:

Set a different card as default — the default is the card future invoices are charged to.

Remove a card you no longer want on file.

Screenshot: the saved-cards list showing brand, last four digits, expiry, the default badge, and an "auto-pay on" indicator on a mandated card.

Payment-health banners

While an account is active, three finer signals surface as a banner before any hard suspension, so you can fix the card while everything still works:

  • "Payment failed — update your payment method" — a charge failed but the account is still active. Add or switch to a working card; the next successful charge clears it.
  • "Confirm your payment" — the charge is not a decline but needs on-session authentication (3D Secure / OTP), typically an Indian e-mandate charge over the AFA threshold or mandate cap. Follow the link (Stripe's hosted invoice page) to authenticate; the banner clears once the invoice is paid.
  • "Card expiring soon" / "Card expired" — surfaced from the saved card's expiry before a charge ever fails. Add or replace the card in-app.

Viewing and downloading invoices

Past invoices are listed on the Billing page from GET {tenant-host}/v1/admin/billing/invoices. Each invoice is itemized per tenant (one line per tenant × dimension) plus the seat-minimum top-up line when seats are under the floor.

Open Billing → Invoices (the billing-history table).

For any invoice, open the hosted invoice page or download the PDF — both are Stripe-hosted links (hosted_url / pdf_url) returned with each row.

Screenshot: the Invoices history table listing invoice number, period, total, status, and the PDF / hosted-invoice links.

Closing or reopening the account

Cancelling the subscription is closing the account — there is no separate "downgrade" or "pause". The account owner can self-delete (a soft delete with a 30-day grace window) and reactivate within that window from the same Billing page. Because deletion tears down every tenant the account owns, it is documented separately.

Account deletion is owner-only, requires typing the account email to confirm, and is destructive when the grace window expires. See Account lifecycle and deletion for the full flow, the 423 console block during the grace window, and what the final purge erases.

Using the Management API

Everything above is done in the admin console. The read and payment-management endpoints also live on the tenant Management API at {tenant-host}/v1/admin/billing/*, driven with a machine-to-machine (M2M) access token passed as Authorization: Bearer {mgmtToken}. Reads need a token carrying billing:read; mutations need billing:manage. See Machine-to-machine for how to mint {mgmtToken}.

Billing is fundamentally a console-operator task, and two things have no headless path. (1) Adding a card cannot be completed via curlPOST /setup-intent only bootstraps the flow; the returned client_secret must be confirmed by Stripe.js in a browser (the embedded Payment Element). There is no Stripe-hosted Checkout to redirect to. (2) Closing or reopening the account is owner-onlyPOST /account/delete and POST /account/reactivate require the account owner's authenticated console session and reject M2M service tokens. Also note an M2M token is bound to one tenant, so GET /subscription returns the per-tenant usage breakdown for only that tenant; the account-level status, trial, and payment fields are always complete.

The public pricing endpoint needs no auth:

curl {tenant-host}/v1/billing/pricing
{
  "currency": "usd",
  "trial_days": 30,
  "min_users": 20,
  "kms_key_price_cents": 2000,
  "workforce_cents": 100,
  "external_cents": 100
}

workforce_cents and external_cents are the flat per-user rates in the currency's minor units, charged at every volume. (These are the shipped defaults; the live values come from the operator's configuration.)

Read the subscription, usage, and estimate

GET {tenant-host}/v1/admin/billing/subscription (needs billing:read):

curl {tenant-host}/v1/admin/billing/subscription \
  -H "Authorization: Bearer {mgmtToken}"
{
  "status": "trialing",
  "trial_end": "2026-08-15T00:00:00Z",
  "trial_days_left": 30,
  "current_period_start": null,
  "current_period_end": null,
  "has_payment_method": false,
  "payment_failed_at": null,
  "payment_retry_at": null,
  "payment_action_required_at": null,
  "payment_action_url": null,
  "card_exp_month": null,
  "card_exp_year": null,
  "suspended_at": null,
  "deletion_requested_at": null,
  "purge_date": null,
  "per_tenant": [
    { "tenant_id": "6f9c1e2a-3b4d-4c5e-8a9b-0c1d2e3f4a5b", "name": "Default", "seats": 3, "mau": 120, "kms": 0, "domains": 2 }
  ],
  "totals": { "workforce": 3, "external": 120, "kms": 0, "tenants": 5, "domains": 4 },
  "estimate_lines": [
    { "description": "Default · Workforce seats", "quantity": 3, "unit_amount": 100, "amount": 300 },
    { "description": "Default · External users", "quantity": 120, "unit_amount": 100, "amount": 12000 },
    { "description": "Additional tenants (3 included)", "quantity": 2, "unit_amount": 1000, "amount": 2000 },
    { "description": "Additional custom domains (3 included)", "quantity": 1, "unit_amount": 1000, "amount": 1000 },
    { "description": "Minimum commitment (20 seats)", "quantity": 17, "unit_amount": 100, "amount": 1700 }
  ],
  "estimate_total": 17000
}

All amounts are in the currency's minor units (cents). The example above shows the seat-minimum top-up (3 occupied seats bill 17 extra to reach the 20-seat floor) and the account-level overages: 5 tenants and 4 verified domains against allowances of 3 each, so 2 tenants and 1 domain bill at $10.00.

List, set-default, and remove saved cards

GET {tenant-host}/v1/admin/billing/payment-methods (needs billing:read) returns the saved cards in the standard list envelope:

curl {tenant-host}/v1/admin/billing/payment-methods \
  -H "Authorization: Bearer {mgmtToken}"
{
  "items": [
    {
      "id": "pm_1AbCdEf...",
      "brand": "visa",
      "last4": "4242",
      "exp_month": 12,
      "exp_year": 2030,
      "is_default": true,
      "is_india": false,
      "mandate_active": false
    }
  ],
  "total": 1,
  "limit": 1,
  "offset": 0
}

Make a saved card the default (needs billing:manage; returns 204 No Content):

curl -X POST {tenant-host}/v1/admin/billing/payment-methods/{pm_id}/default \
  -H "Authorization: Bearer {mgmtToken}"

Remove a saved card (needs billing:manage; returns 204 No Content):

curl -X DELETE {tenant-host}/v1/admin/billing/payment-methods/{pm_id} \
  -H "Authorization: Bearer {mgmtToken}"

Both operations verify the card belongs to your account's Stripe customer; an unrecognized {pm_id} returns 404 with no existence leak.

List invoices

GET {tenant-host}/v1/admin/billing/invoices (needs billing:read):

curl {tenant-host}/v1/admin/billing/invoices \
  -H "Authorization: Bearer {mgmtToken}"
{
  "items": [
    {
      "number": "ABCD-0001",
      "created": "2026-08-15T00:00:00Z",
      "period_start": "2026-08-15T00:00:00Z",
      "period_end": "2026-09-15T00:00:00Z",
      "total": 7000,
      "currency": "usd",
      "status": "paid",
      "pdf_url": "https://pay.stripe.com/invoice/.../pdf",
      "hosted_url": "https://invoice.stripe.com/i/..."
    }
  ],
  "total": 1,
  "limit": 1,
  "offset": 0
}

pdf_url and hosted_url are Stripe-hosted per-invoice links; total is in the currency's minor units and status is Stripe's (draft / open / paid / void / uncollectible).

Learn more

On this page