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

# Business account webhooks

> Events emitted for business accounts: membership, signers, wallets, governance, and approval proposals.

Business account events fire under the `businessAccount.*` namespace. Business accounts group members, signers, and wallets under one account; these events cover changes to the account itself and to who can act on it.

For the shared event envelope, endpoint setup, retries, and signature verification, see [Setting up webhooks](/docs/platform/dashboard/webhooks/setup) and [Event delivery & best practices](/docs/platform/dashboard/webhooks/delivery-best-practices). The current list of event types is always available from the [event types endpoint](/docs/api-reference/events/get-event-types).

## Account and membership

<ParamField body="businessAccount.created" type="object">
  Occurs whenever a business account is created.
</ParamField>

<ParamField body="businessAccount.member.added" type="object">
  Occurs whenever a member is added to a business account. Carries the member's role.
</ParamField>

<ParamField body="businessAccount.member.roleChanged" type="object">
  Occurs whenever a member's role changes. Carries the new role.
</ParamField>

<ParamField body="businessAccount.member.removed" type="object">
  Occurs whenever a member is removed. Removing a member also revokes their key shares, so this is followed by one `businessAccount.signer.revoked` event for each wallet they signed on.
</ParamField>

<ParamField body="businessAccount.ownership.transferred" type="object">
  Occurs whenever ownership moves to another member. Carries both the previous and the new owner.
</ParamField>

<ParamField body="businessAccount.signer.added" type="object">
  Occurs whenever a member gains a key share on one of the account's wallets, making them a signer.
</ParamField>

<ParamField body="businessAccount.signer.revoked" type="object">
  Occurs whenever a signer's key share on a wallet is revoked. Also sent for each signer severed by a member removal or a wallet unlink.
</ParamField>

<ParamField body="businessAccount.wallet.linked" type="object">
  Occurs whenever a wallet comes under the account's control.
</ParamField>

<ParamField body="businessAccount.wallet.unlinked" type="object">
  Occurs whenever a wallet leaves the account. Every signer on that wallet loses their key share, so this is accompanied by a `businessAccount.signer.revoked` event for each.
</ParamField>

<Note>
  `businessAccount.updated` is emitted when the account's own details change,
  such as its name, but is not selectable in the event types catalog today.
</Note>

## Governance and roles

An account's governance section holds the approval rules, and its roles decide what a member can do. Both live in the account's signed document rather than in a record you can read back, so these three events carry the resulting document `version`.

<ParamField body="businessAccount.governance.updated" type="object">
  Occurs whenever the approval rules change. Carries `governedActions`, the list of actions that now have a rule, and not the rules themselves (a rule names the users and roles that can approve). Read the governance section through the API to get the new rules.
</ParamField>

<ParamField body="businessAccount.role.defined" type="object">
  Occurs whenever a custom role is defined. Carries the role name and `inherits`, the built-in role it takes its permissions from.
</ParamField>

<ParamField body="businessAccount.role.deleted" type="object">
  Occurs whenever a custom role is deleted. A role cannot be deleted while a member still holds it, so no member loses access as a result.
</ParamField>

## Approvals

An account can require approvals before a change applies. A change that needs them does not take effect immediately: a **proposal** opens, collects approvals, and applies once its requirements are met. These five events cover that lifecycle.

<Note>
  An approved change sends **two** events: the proposal event below, and the ordinary account event for the change itself. A governed member removal sends both `businessAccount.proposal.executed` and `businessAccount.member.removed`. Subscribe to the family you need: the proposal events for approval workflow, the account events for keeping your own records in step.
</Note>

<ParamField body="businessAccount.proposal.created" type="object">
  Occurs whenever a change needs approvals and a proposal opens. Carries who to ask, and how many people are needed.
</ParamField>

<ParamField body="businessAccount.proposal.approved" type="object">
  Occurs whenever an approval is recorded. Carries the approver and the time they signed.
</ParamField>

<ParamField body="businessAccount.proposal.approvalWithdrawn" type="object">
  Occurs whenever an approver takes back an approval they had given. This is the
  only proposal event that lowers the count, so a reader that tracks progress
  must handle it: a proposal can fall back below its requirements after it had
  met them.
</ParamField>

<ParamField body="businessAccount.proposal.vetoed" type="object">
  Occurs whenever a proposal is refused. A veto is final, whatever approvals the proposal already holds.
</ParamField>

<ParamField body="businessAccount.proposal.executed" type="object">
  Occurs whenever a proposal meets its requirements and the change applies.
</ParamField>

### Reading a proposal payload

`quorum` answers what the change needs. `dependencies` answers why, one entry per governance rule that applies.

<Warning>
  Do not add up the `approvalsRequired` values in `dependencies`. One approval counts toward every rule its approver is eligible for, so the total overstates what is needed. Use `quorum.effectiveRequiredApprovals`, which is the number of people the change actually needs.
</Warning>

```json theme={"system"}
{
  "proposalId": "prp_9f3c",
  "businessAccountId": "ba_acme",
  "action": "removeMember",
  "status": "awaitingApproval",
  "initiatorUserId": "usr_lee",
  "targetUserId": "usr_dana",
  "completeBy": "2026-10-14T18:22:40.881Z",
  "changeSet": [{ "type": "removeMember", "userId": "usr_dana" }],
  "quorum": {
    "effectiveRequiredApprovals": 2,
    "governingDependency": "offboard-dual-control",
    "approvedBy": [],
    "eligibleApprovers": ["usr_ray", "usr_kim", "usr_max", "usr_nia"],
    "mandatoryApproverRoles": ["compliance"]
  },
  "dependencies": [
    {
      "id": "offboard-dual-control",
      "requirementId": "offboard-dual-control",
      "changeType": "removeMember",
      "target": { "userId": "usr_dana" },
      "derived": false,
      "approvalsRequired": 2,
      "approvedBy": [],
      "satisfied": false,
      "eligibleApprovers": ["usr_ray", "usr_kim", "usr_max", "usr_nia"],
      "mandatoryApproverRoles": ["compliance"]
    },
    {
      "id": "share-revocation:wlt_treasury",
      "requirementId": "share-revocation",
      "changeType": "removeSignerFromWallet",
      "target": { "userId": "usr_dana", "walletId": "wlt_treasury" },
      "derived": true,
      "approvalsRequired": 1,
      "approvedBy": [],
      "satisfied": false,
      "eligibleApprovers": ["usr_ray", "usr_kim", "usr_max"]
    }
  ]
}
```

Three fields repay a closer look:

* **`changeSet`** holds only what the person asked for. Removing a member also revokes their key shares, and those extra changes appear as dependencies with `derived: true` rather than in `changeSet`.
* **`eligibleApprovers`** can differ between dependencies. A rule limited to people who hold a key share on a particular wallet admits fewer people than a rule that is not. Send each person the dependency they can actually act on.
* **`id`** is unique within the payload; `requirementId` is the rule's name from the account's own configuration. One rule applied to two wallets produces two dependencies that share a `requirementId`, so use `id` as the key.
