Stripe is the payments infrastructure layer for millions of businesses — and the Stripe API exposes comprehensive data about revenue, subscriptions, and customer payment behavior. AI agents connected to Stripe transform this data from numbers in a dashboard into actionable intelligence: automated alerts when payment failure rates spike, real-time chargeback response preparation, and on-demand revenue analysis for finance teams.
This guide covers building a payment monitoring agent, subscription analytics tools, and automated chargeback handling workflows.
What AI Agents Can Do With Stripe Access#
Payment Monitoring
- Alert finance teams when payment failure rate exceeds threshold in the last hour
- Classify failed payments by reason (insufficient funds, card declined, network error) and route to appropriate response
- Detect unusual payment patterns that may indicate fraud or integration bugs
- Monitor average payment amounts for statistical anomalies
Subscription Analytics
- Generate MRR (Monthly Recurring Revenue) and ARR reports on demand
- Identify customers approaching subscription renewal with declining usage
- Track churn rate trends and flag at-risk subscription cohorts
- Compare plan conversion rates across pricing experiments
Chargeback and Dispute Handling
- Automatically compile chargeback evidence (transaction details, customer history, delivery confirmation)
- Draft dispute response letters based on the dispute reason
- Calculate win probability based on dispute type and available evidence
Customer Finance Operations
- Process refund requests that qualify under policy without human review
- Answer "what is customer X's payment history?" queries from customer success teams
- Generate payment receipts and invoices on demand
Setting Up Stripe API Access#
pip install stripe langchain langchain-openai python-dotenv
export STRIPE_SECRET_KEY="sk_live_your_key" # Use sk_test_ for development
export STRIPE_WEBHOOK_SECRET="whsec_your_webhook_secret"
Important: Create a restricted API key with only the permissions your agent needs:
- Go to Stripe Dashboard → Developers → API Keys → Create restricted key
- Grant read access only unless your agent needs write operations
Option 1: No-Code with n8n#
Best for: Payment failure alerts and webhook-triggered workflows
Payment Failure Alert Workflow#
- Webhook Trigger: Configure Stripe to send
payment_intent.payment_failedevents - Parse event body to extract customer email, amount, failure reason
- OpenAI: Generate a customer-friendly email explaining the issue and next steps
- Gmail/SendGrid: Send the email to the customer
- Slack: Alert the customer success team with failure details
Option 2: LangChain with Python#
Build Stripe Tools#
import os
import stripe
from langchain.tools import tool
from datetime import datetime, timedelta
from dotenv import load_dotenv
load_dotenv()
stripe.api_key = os.getenv("STRIPE_SECRET_KEY")
@tool
def get_payment_failure_summary(hours_back: int = 24) -> str:
"""Get a summary of payment failures in the last N hours with failure reason breakdown."""
cutoff = int((datetime.now() - timedelta(hours=hours_back)).timestamp())
# Fetch recent charges
charges = stripe.Charge.list(
created={"gte": cutoff},
limit=100
)
failures = [c for c in charges.auto_paging_iter()
if c.status == "failed" and c.created >= cutoff]
if not failures:
return f"No payment failures in the last {hours_back} hours"
# Classify by failure reason
reasons = {}
for charge in failures:
reason = charge.failure_code or "unknown"
reasons[reason] = reasons.get(reason, 0) + 1
total_failed_amount = sum(c.amount for c in failures) / 100
summary = [f"Payment Failures (last {hours_back}h):",
f"Total failures: {len(failures)}",
f"Total amount at risk: ${total_failed_amount:,.2f}",
"\nFailure reasons:"]
for reason, count in sorted(reasons.items(), key=lambda x: -x[1]):
summary.append(f" - {reason}: {count} ({count/len(failures)*100:.0f}%)")
return "\n".join(summary)
@tool
def get_mrr_snapshot() -> str:
"""Get current Monthly Recurring Revenue and subscription statistics."""
subscriptions = stripe.Subscription.list(status='active', limit=100)
total_mrr = 0
plan_breakdown = {}
for sub in subscriptions.auto_paging_iter():
for item in sub['items']['data']:
amount = item['price']['unit_amount'] or 0
interval = item['price']['recurring']['interval']
plan_name = item['price'].get('nickname') or item['price']['id']
# Normalize to monthly
monthly = amount / 12 if interval == 'year' else amount
total_mrr += monthly
plan_breakdown[plan_name] = plan_breakdown.get(plan_name, {'count': 0, 'mrr': 0})
plan_breakdown[plan_name]['count'] += 1
plan_breakdown[plan_name]['mrr'] += monthly / 100
result = [f"MRR Snapshot:",
f"Total MRR: ${total_mrr/100:,.2f}",
f"ARR: ${total_mrr/100*12:,.2f}",
"\nPlan breakdown:"]
for plan, data in sorted(plan_breakdown.items(), key=lambda x: -x[1]['mrr']):
result.append(f" - {plan}: {data['count']} subscriptions, ${data['mrr']:,.2f}/mo")
return "\n".join(result)
@tool
def get_customer_payment_history(customer_email: str) -> str:
"""Look up payment history for a customer by email address."""
customers = stripe.Customer.list(email=customer_email, limit=1)
if not customers.data:
return f"No Stripe customer found with email: {customer_email}"
customer = customers.data[0]
charges = stripe.Charge.list(customer=customer.id, limit=20)
history = [f"Payment history for {customer_email} (ID: {customer.id}):"]
for charge in charges.auto_paging_iter():
date = datetime.fromtimestamp(charge.created).strftime("%Y-%m-%d")
amount = charge.amount / 100
status = charge.status
history.append(f" {date}: ${amount:.2f} — {status}")
return "\n".join(history) if len(history) > 1 else "No payment history found"
@tool
def list_recent_disputes() -> str:
"""List open payment disputes/chargebacks for review."""
disputes = stripe.Dispute.list(limit=20)
open_disputes = [d for d in disputes.auto_paging_iter() if d.status == 'needs_response']
if not open_disputes:
return "No disputes currently need response"
result = [f"Open disputes requiring response ({len(open_disputes)}):"]
for dispute in open_disputes:
due_by = datetime.fromtimestamp(dispute.evidence_details['due_by']).strftime("%Y-%m-%d")
result.append(f" - ${dispute.amount/100:.2f} | Reason: {dispute.reason} | Due: {due_by} | ID: {dispute.id}")
return "\n".join(result)
Revenue Monitoring Agent#
from langchain_openai import ChatOpenAI
from langchain.agents import create_tool_calling_agent, AgentExecutor
from langchain_core.prompts import ChatPromptTemplate
llm = ChatOpenAI(model="gpt-4o", temperature=0)
tools = [get_payment_failure_summary, get_mrr_snapshot,
get_customer_payment_history, list_recent_disputes]
prompt = ChatPromptTemplate.from_messages([
("system", """You are a financial analyst with access to Stripe payment data.
Provide clear, actionable financial analysis:
- For payment failures: identify patterns and recommend response priorities
- For MRR questions: provide growth context and plan performance
- For customer inquiries: provide accurate payment history with relevant context
- For disputes: summarize evidence gaps and recommend response strategy
Always present financial data clearly with proper currency formatting."""),
("human", "{input}"),
("placeholder", "{agent_scratchpad}"),
])
agent = create_tool_calling_agent(llm, tools, prompt)
executor = AgentExecutor(agent=agent, tools=tools, verbose=True)
# Generate daily finance briefing
result = executor.invoke({
"input": "Generate a daily revenue briefing: check payment failures from the last 24 hours, current MRR, and any open disputes."
})
print(result["output"])
Webhook-Triggered Payment Failure Agent#
from flask import Flask, request, jsonify
import stripe
app = Flask(__name__)
@app.route('/stripe/webhook', methods=['POST'])
def stripe_webhook():
payload = request.data
sig_header = request.headers.get('Stripe-Signature')
try:
event = stripe.Webhook.construct_event(
payload, sig_header, os.getenv("STRIPE_WEBHOOK_SECRET")
)
except stripe.error.SignatureVerificationError:
return jsonify({"error": "Invalid signature"}), 400
if event.type == 'payment_intent.payment_failed':
payment_intent = event.data.object
customer_id = payment_intent.get('customer')
# Trigger agent to analyze and respond
result = executor.invoke({
"input": f"""A payment failed for customer {customer_id}.
Payment amount: ${payment_intent['amount']/100:.2f}
Failure reason: {payment_intent.get('last_payment_error', {}).get('message', 'Unknown')}
Look up the customer's payment history and provide a recommended response strategy."""
})
# Log result or notify team via Slack
print(result["output"])
return jsonify({"received": True})
Rate Limits and Best Practices#
| Operation type | Rate limit |
|---|---|
| Read operations | 100 req/second |
| Write operations | 100 req/second |
| Test mode | Lower limits apply |
Best practices:
- Scope API keys minimally: Use restricted keys with only the permissions needed
- Validate all webhooks: Always verify Stripe webhook signatures before processing
- Use test mode during development: Never run agent tests against live payment data
- Paginate efficiently: Request 100 items per page (maximum) when iterating large datasets
- Cache analytics results: Cache MRR and summary data for 10-15 minutes to avoid repeated API calls
Next Steps#
- AI Agents Intercom Integration — Connect payment data to customer support
- AI Agents QuickBooks Integration — Sync Stripe revenue to accounting
- Build an AI Agent with LangChain — Complete framework tutorial
- Human-in-the-Loop Agents — When to require human approval for financial operations