> ## 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 proposal webhooks

> Events emitted as an approval proposal is created, approved, withdrawn, vetoed, executed, or expired.

Proposal events describe an approval workflow rather than a change to the account, so they are separate from the [account action events](/docs/business-accounts/webhooks/account-actions). They fire under the `businessAccount.proposal.*` namespace.

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

## Events

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 six 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>

<ParamField body="businessAccount.proposal.expired" type="object">
  Occurs whenever a proposal passes its `completeBy` deadline without executing
  or being vetoed. Carries `expiredAt`, the deadline the proposal missed. The
  event has no actor: the clock, not a member, closed the proposal.
</ParamField>

### Reading a proposal payload

`evaluationSummary` answers whether the change can go through. `ruleEvaluations` answers why, one entry per governance rule that applies.

<Warning>
  Do not add up the `requiredCount` values in `ruleEvaluations`. One approval counts toward every rule its approver is eligible for, so the total overstates what is needed. Check `evaluationSummary.isSatisfied` for completion, and `evaluationSummary.minimumAdditionalApprovals` for how many more people the change needs.
</Warning>

```json theme={"system"}
{
  "proposalId": "prp_9f3c",
  "businessAccountId": "ba_acme",
  "action": "removeMember",
  "initiatorUserId": "usr_lee",
  "targetUserId": "usr_dana",
  "completeBy": "2026-10-14T18:22:40.881Z",
  "changeSet": [{ "type": "removeMember", "userId": "usr_dana" }],
  "evaluationSummary": {
    "isSatisfied": false,
    "status": "pendingApprovals",
    "minimumAdditionalApprovals": 2
  },
  "currentApprovals": [],
  "pendingEligibleApprovers": ["usr_ray", "usr_kim", "usr_max", "usr_nia"],
  "mandatoryApprovers": ["usr_ray"],
  "ruleEvaluations": [
    {
      "ruleId": "removeMember:offboard-dual-control",
      "governedAction": "removeMember",
      "isSatisfied": false,
      "requiredCount": 2,
      "contributingApprovers": [],
      "remainingEligiblePool": ["usr_ray", "usr_kim", "usr_max", "usr_nia"]
    },
    {
      "ruleId": "removeSignerFromWallet:share-revocation:wlt_treasury",
      "governedAction": "removeSignerFromWallet",
      "isSatisfied": false,
      "requiredCount": 1,
      "contributingApprovers": [],
      "remainingEligiblePool": ["usr_ray", "usr_kim", "usr_max"]
    }
  ]
}
```

Four fields repay a closer look:

* **`changeSet`** holds only what the person asked for. Removing a member also revokes their key shares, and that extra rule shows up as its own entry in `ruleEvaluations` with `governedAction: "removeSignerFromWallet"`, not as a second entry in `changeSet`.
* **`ruleId`** combines the rule's own name from the account's governance configuration with the action it governs, plus a wallet id when the rule targets one. A rule's name is unique only within one action, and a member's key shares span every wallet they sign on, so a rule revoking those shares produces one `ruleEvaluations` entry per wallet. Both parts of `ruleId` keep those entries apart.
* **`remainingEligiblePool`** can differ between rules. A rule limited to people who hold a key share on a particular wallet admits fewer people than a rule that does not. Send each person the rule they can act on.
* **`mandatoryApprovers`** names people, not roles: someone appears here when every way of satisfying the account's rules needs their approval specifically. `pendingEligibleApprovers` is the union of everyone still worth asking across every unsatisfied rule, and `mandatoryApprovers` is the ones among them who cannot be skipped.

<Note>
  Once a proposal reaches `vetoed`, `expired`, or `executed`, `ruleEvaluations`, `pendingEligibleApprovers`, and `mandatoryApprovers` all report empty. `currentApprovals` still holds the full record of who approved. `evaluationSummary.isSatisfied` reflects the final outcome (`true` only for `executed`) rather than the tally at the moment the account closed the proposal.
</Note>
