What it creates
Every price uses a stable
lookup_key, so re-running the script never
creates duplicates, it finds existing prices and skips them.
One-shot flow
1
Get a sandbox key
Stripe dashboard → test mode → API keys.Use the Secret key (starts with
sk_test_…) or a Restricted key
(rk_test_…) with these permissions enabled:- Products → Write
- Prices → Write
- Webhook endpoints → Write (only if you pass
--webhook)
2
Drop it into .env.local
STRIPE_API_KEY=rk_test_… is also accepted if that’s the var name
your tooling uses.3
Dry run
4
Apply and write back the price IDs
lookup_key), then
appends or updates the STRIPE_PRICE_* block in .env.local.5
Register the webhook (optional, recommended)
checkout.session.completedcustomer.subscription.deletedcustomer.subscription.pausedinvoice.payment_failed
STRIPE_WEBHOOK_SECRET. On re-runs it’s stored in the dashboard
(Webhooks → endpoint → Signing secret → click to reveal).6
Restart the API to pick up the new env
7
Verify
Open the canvas, click any “Generate” CTA on a workflow that
requires more credits than you have, and the inline buy-credits
sheet should pop with three real Stripe Checkout tiles.
Going to production
The exact same script with a live key:--live flag is REQUIRED for live keys. Without it the script
refuses to run, even in dry mode. There’s no auto-detection, you
explicitly opt in.
Re-running safely
The script is idempotent. Re-running it:- Finds each existing product by name → reuses it
- Finds each existing price by
lookup_key→ reuses it - Finds the existing webhook by URL → updates the subscribed events
- Archive the old price in the Stripe dashboard.
- Pick a new
lookup_key(e.g.lavendly_starter_monthly_v2) inscripts/stripe-setup.mjs. - Re-run with
--apply. - Update the corresponding
STRIPE_PRICE_*env var.
Troubleshooting
Stripe returned 401 Invalid API Key
Stripe returned 401 Invalid API Key
Your key isn’t a Stripe secret/restricted key. Check the prefix,
valid forms are
sk_test_, sk_live_, rk_test_, rk_live_.Stripe returned 403 on /products or /prices
Stripe returned 403 on /products or /prices
Restricted key with insufficient permissions. Edit the key in the
Stripe dashboard and grant Write on Products + Prices (and
Webhook endpoints if you use
--webhook).Refusing to run against a LIVE Stripe key
Refusing to run against a LIVE Stripe key
Pass
--live explicitly. The script never assumes you want to
touch your live account.