| name | stripe-cli |
| description | Stdlib-only Python CLI for managing Calyflow's Stripe catalog (products, prices, webhooks) with a mandatory account guard. Use when provisioning or verifying Stripe products/prices/webhook endpoints for Calyflow, or when setting the STRIPE_PRICE_* env vars for a new Stripe account. |
Calyflow Stripe CLI
A zero-dependency (Python 3.14 stdlib only) CLI in .agents/stripe-cli/ for
idempotent management of Calyflow's Stripe billing objects. Everything JSON
goes to stdout; human-readable text goes to stderr, so output is safe to pipe.
When to use
- Provisioning a fresh Stripe account with the Calyflow subscription catalog
(2 products, 4 monthly prices) — use the Calyflow setup script below.
- Verifying that an account's catalog matches what the app expects
(run the setup script with
--dry-run).
- One-off inspection or upserts of products, prices, or webhook endpoints
without installing the official Stripe CLI or SDK.
- Rotating a price amount: prices are immutable, so the tooling creates a
replacement price and deactivates the old one for you.
Env vars
| Variable | Required | Meaning |
|---|
STRIPE_API_KEY | yes | Secret or restricted key (sk_test_ / sk_live_ / rk_...). Never echo or commit it. |
EXPECT_STRIPE_ACCOUNT | recommended | Account id (acct_...) that mutating commands must match. Without it (or --expect-account), every mutating command refuses to run. |
Minimum grants for a restricted key (per references/stripe-api.md):
Products: Write (covers prices), Webhook Endpoints: Write,
Customer portal: Write, and All core resources: Read (needed for
GET /v1/account, which the guard calls).
Commands
Run every command from the CLI directory:
cd .agents/stripe-cli
Show the connected account id and key mode (no guard, read-only):
python3 -m cli account
List active products (add --inactive for inactive ones):
python3 -m cli products list
Upsert a product identified by its metadata (guarded):
python3 -m cli products upsert \
--name "Calyflow App — Individual" \
--metadata tier=individual --metadata managed_by=calyflow-cli \
--description "Individual subscription"
Upsert a price by lookup key (guarded; amounts are MINOR units — €39 = 3900):
python3 -m cli prices upsert \
--product prod_XXXXXXXX \
--lookup-key calyflow_individual_monthly_launch \
--unit-amount 3900 --currency eur --interval month \
--nickname "Individual monthly — launch €39" \
--metadata plan=calyflow_app_monthly \
--tax-behavior exclusive
Ensure a webhook endpoint exists with the right events (guarded; JSON output
includes "created" — the secret field only appears when it was created):
python3 -m cli webhooks ensure \
--url https://app.calyflow.com/api/stripe/webhook \
--event customer.subscription.updated \
--event invoice.payment_failed \
--description "Calyflow app webhook"
The global --expect-account acct_... flag (fallback: EXPECT_STRIPE_ACCOUNT
env var) sets the account guard for the mutating subcommands. Exit codes:
0 success, 1 Stripe/account-mismatch error, 2 usage or config error.
Calyflow setup
integrations/setup_calyflow_products.py provisions the full Calyflow
catalog idempotently. It is hard-wired to expect account
acct_1TrfTsFtG7wXVl1J and refuses to touch anything else.
Always dry-run first (reads live state, prints the plan to stderr, performs
zero mutations):
cd .agents/stripe-cli
python3 integrations/setup_calyflow_products.py --dry-run
Then apply. stdout is exactly four NAME=price_... lines, ready to paste
into the app's environment:
python3 integrations/setup_calyflow_products.py
Re-running is safe: a fully provisioned account issues zero writes and just
re-prints the same four lines.
| Stripe lookup_key | App env var |
|---|
calyflow_individual_monthly_launch | STRIPE_PRICE_INDIVIDUAL_MONTHLY |
calyflow_individual_monthly_standard | STRIPE_PRICE_INDIVIDUAL_STANDARD_MONTHLY |
calyflow_agency_monthly_launch | STRIPE_PRICE_AGENCY_MONTHLY |
calyflow_agency_monthly_standard | STRIPE_PRICE_AGENCY_STANDARD_MONTHLY |
Testing
cd .agents/stripe-cli
python3 -m unittest discover
The suite is fully offline — every Stripe interaction is faked, and no test
ever opens a network connection. Run it before and after any change here.
Safety rules
- Account guard is mandatory. Every mutating command asserts
GET /v1/account matches the expected account (--expect-account /
EXPECT_STRIPE_ACCOUNT) before any write. The Calyflow account is
acct_1TrfTsFtG7wXVl1J. Never bypass the guard.
- Check key mode. The guard line printed to stderr shows
mode=test or mode=live — confirm it is the mode you intend before a
real run.
- Never commit or echo API keys. Pass them only via the
STRIPE_API_KEY env var; keep them out of shell history, logs, and git.
- Never disable TLS verification.
- Prices are immutable in amount/currency/interval/product. To change an
amount, create a new price with
transfer_lookup_key=true and deactivate
the old one — the tooling does exactly this; never try to edit in place.
- Webhook endpoints are updated in place, never re-created. Deleting and
re-creating an endpoint mints a new
whsec_ signing secret and silently
breaks the deployed app until its secret is rotated.