Esta página se muestra en inglés. Una traducción al español está en curso.
SkillsMay 8, 2026·4 min de lectura

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.

Listo para agents

Staging seguro para este activo

Este activo primero queda en staging. El prompt copiado pide inspeccionar los archivos staged antes de activar scripts, config MCP o config global.

Stage only · 29/100Política: staging
Superficie agent
Cualquier agent MCP/CLI
Tipo
Skill
Instalación
Stage only
Confianza
Confianza: Community
Entrada
Asset
Comando de staging seguro
npx -y tokrepo@latest install a78e66e5-8945-4e63-a57b-bfd75011a20d --target codex

Primero deja archivos en staging; la activación requiere revisar el README y el plan staged.

Introducción

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.

🙏

Fuente y agradecimientos

Pattern compiled from Stripe Webhooks + Anthropic Tool Use.

Stripe MIT-licensed, Anthropic SDK MIT-licensed.

Discusión

Inicia sesión para unirte a la discusión.
Aún no hay comentarios. Sé el primero en compartir tus ideas.

Activos relacionados