Read and manage Stripe customers, payments, invoices, subscriptions, and refunds with dry-run mutations.
- Integrations
- stripe
- payments
- billing
- invoices
- subscriptions
- refunds
- webhooks
- finance
- License
- MIT
- Published
- August 23, 2026
- Pinned commit
adef0e4- Rating
- No ratings yet
- Forks
- 3
- Stars
- 0
- Adaptation effort
- —
README
@kody/stripe
Intent
Reusable Stripe helpers so Kody agents can read and manage customers, payments,
invoices, subscriptions, refunds, products, and webhook events on the
caller's Stripe account — not a shared platform store. Auth is a saved API
key, not OAuth. Reads are free-form; mutations support dryRun: true, and
money-moving or destructive calls also require confirm: true.
This listing is meant to be forked. After you fork, save your own
stripeApiKey and call the helpers in your account.
This official API-key package is the preferred invoke path. Setup is
harder: save a Stripe secret or restricted key. For a faster first win,
connect Stripe MCP in Get started and use
@kody/stripe-mcp.
Auth
API key (secret-backed). There is no Stripe OAuth integration and no bot
token. Do not open /connect/oauth for Stripe.
| Secret | Purpose |
|---|---|
stripeApiKey | Secret key (sk_live_… / sk_test_…) or restricted key (rk_…) |
stripeWebhookSecret | Endpoint signing secret (whsec_…) for HMAC verify |
Required setup
- Create a key at dashboard.stripe.com/apikeys. Prefer a restricted key with only the permissions you need.
- Save it in Kody (do not paste the value in chat):
- In the account secrets UI, approve hosts
api.stripe.comandfiles.stripe.com. - Optional webhook signing secret (Developers → Webhooks → endpoint signing secret):
stripeWebhookSecret is used only for local HMAC verification. It does not
need Stripe API hosts. Inbound deliveries still need a URL you control (after
a fork, a package-declared Kody webhook is one option).
Hosts
api.stripe.com— all REST callsfiles.stripe.com— file uploads (stripeUploadFile)
Restricted-key permissions
A full secret key (sk_…) can call everything this package exposes. A
restricted key must include the permission for the endpoint. Typical set:
| Helper | Permission |
|---|---|
getAccount | rak_account_read |
getBalance, listBalanceTransactions, listPayouts | rak_balance_read |
| customers | rak_customer_read / rak_customer_write |
| charges, payment intents, refunds | rak_charge_read / rak_charge_write, rak_payment_intent_read |
| invoices | rak_invoice_read / rak_invoice_write |
| subscriptions | rak_subscription_read / rak_subscription_write |
| products, prices, payment links | rak_product_read / rak_product_write |
events / loadVerifiedEvent | rak_event_read |
listWebhookEndpoints | rak_webhook_read |
If Stripe returns 403 / insufficient permissions, this package throws
StripeApiError naming the missing rak_… permission (when Stripe includes
it) and the next setup step: add that permission at
https://dashboard.stripe.com/apikeys then update stripeApiKey at the
secrets URL above.
HTTP 401 means the key is missing or invalid — save stripeApiKey at the
prefilled URL.
Multi-account
Pass account: "work" to use secret stripeApiKey-work (and
stripeWebhookSecret-work for HMAC). Or pass secretName: "stripeApiKey-live".
There are no hard-coded account aliases.
Webhook signature verify
Stripe signs ${timestamp}.${rawBody} with HMAC-SHA256 and sends
Stripe-Signature: t=…,v1=…. Two lanes:
- HMAC —
verifyWebhookSignature({ payload, signatureHeader, webhookSecret })when the handler already haswhsec_…(never paste it in chat). - API retrieve —
loadVerifiedEvent({ eventId })re-fetchesevt_…throughstripeApiKey. Prefer this from Kody when you do not want to hold the signing secret in the handler.
import { verifyWebhookSignature, loadVerifiedEvent } from 'kody:@kody/stripe/webhooks'
await verifyWebhookSignature({
payload: rawBody,
signatureHeader: stripeSignatureHeader,
webhookSecret: 'whsec_…', // from your webhook handler, not from chat
})
await loadVerifiedEvent({ eventId: 'evt_123' })Register the endpoint in Stripe webhooks pointing at your inbound URL. This official listing does not mint a shared ingress URL.
Exports
kody:@kody/stripe— action dispatcher (defaults tosmoke-test); also re-exports every helper belowkody:@kody/stripe/account—getAccount,getBalance,listBalanceTransactions,listPayouts,listEventskody:@kody/stripe/customers—listCustomers,searchCustomers,getCustomer,createCustomer,updateCustomer,deleteCustomerkody:@kody/stripe/payments—listPaymentIntents,getPaymentIntent,searchPaymentIntents,listCharges,getCharge,searchCharges,listRefunds,createRefundkody:@kody/stripe/invoices—listInvoices,getInvoice,searchInvoices,createInvoice,finalizeInvoice,sendInvoice,voidInvoice,deleteDraftInvoicekody:@kody/stripe/subscriptions—listSubscriptions,getSubscription,searchSubscriptions,createSubscription,cancelSubscriptionkody:@kody/stripe/products—listProducts,getProduct,createProduct,archiveProduct,listPrices,createPrice,listPaymentLinks,createPaymentLink,deactivatePaymentLinkkody:@kody/stripe/webhooks—verifyWebhookSignature,loadVerifiedEvent,listWebhookEndpoints,getWebhookEndpointkody:@kody/stripe/smoke-test— HMAC + dry-run self-check, then a live account/balance read whenstripeApiKeyexists
Low-level transport (stripeRequest, stripeList, stripeSearch,
stripeParams, stripeUploadFile, StripeApiError) stays pinned to
https://api.stripe.com/v1.
Mutation safety
- Pass
dryRun: trueon any mutation to return{ dryRun: true, method, path, body }without contacting Stripe. createRefund,createSubscription,cancelSubscription,finalizeInvoice,sendInvoice,voidInvoice,deleteDraftInvoice,deleteCustomer,archiveProduct, anddeactivatePaymentLinkalso throw unlessconfirm: true.- POST helpers accept optional
idempotencyKeyand only retry automatically when one is provided. - Amounts are Stripe integer amounts in the smallest currency unit (cents for
USD); summaries include a
displaystring.
import { createRefund } from 'kody:@kody/stripe/payments'
const preview = await createRefund({
chargeId: 'ch_123',
amount: 500,
dryRun: true,
})
const refund = await createRefund({
chargeId: 'ch_123',
amount: 500,
confirm: true,
})Smoke test
import { packages } from 'kody:runtime'
export default async function main() {
return await packages.invoke("kody:@kody/stripe", {
exportName: './smoke-test',
})
}Without stripeApiKey this still returns { ok: true, live: false } plus the
setup URL. With the secret saved it reads account, balance, and one customer
page — no writes.
Example
import stripe from 'kody:@kody/stripe'
export default async function main() {
return await stripe({
action: 'search-charges',
searchQuery: "billing_details.email:'ada@example.com'",
maxItems: 10,
})
}Branding
community-icon.svg is Stripe's official "S" glyph (Simple Icons, CC0), on
Stripe purple #635BFF. Stripe® is a trademark of Stripe, Inc. This package
is not affiliated with or endorsed by Stripe.
Docs
Report this listing
Log in to report this listing.