# Auto-withdraw

Pay Trading balances out to whitelisted external addresses on a UTC schedule with AutoWithdrawService rules, and follow each run.

An auto-withdraw rule pays an account's Trading balance out to external addresses on a schedule. Each run withdraws every configured Asset, in kind, to its own destination through the normal Trading withdrawal path. A rule belongs to one account: the Main Account or one Subaccount.

Use it to sweep profits or settlement balances to a treasury wallet without submitting each withdrawal yourself. API keys can read rules and runs but can never create, change, or disable one.

> **Destinations must be explicitly whitelisted**
>
> Every destination must be listed on the account's external withdraw whitelist when the rule is written and again whenever a run pays it. A disabled whitelist requirement does not count as listed. If a destination leaves the whitelist, the rule pauses itself.

## Methods

`AutoWithdrawService` is available over ConnectRPC only; it has no REST routes.

- **[`SetAutoWithdrawRule`](https://testnet.polyester.com/docs/api-docs/connect/chain.withdraw.v1.AutoWithdrawService/SetAutoWithdrawRule)** · Session with a fresh step-up\
  Create a rule, or replace its latest version.
- **[`DisableAutoWithdrawRule`](https://testnet.polyester.com/docs/api-docs/connect/chain.withdraw.v1.AutoWithdrawService/DisableAutoWithdrawRule)** · Session with a fresh step-up\
  Stop the rule.
- **[`GetAutoWithdrawRule`](https://testnet.polyester.com/docs/api-docs/connect/chain.withdraw.v1.AutoWithdrawService/GetAutoWithdrawRule)** · Session, or API key with `READ_AUTO_WITHDRAW`\
  Read the latest rule version.
- **[`ListAutoWithdrawRuns`](https://testnet.polyester.com/docs/api-docs/connect/chain.withdraw.v1.AutoWithdrawService/ListAutoWithdrawRuns)** · Session, or API key with `READ_AUTO_WITHDRAW`\
  Page through runs and their payouts, newest first.

Omit `subaccountId` to target the Main Account. There is no separate delete: disabling creates a new, disabled version with no destinations. An API key that calls `SetAutoWithdrawRule` or `DisableAutoWithdrawRule` is refused with HTTP `403` and `AUTH_SESSION_KIND_NOT_ALLOWED`, whatever its policy.

## Who can manage a rule

Changing a rule needs a person on an interactive session who holds both rights on the account:

- managing auto-withdraw (`MANAGE_AUTO_WITHDRAW`)
- withdrawing externally (`EXTERNAL_WITHDRAW`)

On the Main Account that is its owner. On a Subaccount, the `OWNER`, `ADMIN`, and `TREASURY` roles qualify, and the Subaccount policy must allow both actions. Every create, change, and disable needs a fresh single-use step-up in `X-Auth-Step-Up`. See [Multi-factor authentication](https://testnet.polyester.com/docs/developer-docs/authentication-security/multi-factor-authentication).

The rule keeps needing that authority while it pays. If the person who set it loses either right, the rule pauses with reason `authority_revoked`.

## Write a rule

1. Whitelist each destination

   Add every destination address to the account's external withdraw whitelist on-chain, with GuardSigner approval. Check it with [`GetWithdrawWhitelistView`](https://testnet.polyester.com/docs/api-docs/connect/auth.v1.AddressBookService/GetWithdrawWhitelistView). See [GuardSigner](https://testnet.polyester.com/docs/developer-docs/authentication-security/guard-signer).

2. Read the current version

   Call `GetAutoWithdrawRule`. Use `0` as `expectedVersion` when no rule exists, otherwise the latest `version`.

3. Set the rule

   Call `SetAutoWithdrawRule` with a fresh step-up, the `schedule`, one to 32 `destinations`, and an optional `minUsdValueE18`. A stale `expectedVersion` fails with `ERROR_CODE_CONFLICT`, so concurrent changes never overwrite each other.

Each destination pays one Asset, in kind, to one whitelisted address; the fields are in the [API reference](https://testnet.polyester.com/docs/api-docs/connect/chain.withdraw.v1.AutoWithdrawService/SetAutoWithdrawRule). When you build them:

- Add at most one destination per Asset. Take `destinationChainId` from the deposit and withdraw configuration. Responses return each address in its canonical form.
- `minAmountE18` must be at least the route's withdrawal minimum. A run pays the Asset only once its payable amount reaches this.
- `maxAmountE18` caps one run's payout. A larger balance pays the ceiling, and the rest stays on the account for a later run.

`minUsdValueE18` adds a USD floor per payout, valued at the current index price. An Asset with no current price is skipped with `price_unavailable` and its balance stays for a later run.

### Schedules

Every slot is UTC, with no daylight-saving shift. Set only the fields your cadence uses; anything else fails with `ERROR_CODE_INVALID_AUTO_WITHDRAW_SCHEDULE`.

| Cadence    | Fields                                                                                                    |
| ---------- | --------------------------------------------------------------------------------------------------------- |
| `HOURLY`   | `minuteOfHour`. Pays only Assets that reached their minimums; an hour with nothing to pay records no run. |
| `DAILY`    | `minuteOfDay`.                                                                                            |
| `WEEKLY`   | `weekday` and `minuteOfDay`.                                                                              |
| `BIWEEKLY` | `weekday`, `minuteOfDay`, and `anchorDate`, a date in a week the rule runs. Weeks start on Monday.        |
| `MONTHLY`  | `dayOfMonth` and `minuteOfDay`. A month without that day runs on its last day.                            |

## Read rules and runs

`GetAutoWithdrawRule` returns `{}` when the account has never had a rule. Treat an empty response as "no rule", not as an error.

Each rule version reports `enabled`, `schedule`, `destinations`, and `pause` when the rule stopped itself. Pause reasons are `destination_not_whitelisted`, `destination_invalid`, and `authority_revoked`. Writing a new version resumes the rule.

`ListAutoWithdrawRuns` returns one run per scheduled slot with a step per destination. Run states are `RUN_PLANNED`, `RUN_PAYING`, `RUN_COMPLETED`, `RUN_PARTIALLY_FAILED`, `RUN_FAILED`, and `RUN_SKIPPED`. Each step pays the available balance up to the ceiling, rounded down to whole on-chain units of the destination token; the remainder stays on the account. Failed payouts return their funds to the account. A submitted step carries an `intentId` that follows the same lifecycle as a manual withdrawal; see [Withdraw from Trading](https://testnet.polyester.com/docs/developer-docs/funds-transfers/withdraw-from-trading).

### API key reads

An API key reads rules and runs only when its policy grants `READ_AUTO_WITHDRAW` (or `MANAGE_AUTO_WITHDRAW`, which includes it), and only for its own account. A Main Account key reads the Main Account; a Subaccount-scoped key reads its Subaccount. Both refusals return `PERMISSION_DENIED`: a missing action names it in the message, and naming another account reports a target scope mismatch. See [API key and Subaccount policies](https://testnet.polyester.com/docs/developer-docs/account/api-key-policies).

Full field schemas are in the [API reference](https://testnet.polyester.com/docs/api-docs), and error codes under [Withdrawals](https://testnet.polyester.com/docs/developer-docs/error-handling/error-codes#withdrawals).
