# Approvals

Source: https://docs.settlemint.com/docs/console/asset-lifecycle/custody-gateway-approvals
Set the custody gateway's approval rule, raise a held gateway change, and approve, run or cancel a change that an add-on approval holds.

The **Approvals** tab of the "Custody gateway" holds the gateway's approval rule and the proposals that wait for approval. The custody gateway is the add-on that screens client deposits and pays client withdrawals. An add-on approval holds one call to a checked function of an existing add-on until enough approvers have approved it. The role that may make the change is the requester's role, and the access manager is the contract that holds the add-on's roles and rules. You raise a held change where you make it, then approve, run or cancel it from [Actions](https://docs.settlemint.com/docs/console/essentials/actions) or from this tab. The [add-on approvals concept](https://docs.settlemint.com/docs/concepts/compliance-and-identity/addon-approvals) explains the model behind the rules.

An add-on approval does not hold the creation of a new airdrop, offering or other add-on. The custody gateway ships approval rules that hold the creation of a gateway only when it starts on a settings profile other than the add-on defaults. Creating a gateway on the defaults needs only the gateway "Manager" role, with no second approver. Airdrops and the other add-on entries are on [Airdrops](https://docs.settlemint.com/docs/console/asset-lifecycle/airdrops).

![Approvals tab of the custody gateway](https://docs.settlemint.com/docs/screenshots/console-custody-gateway-approvals.light.en.webp)

[Screenshot] The "Approvals" tab of the custody gateway shows the approval rule above the changes that wait for approval.

The "Require a second approval" section shows the status line, the switch, "Approvals per change" and "Save". Below it, "Waiting for approval" reads "No approvals pending" while no change is open, and the add-on's status, access manager and fee account follow.

## Before you start [#before-you-start]

- The custody gateway entry: the sidebar shows **Custody gateway** under "Asset lifecycle" to an Owner and a Platform admin, once the system has the custody gateway add-on.
- To approve or run a gateway change, you need the gateway "Manager" role. Every checked gateway function needs that role, and the rules the Console sets make the requester's role the approver.
- To change the approval rule itself, you need the "Gateway permission manager" role. Saving the rule, and approving or running a held rule change, needs that role. A Permission manager or System manager role, or a role on the add-on's access manager, also lists a proposal for you, which on its own does not let you approve it. Grant gateway roles on the "Add-on roles" tab of [Roles and permissions](https://docs.settlemint.com/docs/console/organisation-settings/admins-and-roles).
- **Your wallet verification.** Each approval, run and cancellation is signed by your own wallet and asks for your PIN.

## Steps [#steps]

The tab lists the add-on's state, "Active" or "Paused", its access manager and its fee account below the approval rule and its open proposals. When the add-on has no access manager, the tab shows only that list: **Access manager** reads "Not set", and a line reads "This add-on has no access manager, so its changes cannot require approval."

### Set the approval rule [#set-the-approval-rule]

Under **Require a second approval**, the status line says what is in force:

- "On. Every gateway change waits for approval, and so does turning this off."
- "Off. Gateway managers make changes without approval."
- "Approvals required, but anyone with the Gateway permission manager role can turn them off without approval."
- "Custom. Some changes have their own approval rule. Saving here sets one rule for all of them."

The third line means the rule does not cover changes to itself, so one holder of the **Gateway permission manager** role can turn every hold off without a second approval. Saving the rule again covers that change too.

Turn the switch on and enter **Approvals per change**, from 1 to 32. Select "Save" and confirm with your PIN. The Console sets one rule on every checked gateway function and on the rule-setting function of the access manager itself, so turning the rule off later needs approval too. The approver is anyone with the requester's role, and the approval window keeps the longest window already set, or seven days when none is set. A rule can cover more functions than one transaction can set, so the Console saves it in several transactions, each with its own PIN prompt: "This rule is saved in 3 steps. Each step asks you to confirm again." While the network is confirming a save, the tab reads "The last save was sent, but the new rule has not shown up yet. Refresh to check again."

The rule counts wallets, so grant the **Manager** and "Gateway permission manager" roles to one wallet per person. A person who holds either role on two wallets can meet a one-approval rule alone by calling the chain directly, and with the "Gateway permission manager" role that includes turning the rule off.

When the rule is already on, saving a new one is itself a held change, and the Console shows the block of the next step.

### Raise a proposal [#raise-a-proposal]

Make the change as usual, for example on the gateway's "Coins" or "Profiles" tab, or on a deposit or withdrawal. When the change is held, the Console does not send it. It shows **This change needs approval first** with "This change takes effect only after others approve it. Ask for approval, then run it from the request once it is approved." The block names the "Contract", and "Technical details" shows the "Function" selector and the "Call hash".

Select **Ask for approval** and confirm with your PIN. The Console reads "Approval requested. The approvers see it in their actions inbox and on the Approvals tab." You are now the proposer, and the request appears in your Actions inbox as "Waiting for other approvers".

A withdrawal request at or above the gateway's instant limit waits in review for the gateway managers' approval, and the record lists **Approval** under "Still to do". Give it with "Approve" on the request's row or in its record on the gateway's "Withdrawals" tab, and confirm with your PIN. While the approval rule is on, "Approve" shows the block above instead of sending: ask for approval, and once the proposal has its approvals, select "Run change" on it, or "Approve" on the request again. The request then waits for funds, or stays in review until the screener has cleared it too. A frozen request offers no "Approve" while it stays frozen, and a request whose client has a revoked or expired compliance claim is refused until the claim is restored.

### Raise an approved source or payout address for the client [#raise-an-approved-source-or-payout-address-for-the-client]

On the gateway overview, the **Whitelist** panel lists the addresses of the client the gateway belongs to, with when each was proved and whether it is an approved source or payout address. Select "Add source" or "Add payout" on a row, or "Raise removal" on a revoked entry that is still an approved source, and confirm with your PIN. The Console sends the same request as the direct routes, so a held change shows the approval block above.

### Review and approve [#review-and-approve]

An approver finds the proposal as an "Add-on approval" row in [Actions](https://docs.settlemint.com/docs/console/essentials/actions), under "Needs you", reading "Waiting for your approval" with "1 of 2 approvals" or similar. The same proposal is listed under "Waiting for approval" on the gateway's "Approvals" tab. Select **Review** in either place.

The **Approval request** sheet says "This add-on change runs only after it has the approvals it needs." It shows:

- the change in words, such as **Release a frozen withdrawal**
- the **Contract** and the call's arguments with labels
- **Requested by**
- **Expires**
- the **Approvals** progress
- **Approved by**

A change to an approval rule also shows the rule it writes:

- **Actions covered**
- **Approvals required**, or "No second approval"
- **Approval window**
- **Approver**
- **Back to the default**, where it clears an override

Select **Approve** and confirm with your PIN. The Console reports "Approval recorded." and the sheet then reads "You approved this. Waiting for other approvers." until the count is met.

### Run the change [#run-the-change]

Once the proposal has its approvals, the row reads "Ready to run" under "Needs you" for anyone whose wallet holds the requester's role, and the sheet reads "Approved. Someone with the requester's role can now run it." Select "Review", then **Run change**, and confirm with your PIN. The platform sends the approved call to the contract from your wallet. The Console reports "The change has run." and the proposal leaves the inbox.

The change runs once. If the call fails on chain, the proposal stays open with its approvals, and you can run it again.

### Cancel a request [#cancel-a-request]

The proposer, anyone who approved, and an approver who may still approve can cancel an open proposal that has not expired. Select "Review", then **Cancel request**. The Console asks you to confirm with "Cancel request" or "Keep request", then asks for your PIN. It reports "Request cancelled."

## What an add-on approval holds [#what-an-add-on-approval-holds]

A function that an add-on marks as checked asks the add-on's own access manager, the contract that holds the add-on's roles, before it runs. When it has a rule for that function, the call runs only after a proposal for the exact call data collects the approvals the rule needs. A function with no rule, or a rule of zero approvals, runs at once for a holder of its role. The Console calls that role the requester's role, and the API calls it the maker role.

| You want to | What the platform checks |
| --- | --- |
| Create a custody gateway for a client, or change one, for example approve a payout address, release a frozen withdrawal or approve a withdrawal at or above the instant limit | The gateway add-on's approval rule. A new gateway access manager starts with one approval and a seven-day window on every checked function. Creating a gateway is held only when it starts on a non-default settings profile, and a setting change is held only when it loosens the setting. |
| Create an offering or an XvP settlement | Your role. No add-on approval applies. Creating an offering also waits for a second organisation owner when the **Offerings** row under **Approvals** is on, as [Offerings](https://docs.settlemint.com/docs/console/asset-lifecycle/primary-offerings) describes. |
| Register an add-on factory, so that a new add-on type exists on the system | The system manager role. The write is in the `system-administration` protection group, so it waits for a second person when your organisation lists that group for maker-checker approval. |
| Change an offering's terms or its sale roles | The sale role the change needs. Terms are in the `token-administration` group and roles in the `role-management` group, held under the same maker-checker list. |

A held administrative change is a different mechanism from an add-on approval. It waits as an approval request in [Actions](https://docs.settlemint.com/docs/console/essentials/actions) until a second person decides it, and nothing reaches the chain before then. An add-on approval runs on chain, on the add-on's access manager.

The trading venue also marks some functions as checked: changing a price band, halting an instrument, delisting an instrument, pausing and resuming the venue, and busting a trade. Its factory sets no rule, so these run at once for the holder of the "Venue operator" role, of the "Venue admin" role for resuming the venue, or of the venue submitter role for a bust. The account that provisioned the venue holds the default administrator role on the venue's access manager, and it alone can set a rule, through the API. A proposal on any contract other than the custody gateway, its factory or the access manager reads "Unrecognized call" and has no "Approve" button.

## What approvers see [#what-approvers-see]

The Console and the Platform API treat every wallet you hold as one person, so the inbox row and the sheet follow your part in the proposal. The access manager on chain checks identities: it refuses an approval from any wallet registered to the proposer's identity, and counts one approval per identity. A wallet with no registered identity counts as a person of its own, and a Gateway permission manager can grant the approver role to more wallets, so review who holds that role.

| Your part | Inbox row | In the sheet |
| --- | --- | --- |
| You hold the approver role, and no wallet of yours proposed or approved | **Waiting for your approval**, under **Needs you** | **Approve** and **Cancel request** |
| You hold the approver role, but the Console cannot read the call | **Can't be approved here**, under **Needs you** | The **Function selector** and the raw **Call data**, and no button |
| You proposed it, or you already approved it, and it lacks approvals | **Waiting for other approvers**, under **Waiting on others** | **Cancel request** |
| A wallet of yours holds the requester's role and the approvals are met | **Ready to run**, under **Needs you** | **Run change**, and **Cancel request** when you proposed it or approved it |

Each row shows the approvals so far out of those needed, and its **Review** button opens the sheet, including for rows under "Waiting on others". You see one row per proposal. A person who proposed from one wallet and holds the approver role on another sees "Waiting for other approvers", because the platform never asks a person to approve their own proposal.

The platform lists a proposal for you only when you hold the Permission manager or System manager role, a role on that add-on's access manager, or the system role the access manager is bound to. The Investor portal lists the same rows without **Review**. It tells an approver "Waiting for your approval in the Console", or "This call can't be read, so it can't be approved here." for a call it cannot read.

## You should see [#you-should-see]

- Open the gateway's **Approvals** tab. A proposal that ran or was cancelled is gone from "Waiting for approval", and "No approvals pending" shows when none is left.
- Opening a closed proposal reads "This change has run." or "This request was cancelled."

## Troubleshooting [#troubleshooting]

A refused proposal write answers `422` with `platform.addonApprovals.PROPOSAL_REFUSED`, DALP-12408, and its `reason` names the cause. Change the wallet, the proposal or the call before a retry, except where the table says to send the request again. The platform simulates every write from your wallet before it queues anything, so a refusal at that stage sends nothing. A write that passes can still fail on chain if the state changes before it is mined. The sheet shows the error's title and the fix in place.

| Code or reason | What happened | What to do |
| --- | --- | --- |
| `proposer-cannot-approve` | A wallet of yours raised the proposal, or you proposed it through the platform while working as any participant. | Ask another approver. |
| `already-approved` | A wallet of yours already approved it, or your earlier approve request is still being queued. | Wait for other approvers. Do not approve again from another wallet. |
| `checker-unauthorized` | The wallet does not hold the role the action needs, for example the approver role. | Act from a wallet that holds the role, or ask a gateway permission manager to grant it. |
| `not-maker` | **Run change** came from a wallet without the requester's role. | Run it from a wallet that holds the requester's role. |
| `not-approved` | The proposal does not have the approvals it needs yet. | Wait for the remaining approvals. |
| `proposal-not-open` | The proposal already ran or was cancelled. | Nothing to do. Raise a new proposal if you still need the change. |
| `proposal-expired` | The approval window ended. The sheet shows **Expired** and no button. | Raise the change again. |
| `proposal-superseded` | The rule's approver role changed after the proposal was made. | Raise the change again under the new rule. |
| `proposal-already-open` | An open proposal for this exact call already exists. | Approve or run the existing one. |
| `target-not-managed` | The contract does not use this access manager for its checks. | Propose on the access manager the contract names. |
| `invalid-proposal-call` | The call is too short to name a function, or it targets the access manager with a call other than setting a rule. | Correct the call data. |
| `refused` | The access manager refused the call for another reason. The error's `revert` carries the contract error. | Read the revert, then send the request again. |
| `platform.addonApprovals.APPROVAL_NOT_REQUIRED`, DALP-12407, `422` | No rule holds this call, so there is nothing to propose. | Send the call to the contract directly from a wallet that holds its role. |
| `platform.addonApprovals.APPROVAL_REQUIRED`, DALP-12411, `422` | Setting a rule is itself held. The error carries the call to propose: the access manager as target, the default administrator role and the call data. | Propose that call, collect the approvals, then run it or send the request again. |
| `GATEWAY_APPROVAL_REQUIRED`, DALP-9821, `422` | A gateway write is held. The error lists the calls to propose. The Console shows these as **This change needs approval first**. | Select **Ask for approval**. |

A proposal you cannot see answers `404` with `platform.http.NOT_FOUND`. Approving a proposal whose window ends before the next block answers `proposal-expired`. A rule write from a wallet with no administrator authority on the access manager answers `403` with `platform.http.ACCESS_DENIED`, DALP-12252. The full list of codes is in the [platform API error reference](https://docs.settlemint.com/docs/api-reference/errors/platform-api-error-reference).

## API routes [#api-routes]

Proposals and rules live under the access manager of the add-on. The custody gateway's "Approvals" tab shows its **Access manager** address.

| Task | Route |
| --- | --- |
| List proposals | `GET /api/v2/system/addon-access-managers/{accessManagerAddress}/proposals` |
| Read one proposal | `GET /api/v2/system/addon-access-managers/{accessManagerAddress}/proposals/{proposalId}` |
| Propose a call | `POST /api/v2/system/addon-access-managers/{accessManagerAddress}/proposals` |
| Approve a proposal | `POST …/proposals/{proposalId}/decisions` with `{"decision":"approve"}` |
| Run or cancel a proposal | `POST …/proposals/{proposalId}/transitions` with `{"transition":"execute"}` or `{"transition":"cancel"}` |
| List the rules in force | `GET /api/v2/system/addon-access-managers/{accessManagerAddress}/action-configs` |
| Set a rule for up to 50 functions | `POST /api/v2/system/addon-access-managers/{accessManagerAddress}/action-configs` |

The proposal list filters by `target`, `status` and `viewer`. `viewer=approver` keeps the open proposals you may still approve, and `viewer=maker` keeps the ones you proposed or may run. Each write is signed by your own wallet, needs your wallet verification and takes an `Idempotency-Key`. The request and response fields of each route are in [the v2 API reference](https://docs.settlemint.com/docs/api-reference/v2).
