DevStride runs its own billing: it decides who is on trial, who pays, what they pay and who is locked. Stripe keeps the cards, processes the payments and stores the payment history. The ds stripe CLI (cli/commands/stripe.ts) wraps two commands: a read-only inventory of what Stripe holds for every customer, and test-mode proofs of the Stripe behaviour DevStride's billing relies on. It is a thin operational tool, not a sync engine — each command does one specific thing, and neither runs on a schedule.
BILLING_AUTO_CHARGE) and trial expiry (BILLING_TRIAL_EXPIRY) are on on every stage, production included; staff can also charge one customer from the staff Billing page.charge.refunded, charge.refund.updated and refund.updated (Stripe sends the last one for every refund, so subscribe to it). Anything else Stripe sends is logged and acknowledged.Commands this page used to document are gone: add-products (the products it created were the old plan cards), create-customers and find-subscription-quantity.
ds stripe goes through the standard AWS SSO gate before running. Each command then calls the Stripe API directly with its own key, named below; the stage's own STRIPE_SECRET_KEY lives in .env — written by ds secrets pull, or filled in by hand before the secrets bundles exist — and is pushed up with ds script set-config — see Getting Started.ds -b stripe billing-inventory --out <directory outside the repository>
Exports, read-only, everything Stripe holds for every customer beside DevStride's own view of the same customer — the census the billing migration is planned from, and the check run again before the old subscription code is deleted. Every Stripe call it makes is a list; it changes nothing, in Stripe or in DevStride.
It writes one row per billable organization to inventory.csv, plus the raw records it read as JSON Lines: organizations.jsonl, subscriptions.jsonl, customers.jsonl, unsettled-invoices.jsonl, pending-invoice-items.jsonl and catalogue.jsonl.
inventory.csv puts DevStride's stored status, seats and price beside Stripe's, compares seat counts three ways (stored, recomputed, Stripe), and carries renewal and trial dates, hand-extended trials, paused collection, the latest invoice status, scheduled changes in words, whichever payment method Stripe would actually charge, the invoice email, and a note column naming anything a person needs to look at.
STRIPE_INVENTORY_KEY — an agent credential, kept in the agent secrets bundle and written by ds secrets pull to ~/.config/devstride/agent.env, never into .env (a key exported in your shell wins) — and that must be a restricted key (rk_…) with read access only — a full sk_… secret key is rejected. --out must also resolve outside this repository: the files hold customer names, invoice emails and negotiated prices, and the repo has no ignore rule for them. Do not work around either refusal.The files are written readable by you alone, and the command will not follow a symlink or write into a directory that resolves inside the repository. In inventory.csv, any name or email beginning with =, +, - or @ carries a leading apostrophe so a spreadsheet cannot execute it as a formula.
When to run:
ds -b stripe prove-invoice-flow
Runs Stripe test-mode proofs of the behaviour DevStride's billing relies on, on the pinned Stripe SDK and API version rather than assumed, and prints PASS or FAIL for each claim. The groups cover direct card charges made through DevStride's own charging code (an off-session charge of the saved card, one payment per idempotency key, a decline read as the customer's to fix, finding a payment again by its metadata, and the receipt Billing History shows), staff refunds (a refund request made once however often it is replayed, the payment read back with its refund and the request that made it, and Stripe refusing more than is left), plus one-off invoices and subscription cancellation. The invoice groups document the design that direct card charging replaced and are kept as the record of what was measured.
It needs STRIPE_PROOF_KEY, a test-mode key, and refuses a live key outright. It is not kept in any secrets bundle, because the command writes; the STRIPE_SECRET_KEY already in your .env is a test-mode key, so STRIPE_PROOF_KEY="$STRIPE_SECRET_KEY" is enough. The customers it creates are deleted and its product and price archived at the end, even when a proof fails; a clean-up that fails makes the run fail and names what was left behind.
When to run: before relying on one of these Stripe behaviours in a change, or after a Stripe SDK or API version bump.
ds script one-off and maintenance commandsSTRIPE_SECRET_KEY and other .env values, and the webhook events to subscribe toAPI Development
Generating the typed API client SDK, OpenAPI documentation, and MCP server SDK from backend Hono routes.
Maintenance & Codebase Checks
Codebase wiring audits, event-system delivery and topology checks, data cleanup and repair scripts, operational toggles, and historical one-off migrations available via ds script.