Cette page est affichée en anglais. Une traduction française est en cours.
SkillsMay 8, 2026·4 min de lecture

Stripe Webhook → Anthropic Tool Pattern for Async Agents

Convert Stripe webhook events (payment, dispute, refund) into Claude tool calls so agents react to real-time payment lifecycle events.

Stripe
Stripe · Community
Prêt pour agents

Cet actif peut être lu et installé directement par les agents

TokRepo expose la commande CLI, le metadata JSON, le plan d'installation et le contenu raw pour aider les agents à juger l'adaptation, le risque et les prochaines actions.

Stage only · 17/100Stage only
Cible
Claude Code
Type
Skill
Installation
Stage only
Confiance
Confiance : New
Point d'entrée
Asset
Commande CLI d'installation
npx tokrepo install a78e66e5-8945-4e63-a57b-bfd75011a20d --target codex
Introduction

This skill turns Stripe webhook events into Anthropic tool-call inputs so a Claude agent reacts to real payment lifecycle events: payment succeeded, dispute opened, subscription canceled, refund issued. The pattern is a tiny FastAPI/Express endpoint that verifies the Stripe signature, transforms the event into a structured tool call, and dispatches the call against your existing agent. Best for: support agents that triage disputes, billing chatbots that confirm payments, churn-intervention agents reacting to subscription events. Works with: Anthropic API, OpenAI Tool Use, any agent that takes tool inputs. Setup time: 15 minutes.


FastAPI endpoint

import os, stripe
from anthropic import Anthropic
from fastapi import FastAPI, Request, HTTPException

app = FastAPI()
stripe.api_key = os.environ["STRIPE_SECRET_KEY"]
WEBHOOK_SECRET = os.environ["STRIPE_WEBHOOK_SECRET"]
claude = Anthropic()

EVENT_TO_TOOL = {
    "charge.dispute.created":         "handle_dispute",
    "invoice.payment_failed":         "retry_failed_payment",
    "customer.subscription.deleted":  "churn_outreach",
    "charge.refunded":                "log_refund",
}

@app.post("/stripe/webhook")
async def webhook(request: Request):
    payload = await request.body()
    sig = request.headers.get("stripe-signature")
    try:
        event = stripe.Webhook.construct_event(payload, sig, WEBHOOK_SECRET)
    except (ValueError, stripe.error.SignatureVerificationError):
        raise HTTPException(400, "bad signature")

    tool_name = EVENT_TO_TOOL.get(event["type"])
    if not tool_name:
        return {"status": "ignored"}

    obj = event["data"]["object"]
    msg = claude.messages.create(
        model="claude-3-5-sonnet-20241022",
        max_tokens=1024,
        tools=[{
            "name": tool_name,
            "description": f"Triggered by Stripe event {event['type']}",
            "input_schema": {"type": "object", "properties": {
                "stripe_id": {"type": "string"},
                "amount":    {"type": "integer"},
                "customer":  {"type": "string"},
                "reason":    {"type": "string"},
            }},
        }],
        messages=[{
            "role": "user",
            "content": f"A Stripe {event['type']} event occurred. Decide on next action.\n\n{obj}",
        }],
    )
    return {"status": "dispatched", "claude_response": msg.content}

Production hardening

  • Signature verification is mandatory — never skip construct_event. Without it, anyone can POST fake events.
  • Idempotency — Stripe retries webhooks for 3 days on non-2xx. Store event["id"] in Redis with 7-day TTL; skip if seen.
  • Async dispatch — return 200 within 10s. Push the Claude call to a queue (Celery / Inngest / SQS) if it takes longer.
  • Replay attacksconstruct_event enforces 5-minute timestamp tolerance. Don't widen it.

FAQ

Q: Why not have Claude call Stripe directly with the Agent Toolkit? A: For synchronous user-driven flows, do that. This pattern is for async events that happen without a user prompt — disputes opened by the cardholder, subscriptions canceled by Stripe Smart Retries, payments failing on retry. The webhook is the trigger; Claude is the policy engine.

Q: How do I scope which events fire Claude? A: Whitelist in the EVENT_TO_TOOL map. Stripe will keep delivering all subscribed events but you only spend Claude tokens on the ones you map. Subscribe selectively at dashboard.stripe.com/webhooks too.

Q: What about test mode? A: Use Stripe CLI: stripe trigger charge.dispute.created --forward-to localhost:8000/stripe/webhook. The CLI signs events with your test webhook secret and replays them locally.


