Integration
Webhooks
AgentPay receives Stripe webhooks for card, authorization, and payment-intent events. The handler lives at /v1/webhooks/stripe. If you need to forward events to your own system, you can subscribe to an outbound webhook — coming in v0.1. For now, here's exactly what we consume and how.
Events we handle
| Event | What we do |
|---|---|
| setup_intent.succeeded | Safety-net: records the saved PM if the SetupIntent flow was interrupted before /api/setup/complete ran. |
| payment_intent.amount_capturable_updated | Marks the card_hold row as requires_capture in our DB. |
| payment_intent.succeeded | Marks the card_hold as captured. |
| payment_intent.canceled | Marks the card_hold as canceled. |
| issuing_authorization.created | A card was used at a merchant. We capture the PaymentIntent hold and mark the card USED. |
| issuing_card.updated | Syncs card status (active / canceled) from Stripe to our DB. |
| account.updated | No-op. We read live state when we need it. |
Signature verification
Every webhook is verified via stripe.webhooks.constructEvent using the STRIPE_WEBHOOK_SECRET env var. Events with invalid signatures return 400 invalid_request. In development, if no secret is set, we parse the body directly — do not do this in production.
Idempotency
Every event is stored in webhook_events keyed by the Stripe event id with a unique constraint. Duplicate deliveries short-circuit with {"ok":true,"duplicate":true}. This matters because Stripe retries a webhook up to 3 days if you return non-2xx.
Testing webhooks locally
Use the Stripe CLI to forward events to your dev server:
# Forwards platform-level events to your dev server
stripe listen --forward-to http://localhost:3100/v1/webhooks/stripe
# In another tab, trigger a test event
stripe trigger payment_intent.succeededRetry behavior
If our handler returns non-2xx, Stripe retries with exponential backoff for up to 3 days. Our handler wraps dispatch in try/catch and returns 500 on any exception — Stripe will retry until success. The idempotency guard prevents double-processing.