Stripe API Skill: Managed OAuth for Payment Workflows in AI Agents
With over 17,000 downloads, the stripe-api skill by @byungkyu is the leading Stripe integration on ClawHub. It gives Claude access to the full Stripe API — customers, subscriptions, invoices, products, prices, charges, payment intents, refunds, and coupons — through Maton's managed OAuth gateway. One API key, no Stripe secret keys stored anywhere, and the same pattern extends to 100+ other APIs.
The Problem It Solves
Integrating Stripe directly means managing secret API keys: storing them in environment variables, rotating them when compromised, and ensuring they never end up in logs or chat history. When an AI agent is involved, there's an additional concern — Claude sees everything in its context, including any credentials that appear in requests or responses.
Maton's gateway solves this with a level of indirection. You connect your Stripe account to Maton via OAuth, then communicate with Stripe through Maton's proxy. Claude only ever sees your MATON_API_KEY, never your Stripe secret key. Maton injects the appropriate Stripe OAuth token server-side before forwarding requests.
Architecture
The skill routes all Stripe API calls through Maton's gateway:
Claude → Maton gateway → Stripe API
(https://gateway.maton.ai/stripe/v1/...)
Base URL pattern:
https://gateway.maton.ai/stripe/v1/{stripe-endpoint}
This mirrors the native Stripe API URL structure (https://api.stripe.com/v1/{endpoint}) — if you know Stripe's API, you already know this skill's endpoints.
Authentication: One header, everywhere:
Authorization: Bearer $MATON_API_KEY
Setup
- Create a Maton account at maton.ai and copy your API key from maton.ai/settings
- Connect your Stripe account via OAuth at ctrl.maton.ai
- Set the environment variable:
export MATON_API_KEY="your_maton_api_key"Install the skill:
clawdbot install stripe-apiCore Operations
Customers
import os, requests
base = "https://gateway.maton.ai/stripe"
headers = {"Authorization": f"Bearer {os.environ['MATON_API_KEY']}"}
# List customers
r = requests.get(f"{base}/v1/customers?limit=10", headers=headers)
customers = r.json()["data"]
# Create a customer
r = requests.post(f"{base}/v1/customers",
headers=headers,
data={"email": "user@example.com", "name": "Jane Doe"})
customer = r.json()
# Get a specific customer
r = requests.get(f"{base}/v1/customers/cus_xxx", headers=headers)Note: Stripe's API uses application/x-www-form-urlencoded for POST requests, not JSON.
Products and Prices
# Create a product
r = requests.post(f"{base}/v1/products",
headers=headers,
data={"name": "Pro Plan", "type": "service"})
product_id = r.json()["id"] # prod_xxx
# Create a price for that product ($19.99/month)
r = requests.post(f"{base}/v1/prices",
headers=headers,
data={
"product": product_id,
"unit_amount": 1999,
"currency": "usd",
"recurring[interval]": "month"
})
price_id = r.json()["id"] # price_xxxAmounts are always in the smallest currency unit: cents for USD, pence for GBP, etc.
Subscriptions
# Create a subscription
r = requests.post(f"{base}/v1/subscriptions",
headers=headers,
data={"customer": "cus_xxx", "items[0][price]": "price_xxx"})
subscription = r.json()
# Filter subscriptions by status
r = requests.get(f"{base}/v1/subscriptions?status=past_due&customer=cus_xxx",
headers=headers)
# Cancel a subscription
r = requests.delete(f"{base}/v1/subscriptions/sub_xxx", headers=headers)Invoices
# List unpaid invoices
r = requests.get(f"{base}/v1/invoices?status=open&limit=20", headers=headers)
# Finalize a draft invoice
r = requests.post(f"{base}/v1/invoices/in_xxx/finalize", headers=headers)
# Trigger payment on a finalized invoice
r = requests.post(f"{base}/v1/invoices/in_xxx/pay", headers=headers)
# Void an invoice
r = requests.post(f"{base}/v1/invoices/in_xxx/void", headers=headers)Each paid invoice includes hosted_invoice_url (web) and invoice_pdf links.
Charges and Payment Intents
# List recent charges
r = requests.get(f"{base}/v1/charges?limit=10&customer=cus_xxx", headers=headers)
# Create a payment intent
r = requests.post(f"{base}/v1/payment_intents",
headers=headers,
data={"amount": 5000, "currency": "usd", "customer": "cus_xxx",
"payment_method_types[]": "card"})
# Confirm a payment intent
r = requests.post(f"{base}/v1/payment_intents/pi_xxx/confirm", headers=headers)Refunds
# Full refund
r = requests.post(f"{base}/v1/refunds",
headers=headers,
data={"charge": "ch_xxx"})
# Partial refund ($10.00)
r = requests.post(f"{base}/v1/refunds",
headers=headers,
data={"charge": "ch_xxx", "amount": 1000})Coupons
# Create a 25% off coupon (one-time use)
r = requests.post(f"{base}/v1/coupons",
headers=headers,
data={"percent_off": 25, "duration": "once"})
# $5 off forever
r = requests.post(f"{base}/v1/coupons",
headers=headers,
data={"amount_off": 500, "currency": "usd", "duration": "forever"})Pagination
Stripe uses cursor-based pagination. To page through results:
def get_all_customers():
all_customers = []
last_id = None
while True:
params = {"limit": 100}
if last_id:
params["starting_after"] = last_id
r = requests.get(f"{base}/v1/customers", headers=headers, params=params)
data = r.json()
all_customers.extend(data["data"])
if not data["has_more"]:
break
last_id = data["data"][-1]["id"]
return all_customersMultiple Stripe Accounts
If you manage multiple Stripe accounts, connect each one at Maton's control panel and specify which connection to use per request:
# List your connections
r = requests.get("https://ctrl.maton.ai/connections?app=stripe&status=ACTIVE", headers=headers)
# Use a specific connection
r = requests.get(f"{base}/v1/customers",
headers={
"Authorization": f"Bearer {os.environ['MATON_API_KEY']}",
"Maton-Connection": "c3c82a73-4c86-4c73-8ebd-1f325212fde6"
})If Maton-Connection is omitted, Maton uses the default (oldest active) connection.
Stripe API vs. stripe-api Skill
| Aspect | Direct Stripe API | stripe-api skill (via Maton) |
|---|---|---|
| Credentials in agent context | Stripe secret key | Maton API key only |
| Multiple accounts | Separate keys | One key + connection header |
| OAuth token rotation | Manual | Automatic via Maton |
| Extends to other APIs | ❌ | ✅ 100+ via api-gateway |
| API coverage | Full Stripe | Full Stripe (same endpoints) |
| Latency | Direct | +1 proxy hop |
Key Resource ID Prefixes
Stripe's IDs are self-describing:
| Prefix | Resource |
|---|---|
cus_ | Customer |
prod_ | Product |
price_ | Price |
sub_ | Subscription |
in_ | Invoice |
ch_ | Charge |
pi_ | Payment Intent |
pm_ | Payment Method |
re_ | Refund |
Practical Tips
-
Use
api-gatewayfor other services. The same Maton API key and OAuth pattern applies to 100+ APIs (Slack, HubSpot, Salesforce, Google Workspace, etc.) via theapi-gatewayskill. If you're already using Maton for Stripe, you have access to the full catalog. -
Amounts in cents, not dollars. This is Stripe's convention, not Maton's.
unit_amount: 1999= $19.99 USD. Always double-check when creating prices or charges. -
POST uses form-encoding, not JSON. Stripe's API uses
application/x-www-form-urlencodedfor writes. In Python'srequests, this means usingdata={}(form) notjson={}(JSON body). Nested params use bracket notation:recurring[interval]=month. -
Use
-gflag with curl for bracket params. When using curl with nested parameters, add-gto disable glob expansion:curl -g "...?items[0][price]=price_xxx". -
Filter subscriptions by status for churn analysis.
status=past_duecatches failed renewals,status=canceledshows churn. These two queries are often the starting point for billing health checks. -
Finalize then pay for manual invoice workflows. Draft invoices won't charge customers. Call
/finalizeto lock the invoice, then/payto collect payment. Or use Stripe's automatic collection by enablingauto_advanceon invoices.
Considerations
- Maton is an intermediary: Your Stripe connection lives in Maton's OAuth system. If Maton experiences downtime, Stripe API calls through this skill will fail.
- Proxy latency: Each request adds one network hop through
gateway.maton.ai. For high-volume or latency-sensitive operations, measure impact against direct Stripe API calls. - Account verification required: Connecting Stripe to Maton requires completing the Stripe OAuth flow in a browser. This can't be done programmatically — it's a one-time manual step per Stripe account.
- No webhook support: This skill handles outbound API calls. Stripe webhooks (incoming events) require a separate endpoint — the skill doesn't handle event subscriptions.
- Stripe test mode: Use Stripe test mode credentials during development. Test and live are separate Stripe accounts from Maton's perspective — you'll need separate Maton connections for each.
The Bigger Picture
The stripe-api skill represents a broader pattern: managed OAuth gateways that let AI agents access third-party services without ever seeing actual service credentials. Maton extends this to 100+ APIs using the same model — one API key, many services, credentials handled server-side.
For teams building AI agents that need to touch billing, payment history, or subscription state as part of a broader workflow, this approach avoids the credential management problem that normally creates friction between AI systems and sensitive financial APIs.
View the skill on ClawHub: stripe-api