Connect Webhooks Fresh
Set up and handle webhooks for Connect integrations, including account-level and connected account events.
Two webhook scopes
Connect webhooks listen for events in two scopes:
| Scope | What it covers | API connect param |
|---|---|---|
| Your account | Events triggered by resources in your platform account | false |
| Connected accounts | Events triggered by resources in connected accounts (direct charges, account updates) | true |
Set up separate webhook endpoints for each scope in Dashboard → Workbench → Webhooks (or via API).
Connected account events
Every event for a connected account includes a top-level account field identifying the connected account:
json
{
"id": "evt_123",
"livemode": true,
"object": "event",
"type": "customer.created",
"account": "acct_CONNECTED_ACCOUNT_ID",
"pending_webhooks": 2,
"created": 1349654313,
"data": { ... }
}Because the connected account owns the triggering object, you must make API requests for that object using the connected account's ID (Stripe-Account header).
Key events for Connect
| Event | Scope | Description |
|---|---|---|
account.updated | Connected accounts | Monitor requirement changes and status changes. Fire on all connected accounts. |
account.application.deauthorized | Connected accounts | Connected account disconnected from your platform. Trigger cleanup. Available for Standard accounts. |
account.external_account.updated | Connected accounts | Bank account or debit card updated. May impact payouts. |
person.updated | Connected accounts | Person on the account was updated. Use with the Persons API to track requirement changes. |
payment_intent.succeeded | Connected accounts (direct) or Your account (destination) | Payment succeeded. Scope depends on charge type. |
payout.failed | Connected accounts | Payout failed — external account is now disabled. |
balance.available | Your account | Platform balance updated. Used after top-ups or to trigger manual transfers. |
Webhook handler example (Node.js)
javascript
const express = require('express');
const stripe = require('stripe')('sk_test_YOUR_KEY');
const app = express();
const endpointSecret = 'whsec_YOUR_ENDPOINT_SECRET';
app.post('/webhook/connect', express.raw({ type: 'application/json' }), (req, res) => {
let event;
try {
event = stripe.webhooks.constructEvent(
req.body,
req.headers['stripe-signature'],
endpointSecret
);
} catch (err) {
return res.status(400).send(`Webhook Error: ${err.message}`);
}
const connectedAccountId = event.account; // present on connected account events
switch (event.type) {
case 'account.updated':
const account = event.data.object;
// Check account.requirements.currently_due
// If non-empty and deadline approaching, send back through onboarding
break;
case 'account.application.deauthorized':
// Connected account disconnected
// Clean up your database records for connectedAccountId
break;
case 'payout.failed':
const payout = event.data.object;
// External account is now disabled
// Notify the connected account to update their bank details
break;
case 'payment_intent.succeeded':
const paymentIntent = event.data.object;
// Fulfill for connectedAccountId
break;
case 'account.external_account.updated':
const externalAccount = event.data.object;
// Bank account details changed — verify payout configuration
break;
}
res.send();
});Test webhooks locally
Use the Stripe CLI to forward events to your local server:
bash
# Connected account events
stripe listen --forward-connect-to localhost:4242/webhook/connect
# Your account events
stripe listen --forward-to localhost:4242/webhook/accountTrigger test events:
bash
# For a connected account
stripe trigger --stripe-account acct_CONNECTED account.updated
# For your account
stripe trigger payment_intent.succeededlivemode check
For production webhook endpoints, you receive both live and test webhooks. Always check event.livemode to determine if action is required:
javascript
if (!event.livemode) {
// test event — may not need real action
return res.send();
}v2 Account events
v2 Account objects trigger both v1 and v2 events with different scopes:
- v2 events (
v2.core.account.*) — use the Your account scope - v1 events (
account.updated) — use the Connected accounts scope
Even when triggered by the same v2 Account, the event version determines which scope applies.