Skip to content

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:

ScopeWhat it coversAPI connect param
Your accountEvents triggered by resources in your platform accountfalse
Connected accountsEvents 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

EventScopeDescription
account.updatedConnected accountsMonitor requirement changes and status changes. Fire on all connected accounts.
account.application.deauthorizedConnected accountsConnected account disconnected from your platform. Trigger cleanup. Available for Standard accounts.
account.external_account.updatedConnected accountsBank account or debit card updated. May impact payouts.
person.updatedConnected accountsPerson on the account was updated. Use with the Persons API to track requirement changes.
payment_intent.succeededConnected accounts (direct) or Your account (destination)Payment succeeded. Scope depends on charge type.
payout.failedConnected accountsPayout failed — external account is now disabled.
balance.availableYour accountPlatform 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/account

Trigger test events:

bash
# For a connected account
stripe trigger --stripe-account acct_CONNECTED account.updated

# For your account
stripe trigger payment_intent.succeeded

livemode 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.

Stripe API Reference - Self-contained docs reference, refreshed 2026-05-18