> ## Documentation Index
> Fetch the complete documentation index at: https://docs.stackone.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Handle Account Events

> Subscribe to and handle StackOne's account lifecycle webhooks in your backend.

Account events let your backend react to account lifecycle — a user connecting or disconnecting — instead of polling. This is the implementation walkthrough; for the event catalog and payload fields, see [Platform Events](/platform-api/platform-events) in the reference.

<Steps>
  <Step title="Create a webhook endpoint">
    Expose an HTTPS endpoint in your backend that returns `200` quickly. On the [Webhooks](https://app.stackone.com/webhooks) page, click **Add Webhook** and register its URL. From the **Signing secrets** tab, copy the signing secret if you plan to verify signatures (recommended).
  </Step>

  <Step title="Subscribe to account events">
    Account events are subscribed **on the webhook itself** — not on a connector profile (that's for connector events). When you add or edit the webhook on the [Webhooks](https://app.stackone.com/webhooks) page, select the account events you want: `account.created`, `account.updated`, and `account.deleted`.

    Via the API, set the webhook's `events` array when you create it (`POST /webhooks`) or update it (`PATCH /webhooks/{id}`):

    ```json theme={null}
    {
      "webhook_url": "https://your-app.com/webhooks/stackone",
      "label": "account-lifecycle",
      "events": ["account.created", "account.updated", "account.deleted"]
    }
    ```
  </Step>

  <Step title="Verify the signature">
    Each delivery is signed with HMAC-SHA256 over the raw request body, base64url-encoded, in the `x-stackone-signature` header. Verify it against your signing secret before trusting the payload:

    ```javascript theme={null}
    import { createHmac, timingSafeEqual } from 'crypto';

    function isSignatureValid(signature, rawBody, signingSecret) {
      if (!signature) return false;
      const expected = createHmac('sha256', signingSecret)
        .update(rawBody)
        .digest('base64url');
      const a = Buffer.from(expected);
      const b = Buffer.from(signature);
      return a.length === b.length && timingSafeEqual(a, b);
    }
    ```

    Hash the **raw request bytes** (not a re-serialized JSON string), and compare in constant time. See [Verifying webhook signatures](/connect/webhooks#verifying-webhook-signatures) for detail and secret rotation.
  </Step>

  <Step title="Handle the event">
    Switch on the `event` field and act on the account. The payload carries `account_id`, `provider`, and the `origin_*` identifiers — see the [payload reference](/platform-api/platform-events#payload).

    ```javascript theme={null}
    import express from 'express';
    const app = express();

    app.post('/webhooks/stackone', express.raw({ type: 'application/json' }), (req, res) => {
      const signature = req.headers['x-stackone-signature'];
      if (!isSignatureValid(signature, req.body, process.env.STACKONE_SIGNING_SECRET)) {
        return res.status(401).send('invalid signature');
      }

      const { event, account_id, provider } = JSON.parse(req.body);

      switch (event) {
        case 'account.created':
          // kick off an initial sync for the new account
          break;
        case 'account.updated':
          // re-check scopes or resume a paused sync
          break;
        case 'account.deleted':
          // revoke downstream access and clean up the stored account_id
          break;
      }

      res.sendStatus(200);
    });
    ```
  </Step>
</Steps>

<Tip>
  [Auth Links](/embed/account-linking/auth-link) have no frontend callback — `account.created` is how you know an account was linked.
</Tip>
