Stripe for serial founders β one Organization, one account per project
If you run (or plan to run) several projects, don't cram them all into one Stripe account. Stripe Organizations gives you an umbrella: one login, unified team management, and up to 75 sibling accounts β one per project β each with its own clean books, its own webhooks, its own keys. Creating a new project account takes about a minute because you can copy the legal entity, business details, and payout bank account from an existing account in the org β no re-doing KYC for every side project.
This guide is the exact flow, plus how much of it an AI agent can drive (spoiler: everything except ~5 dashboard clicks and one key handoff).
Create the org account for a new project (human, ~1 minute)
This part is dashboard-only (there's no API for creating org business accounts):
- Log in to the Stripe Dashboard.
- Top-left account dropdown β
[ + Create account ]. - Choose "Create an account in your organization" (not "Create a separate account").
- Name the account, pick the country of operation, and select the existing account to copy business data and bank details from.
- Create β the dashboard switches to the new account and a short onboarding follows (display name, what the business does, which products to activate).
Check the country before you configure anything. An account's country is immutable after creation β verify it immediately (stripe get /account β country). If it's wrong (e.g. it defaulted to US but your legal entity is elsewhere), close the account and recreate it now, before wiring anything to it.
At the end of onboarding, Stripe now even offers a "set up with an AI agent" path with a copy-paste integration prompt (MCP server, plugin, implementation planner). If you're on RailsFast you can skip the integration-planner part β the pay gem is your integration, pre-wired. What your agent actually needs is below.
What the agent does next (everything else)
After the human runs stripe login once for the new account (browser step), the CLI holds all four keys β test + live, publishable + restricted β in ~/.config/stripe/config.toml (chmod 600). An agent should read them from that file, never echo them.
Verified capability map, per mode:
| Need | How | Agent-executable? |
|---|---|---|
Test pk/rk pair (development creds) |
~/.config/stripe/config.toml after stripe login |
β fully |
| Development webhook signing secret | stripe listen --print-secret |
β fully |
Sandbox webhook endpoint (+ its whsec_) |
stripe webhook_endpoints create --url https://yourdomain.com/pay/webhooks/stripe ... β the response includes the signing secret |
β fully |
| Live publishable key | same config file | β fully |
| Live secret key for the app | Dashboard: create a restricted key β clipboard handoff | π§ one human step |
Live webhook endpoint (+ its whsec_) |
STRIPE_API_KEY=<that restricted key> stripe webhook_endpoints create --live ... |
β after the handoff |
(That table is the single-account path. Running an Organization? The org key below replaces the per-project restricted-key handoff for everything except the app's own runtime key β and can even replace that, trade-offs noted.)
Two sharp edges the agent must know:
- The CLI's own keys expire in 90 days and its live key is deliberately permission-limited (live writes like webhook creation get rejected). They're perfect as development credentials and useless as production app credentials β that's by design, not a bug. The app's production key must be a dashboard-created restricted key β see the exact permission list below.
- A live account isn't chargeable until onboarding completes β gate on
stripe get /accountβcharges_enabledbefore believing any live-mode test.
The exact restricted-key permissions a RailsFast app needs
Derived from the actual Stripe API surface of pay v11 (every Stripe::* call in the gem), so nothing is over-granted β and verified live (webhook creation, customer round trips, and deliberate permission-gap probes against a real org key).
The "Select permissions" screen groups permissions by resource group, and each row is None / Read / Read and write. Walk it top to bottom β everything not listed below stays on None:
Core β set Read and write for:
- Charges and Refunds
- Customer Sessions (pay's embedded pricing/payment elements)
- Customers
- Payment Intents
- Payment Methods
- Products
- Setup Intents
Billing β set Read and write for:
- Billing Meter Events (only matters for usage-based billing β harmless to grant)
- Credit Notes
- Customer Portal
- Invoices
- Prices (pay and pricing_plans call
Price.retrieve; write lets an agent create prices for your plans) - Subscriptions
- Tax Rates
Checkout Sessions β the group has a single permission: set it to Read and write.
Webhook Endpoints β single permission ("Webhook Endpoints, Event Destinations"): set Read and write. This is what lets the agent create the production webhook with this same key and capture its whsec_ signing secret. Drop it back to None afterwards if you like.
Leave every other group (Connect included β pay only uses it for marketplace features RailsFast doesn't enable) on None. Using Stripe Tax? automatic_tax on Checkout works under the permissions above once your tax registrations are configured in the dashboard.
Under-granted? Stripe's error messages name the exact missing permission β e.g. "Enabling "Prices Read" ('plan_read') permissions on this key would allow this request to continue." An agent hitting Permission denied should read the error, report the named permission, and ask the human to add it β no guessing.
The same screen (and the same list) applies to both a per-account restricted key and an organization API key β see below for which to create.
Creating your products and prices
Credentials alone don't give you anything to sell. RailsFast plans are defined in code (config/initializers/pricing_plans.rb), and each paid plan maps to a Stripe Product with recurring Prices. Creating those is fully agent-executable under the permission list above:
# Sandbox first (CLI account keys):
stripe products create --name "Pro"
stripe prices create -d "product=prod_XXX" -d "currency=usd" \
-d "unit_amount=2900" -d "recurring[interval]=month"
stripe prices create -d "product=prod_XXX" -d "currency=usd" \
-d "unit_amount=29000" -d "recurring[interval]=year"
# Live (org key β same calls with the context headers):
curl -s https://api.stripe.com/v1/products \
-u "$(cat ~/.config/railsfast/stripe_org_key):" \
-H "Stripe-Context: acct_XXXXXXXX" -H "Stripe-Version: 2026-06-24.dahlia" \
-d name="Pro"
Then wire the returned price_β¦ IDs into config/railsfast/railsfast.yml:
stripe:
plans:
pro:
monthly: price_live_xxx
yearly: price_live_yyy
β¦and in config/initializers/pricing_plans.rb, swap each plan's hardcoded price 29 for the stripe_price month: β¦, year: β¦ line that ships commented out right below it. Sandbox and live have different price IDs β railsfast.yml supports per-environment values, so wire both.
The power move: one Organization API key (verified)
If you plan to churn many projects, create a single organization API key instead of a per-account key each time: org dashboard β org API keys β create a restricted key with exactly the permission list above. It's sk_org_β¦-prefixed, centrally revocable, and works on any account in the org β every request just carries two extra headers naming the target:
curl https://api.stripe.com/v1/webhook_endpoints \
-u "$(cat ~/.config/railsfast/stripe_org_key):" \
-H "Stripe-Context: acct_XXXXXXXX" \
-H "Stripe-Version: 2026-06-24.dahlia" \
-d url="https://myproject.com/pay/webhooks/stripe" \
-d "enabled_events[]=customer.subscription.created" ...
All of this is verified working with a real org key: live webhook creation (response includes the whsec_ signing secret β the exact operation the CLI's own live key refuses), customer writes, and the permission model behaving as scoped.
With the org key on file, a new project's Stripe surface shrinks to: ~5 dashboard clicks to create the org account (top of this guide) + grab the publishable key off the onboarding screen. Everything else β live webhook, signing secret, credentials β is agent work. No per-project key minting, no per-project stripe login (though logging in is still the convenient way to get sandbox/test keys for the development block).
Where does the app's production secret key come from? Two modes, honest trade-off:
- Isolation mode (default recommendation): a per-account restricted key (same permission list) for any project handling real volume β one dashboard step per project. A leaked key compromises one project.
- Max-churn mode: the app itself runs on the org key.
stripe-ruby(which RailsFast ships) supports this natively β set the app'sstripe.contextcredential and the initializer routes every call, no patches needed. Launch fast, but understand the exposure: one leaked project server exposes every account in the org (bounded by the key's scoped permissions and central revocability).
The graduation policy
Max-churn mode needs a written exit rule, decided before the first project takes off β future-you under revenue pressure will not stop to think about key hygiene. The policy we use and recommend:
- Launch every new project on the org key. Speed is the point.
- Graduate a project to its own per-account restricted key when ANY of these becomes true: its first real customer charge Β· ~$500 MRR Β· ~100 customers. Whichever comes first.
- Graduation is a ~5-minute, mostly-agent task: mint the per-account restricted key in the dashboard (permission list above) β hand it over β swap
private_key, delete thecontext:line β redeploy β verify with a live read. - Rotate the org key on a calendar (semi-annually) and immediately on any suspected exposure β central revocation in the org dashboard kills it everywhere at once, which is precisely the feature.
- The org key lives in exactly two kinds of places: encrypted Rails credentials, and a
chmod 600local file. Never in ENV files, CI settings, or anything plaintext.