Quick Use

  1. Add a Stripe webhook endpoint at dashboard.stripe.com/webhooks pointing at /stripe/webhook
  2. Set STRIPE_WEBHOOK_SECRET, STRIPE_SECRET_KEY, ANTHROPIC_API_KEY
  3. Map events to tool names in EVENT_TO_TOOL

Intro

This skill turns Stripe webhook events into Anthropic tool-call inputs so a Claude agent reacts to real payment lifecycle events: payment succeeded, dispute opened, subscription canceled, refund issued. The pattern is a tiny FastAPI/Express endpoint that verifies the Stripe signature, transforms the event into a structured tool call, and dispatches the call against your existing agent. Best for: support agents that triage disputes, billing chatbots that confirm payments, churn-intervention agents reacting to subscription events. Works with: Anthropic API, OpenAI Tool Use, any agent that takes tool inputs. Setup time: 15 minutes.


FastAPI endpoint

import os, stripe
from anthropic import Anthropic
from fastapi import FastAPI, Request, HTTPException

app = FastAPI()
stripe.api_key = os.environ["STRIPE_SECRET_KEY"]
WEBHOOK_SECRET = os.environ["STRIPE_WEBHOOK_SECRET"]
claude = Anthropic()

EVENT_TO_TOOL = {
    "charge.dispute.created":         "handle_dispute",
    "invoice.payment_failed":         "retry_failed_payment",
    "customer.subscription.deleted":  "churn_outreach",
    "charge.refunded":                "log_refund",
}

@app.post("/stripe/webhook")
async def webhook(request: Request):
    payload = await request.body()
    sig = request.headers.get("stripe-signature")
    try:
        event = stripe.Webhook.construct_event(payload, sig, WEBHOOK_SECRET)
    except (ValueError, stripe.error.SignatureVerificationError):
        raise HTTPException(400, "bad signature")

    tool_name = EVENT_TO_TOOL.get(event["type"])
    if not tool_name:
        return {"status": "ignored"}

    obj = event["data"]["object"]
    msg = claude.messages.create(
        model="claude-3-5-sonnet-20241022",
        max_tokens=1024,
        tools=[{
            "name": tool_name,
            "description": f"Triggered by Stripe event {event['type']}",
            "input_schema": {"type": "object", "properties": {
                "stripe_id": {"type": "string"},
                "amount":    {"type": "integer"},
                "customer":  {"type": "string"},
                "reason":    {"type": "string"},
            }},
        }],
        messages=[{
            "role": "user",
            "content": f"A Stripe {event['type']} event occurred. Decide on next action.\n\n{obj}",
        }],
    )
    return {"status": "dispatched", "claude_response": msg.content}

Production hardening

  • Signature verification is mandatory — never skip construct_event. Without it, anyone can POST fake events.
  • Idempotency — Stripe retries webhooks for 3 days on non-2xx. Store event["id"] in Redis with 7-day TTL; skip if seen.
  • Async dispatch — return 200 within 10s. Push the Claude call to a queue (Celery / Inngest / SQS) if it takes longer.
  • Replay attacksconstruct_event enforces 5-minute timestamp tolerance. Don't widen it.

FAQ

Q: Why not have Claude call Stripe directly with the Agent Toolkit? A: For synchronous user-driven flows, do that. This pattern is for async events that happen without a user prompt — disputes opened by the cardholder, subscriptions canceled by Stripe Smart Retries, payments failing on retry. The webhook is the trigger; Claude is the policy engine.

Q: How do I scope which events fire Claude? A: Whitelist in the EVENT_TO_TOOL map. Stripe will keep delivering all subscribed events but you only spend Claude tokens on the ones you map. Subscribe selectively at dashboard.stripe.com/webhooks too.

Q: What about test mode? A: Use Stripe CLI: stripe trigger charge.dispute.created --forward-to localhost:8000/stripe/webhook. The CLI signs events with your test webhook secret and replays them locally.


Source & Thanks

Pattern compiled from Stripe Webhooks + Anthropic Tool Use.

Stripe MIT-licensed, Anthropic SDK MIT-licensed.

🙏

Source et remerciements

Pattern compiled from Stripe Webhooks + Anthropic Tool Use.

Stripe MIT-licensed, Anthropic SDK MIT-licensed.

Fil de discussion

Connectez-vous pour rejoindre la discussion.
Aucun commentaire pour l'instant. Soyez le premier à partager votre avis.

Actifs similaires