Skip to main content
59 toolsAPI key
stripe_example.py
Customers, payments, charges, refunds, the product catalogue, subscriptions, Checkout Sessions and the account balance. Stripe’s parameter surface is enormous — checkout.sessions.create alone has around fifty parameters with deep nesting — so these schemas model the commonly-used subset exactly rather than claiming completeness badly. Form-encoded bodies, bracket notation for nesting, and a cursor the API never hands back are all declared in the wire contract.

Authenticating

This pack takes an API key in Authorization, and configure() is optional when $STRIPE_API_KEY is set. There is no authorization server, no consent screen and no refresh — API keys is the whole story. Prefer a restricted key scoped to what the agent actually needs — an agent that only reads should not hold a key that can issue refunds.

The client

api_key_tool_factory is the whole client: a thin wrapper over httpx that attaches your key and these endpoint constants to each request. stripe does not enter your dependency tree.

Paging through a list

A cursor belongs to the tool that returns it, so it is declared on that tool’s builder call:
stripe_pagination.py
Pagination is declared on customers_list, payment_intents_list, charges_list, refunds_list, products_list, prices_list, subscriptions_list, invoices_list, invoice_items_list, checkout_sessions_list, checkout_sessions_line_items, payment_methods_list, customer_payment_methods_list, disputes_list, accounts_list, transfers_list, payouts_list, application_fees_list and balance_transactions_list. The other 40 take no cursor.

Tools

Each is a Tool, called with ainvoke as in the snippet above. The name links to its parameters, its response and what it costs.
Customerscustomers_listGETList customers, most recently created first.customers_retrieveGETRetrieve a single customer by ID.customers_createPOSTCreate a customer.customers_updatePOSTUpdate a customer.Paymentspayment_intents_listGETList PaymentIntents, most recently created first.payment_intents_retrieveGETRetrieve a single PaymentIntent by ID.charges_listGETList charges, most recently created first.refunds_createPOSTRefund a charge.refunds_listGETList refunds, most recently created first.refunds_retrieveGETRetrieve a single refund by ID, including its current status.balance_retrieveGETRetrieve the current account balance.Catalogproducts_listGETList products in the catalog.prices_listGETList prices.subscriptions_listGETList subscriptions.Subscriptionssubscriptions_createPOSTSubscribe a customer to one or more prices.subscriptions_updatePOSTUpdate a subscription.subscriptions_cancelDELETECancel a subscription immediately.Invoicesinvoices_listGETList invoices, most recently created first.invoices_retrieveGETRetrieve a single invoice by ID.invoices_createPOSTCreate a draft invoice for a customer.invoices_updatePOSTUpdate an invoice.invoices_finalizePOSTFinalise a draft invoice, making it open and payable.invoices_sendPOSTEmail an invoice to the customer outside the normal schedule.invoices_voidPOSTVoid a finalised invoice.invoices_mark_uncollectiblePOSTRecord an invoice as bad debt.Invoiceitemsinvoice_items_createPOSTAdd a line to an invoice.invoice_items_listGETList invoice lines.invoice_items_updatePOSTUpdate an invoice line.invoice_items_deleteDELETERemove an invoice line, while it is unattached or its invoice is a draft.Catalog writescheckout_sessions_retrieveGETRetrieve a checkout session.checkout_sessions_listGETList checkout sessions.checkout_sessions_line_itemsGETList what a checkout session sold, with amounts and quantities.products_createPOSTCreate a product.products_updatePOSTUpdate a product.prices_createPOSTCreate a price for a product.prices_updatePOSTUpdate a price’s label, metadata or active flag.Payment intentspayment_intents_createPOSTStart a payment.payment_intents_capturePOSTTake funds previously authorised by a manual-capture payment.payment_intents_cancelPOSTCancel a payment, releasing any authorised funds back to the customer.charges_retrieveGETRetrieve a single charge by ID.Payment methodspayment_methods_listGETList payment methods.payment_methods_retrieveGETRetrieve a payment method by ID.payment_methods_attachPOSTAttach a payment method to a customer so it can be charged later.payment_methods_detachPOSTDetach a payment method from its customer.customer_payment_methods_listGETList the payment methods attached to one customer.customer_payment_method_retrieveGETRetrieve one payment method belonging to a customer.Disputesdisputes_listGETList disputes, most recently created first.disputes_retrieveGETRetrieve a dispute by ID.disputes_updatePOSTAdd evidence to a dispute.disputes_closePOSTConcede a dispute.Connectaccounts_listGETList the connected accounts on this platform.accounts_retrieveGETRetrieve a connected account by ID.transfers_listGETList transfers to connected accounts, most recently created first.transfers_retrieveGETRetrieve a transfer by ID.payouts_listGETList payouts to your own bank account or card, most recently created first.payouts_retrieveGETRetrieve a payout by ID.application_fees_listGETList the platform fees collected from connected accounts.balance_transactions_listGETList every movement across the Stripe balance: charges, refunds, fees and payouts.Checkoutcheckout_sessions_createPOSTCreate a Checkout Session and get a hosted payment URL.

