Stripe Restricted Keys

Stripe restricted API key example for an AI agent

A real, minimal working example — the exact scopes to enable for an agent that issues refunds, how to create the key in Dashboard or by API, and the three places this primitive stops being enough.

TL;DR

For an agent that only issues refunds on existing charges: tick Charges: Read, Refunds: Write, Customers: Read — nothing else. Generate under Developers → API keys → Create restricted key. Prefix the key name with the agent's runner ID. The key covers which endpoints the agent can hit; it does not cap how much, scope to which customers, or let you revoke in under a minute.

The example

The concrete use case: a support agent that reads incoming tickets, decides whether to refund, and issues the refund via Stripe. The agent should not be able to create charges, edit customers, mint payouts, or touch Connect accounts. The minimal scope set:

# permissions to tick on the Restricted Key form
Charges           → Read
Refunds           → Write       (implies Read)
Customers         → Read
PaymentIntents    → Read        (needed to look up the parent intent)
# everything else → None

That is five ticks. Any other resource left at None means the key returns 401 Unauthorized if the agent tries it. The agent SDK will see the 401 and — if it's a well-behaved agent — surface the error instead of retrying forever.

How to create it

Dashboard (fastest)

  1. In the Stripe Dashboard, go to Developers → API keys.
  2. Scroll to Restricted keys. Click Create restricted key.
  3. Name it something specific: agent-support-refunds-runner42. The name is the only signal you'll have in audit logs, so make it include the agent's role and runner.
  4. Tick only the five permissions above. Leave everything else at None.
  5. Click Create key. Stripe shows the full key (rk_live_…) exactly once. Copy it into your secret store now — Stripe will not show it again.

Via API (for programmatic provisioning)

Stripe does not expose a public API to create Restricted Keys — you create them in the Dashboard or via their CLI. If you need programmatic issuance per-agent, you either rotate manually on a schedule or use a proxy that issues its own keys and holds one shared restricted key upstream.

Why these five permissions and nothing more

Each permission you enable widens the blast radius of a stuck loop. Walk through what the agent actually needs, one call at a time:

Agent actionAPI callRequired permission
Look up the payment the ticket refers toGET /v1/charges/:idCharges: Read
Check who the customer isGET /v1/customers/:idCustomers: Read
Resolve which PaymentIntent the charge belongs toGET /v1/payment_intents/:idPaymentIntents: Read
Issue the refundPOST /v1/refundsRefunds: Write

Anything else — writing to customers, creating charges, creating disputes, transferring funds, editing webhooks — is not on the path for this use case. Leave it off.

Where this example stops being enough

The Restricted Key is a correctness primitive — it answers which endpoints. It does not answer the three questions a security-conscious operator actually has about an agent:

  1. How much can it refund before something stops it? Restricted Keys have no dollar cap. A stuck loop on POST /v1/refunds will run until Stripe's own rate limits kick in — by which point the agent has already refunded thousands of charges.
  2. Can it refund any customer, or only ones it was assigned? Refunds: Write is global across the account. If the agent's ticket-selection logic misfires, it can refund customers outside its intended cohort.
  3. How fast can I stop it mid-run? Rotating a Restricted Key from the Dashboard takes ~30 seconds of human work, and the agent's existing SDK client may hold the key in memory until the next restart.

If your answer to any of those three is "I don't know" or "I haven't tested it", a Restricted Key alone is not sufficient. We wrote a longer walkthrough of the five controls an agent actually needs on a live key.

How Keybrake helps

Keybrake sits between your agent and api.stripe.com. You keep the Restricted Key (our upstream) and issue per-agent vault_key_… credentials with policies attached: daily USD cap, customer allowlist, endpoint allowlist at path-level (POST /v1/refunds yes, POST /v1/refunds/:id/cancel no), and a mid-run kill-switch that takes effect on the next call. The Restricted Key gives you which resource; Keybrake adds how much, against whom, and for how long. See the full feature matrix →

Get early access

Related questions

Can I have multiple Restricted Keys active at the same time?

Yes. Stripe accounts support many Restricted Keys simultaneously. A common pattern is one per agent role (support-refund, catalog-update, invoice-lookup), so revoking one does not take down the others.

Does a Restricted Key work with Stripe Connect?

Only for platform-scoped calls. You cannot scope a Restricted Key to act on a specific connected account the way you can scope by resource. For Connect-heavy use cases, you usually combine a Restricted Key with a Stripe-Account header allowlist enforced by your proxy.

How do I test a Restricted Key safely before issuing it to the agent?

Generate the key in test mode first (rk_test_…) with the identical scope set, run the agent against test-mode endpoints, and only promote once you've seen the agent hit the expected 401s on every out-of-scope call. The test-mode key is free and disposable.

Further reading