Form-encoded, in both directions

Stripe speaks application/x-www-form-urlencoded with bracket notation. body_format="form" and query_format="bracket" say so once on the factory, and a nested schema serialises the way Stripe expects: The bracket form applies to the query too, which is what created[gte]=1700000000 needs.

A cursor that is derived, not returned

Where Slack and Google hand back an opaque token, Stripe’s starting_after takes the last object’s id. The declaration reads it straight out of the page:
stripe_derived_cursor.py
has_more is declared because a derived cursor is always present — there is always a last object — so without it the walk would never terminate. This is the constraint the trimming handlers work under: they must keep each object’s id and they must keep has_more, or paging breaks. It is stated in the pack source next to the handlers for exactly that reason.

No envelope

Stripe uses real HTTP status codes and puts the detail in error.message, which the runtime already understands. Nothing to declare — worth saying out loud, because the right amount of configuration for a well-behaved API is none.

A pinned API version

An unpinned Stripe request does not use the latest version. It uses whichever version the account defaults to, set in a dashboard this library cannot see — so the same code answers differently for two callers, and a schema written against one of them is correct by luck. stripe.API_VERSION goes out as a Stripe-Version header on every request, declared once as static_headers on the factory. It never appears in a tool’s LLM schema, so it costs nothing in context and the model has no path to changing it. The cost of not doing this is not hypothetical. Stripe moved current_period_start and current_period_end off the Subscription object and onto each subscription item; a handler still projecting them from the subscription finds nothing, drops both, and hands the model an active subscription with no renewal date and no indication that anything is missing. trim_subscriptions reads them from the items, and the pin is what keeps that true.

Idempotency and Connect

Both are values your application decides and the model never sees, so they go through the per-call header channel rather than onto the tool:
stripe_idempotent_refund.py
Charter never mints the idempotency key. A key generated per call defeats the purpose — the point is that your retry sends the same one — and Charter does not own retries, so it does not own the key. headers is keyword-only and is never merged into args, so it cannot appear in a tool’s LLM schema.

Gotchas

amount=2000 is €20.00, not €2,000. Zero-decimal currencies such as JPY are the exception, where amount=2000 is ¥2000. Stripe applies this to refunds too, so a partial refund of “ten euros” is amount=1000.
So do products_list and balance_retrieve. The other nine tools run a handler that projects each object down to the fields an agent can act on. A raw PaymentIntent carries around forty fields, most of them null or infrastructural.
customers_update is POST /v1/customers/{customer}, not PATCH — that is Stripe’s convention. Parameters you do not send are unchanged; sending metadata replaces the whole metadata object rather than merging into it.
Both starting_after and ending_before are on the list schemas because Stripe accepts both. The Pagination declares the forward direction only, so next_page_args walks forwards; ending_before is there for you to use directly. They are the two directions of one cursor, so sending both is refused locally.
current_period_start and current_period_end used to be fields on the Subscription; they now live on each subscription item, which can be on different schedules. trim_subscriptions lifts the period back to the top level when every item agrees, which is nearly always, and reports it per item when they do not. Nothing is averaged and nothing is guessed.
A refund names exactly one of charge or payment_intent; a Checkout Session in payment or subscription mode needs line_items; one in setup mode needs currency; and success_url and cancel_url are refused when ui_mode is embedded_page or elements, where the customer never leaves your page. Each is a documented 400 turned into a local validation error — worth doing here because a Checkout Session is usually created with a customer waiting on it.Conditions that depend on parameters these schemas do not model are left to Stripe and named in the field descriptions instead. return_url is the one to know: it is required for the in-page modes only when redirect-based payment methods are enabled on the session, which is not visible from here.
A schema with exactly one Body() field unwraps to its bare value, which for a list would produce [0][price]=… — meaningless to any server. That raises a clear error rather than sending it. No tool in this pack trips it; a tool you add might.