Wallet policies

Wallet controls, operation permissions, and policy audit visibility.

A wallet policy constrains how a custody wallet can move funds and execute supported operations. SDP starts from default allow, then applies the active wallet control profile and any policy fields you configure. Policies are managed via GET and PUT on /v1/payments/wallets/{walletId}/policies and enforced before each wallet operation executes.

Use policies for treasury wallets, automated payout wallets, or any custody wallet where unbounded outbound flow would be a liability.

Reading a wallet's policies

Terminal
curl https://api.solana.com/v1/payments/wallets/wal_abc123/policies \
  -H "Authorization: Bearer sk_test_..."

The payload is wrapped in the standard data / meta envelope, with the policy under a policy key:

response.json
{
  "data": {
    "policy": {
      "walletId": "wal_abc123",
      "destinationAllowlist": ["7xKXz...9fGh", "5aBCd...2eFg"],
      "maxTransferAmount": "100.00",
      "maxDailyAmount": "1000.00",
      "defaultAction": "allow",
      "controlProfile": {
        "id": "wcp_abc123",
        "status": "active",
        "activeRevisionId": "wcpr_abc123",
        "revisionId": "wcpr_abc123",
        "revisionNumber": 3,
        "defaultAction": "allow",
        "rules": [
          {
            "id": "approval-required",
            "kind": "approval",
            "families": ["payment", "ramp"],
            "action": "approval_required",
            "name": "Approval checks"
          }
        ],
        "providerMappingStatus": "not_applicable",
        "createdAt": "2026-05-14T08:00:00Z",
        "updatedAt": "2026-05-14T10:15:00Z",
        "activatedAt": "2026-05-14T10:15:00Z"
      },
      "audit": {
        "recentEvaluations": [
          {
            "walletOperationId": "wop_abc123",
            "policyEvaluationId": "peval_abc123",
            "operationFamily": "payment",
            "operationType": "payment_transfer_execute",
            "asset": "USDC",
            "amount": "100.00",
            "destination": "7xKXz...9fGh",
            "status": "pending_approval",
            "decision": "approval_required",
            "reasonCode": "wallet_policy_match",
            "reason": "Payment matched approval policy.",
            "requiresApproval": true,
            "approvalRequestId": "appr_abc123",
            "operationCreatedAt": "2026-05-14T10:20:00Z",
            "operationUpdatedAt": "2026-05-14T10:20:00Z",
            "evaluatedAt": "2026-05-14T10:20:00Z"
          }
        ]
      },
      "createdAt": "2026-05-14T08:00:00Z",
      "updatedAt": "2026-05-14T10:15:00Z"
    }
  },
  "meta": { "requestId": "req_...", "timestamp": "2026-05-18T00:00:00.000Z" }
}

Amount fields are UI-unit decimal strings ("100.00" means 100 tokens, regardless of the mint's decimals — matching the transfer-request convention). The configured value is a single threshold, but enforcement is per token: maxDailyAmount sums each token's outbound transfers separately within the day window, so a wallet with maxDailyAmount: "1000" can send up to 1000 USDC and 1000 SOL in the same UTC day — there is no aggregate cap across mints. If you need different per-token thresholds (e.g., 10,000 USDC daily but 50 SOL daily), partition into separate wallets.

Default allow and active controls

Wallet policy evaluation starts from default allow. If no wallet control profile exists, supported wallet operations continue to run unless another product-specific check rejects them. When you activate a control profile, SDP evaluates its rules first and returns the resulting decision as one of allow, deny, approval_required, provider_approval_required, review, or not_evaluated.

The controlProfile object shows the active immutable revision. Treat revisionId and revisionNumber as audit references: a policy evaluation records which revision was active when the wallet operation was checked.

Dashboard actions

The dashboard authoring flow exposes three actions: Allow, Deny, and Require approval. Choose Require approval when an operator must decide whether an operation can continue; it holds the operation and creates an Approval Request.

review remains a possible API evaluation result for legacy or safety paths, such as an unrecognized policy rule. It is visible in audit records, but it is not a dashboard authoring option or a separate human-review workflow. Do not use it to model operator approval; use approval_required instead.

Operation permissions

Wallet policies can match operations at two levels:

  • Operation family rules apply to every evaluated operation in a broad product area.
  • Operation type rules match one exact operation identifier and take precedence when you need a narrower decision.

These are wallet-policy selectors, not API-key permissions. API-key permissions such as payments:write and tokens:write determine whether a caller may invoke an endpoint; operation permissions determine what the selected wallet may do after the request is authorized. The wallet policy is the baseline: an API key policy can narrow what a key may do, or route an operation into approval, but it can never expand past what the wallet policy allows. See Manage API keys for caller permissions.

Operation families

FamilyDescriptionCurrent evaluated operations
paymentOutbound single and batch payments.Single and batch transfer execution
rampFiat-to-crypto and crypto-to-fiat ramp activity.On-ramp and off-ramp quote creation
issuanceToken lifecycle actions signed by the wallet.Mint, burn, seize, and authority-update execution
programOn-chain program interactions signed by the wallet.Earn vault deposits
transferGeneric direct-transfer family reserved for operations outside the payment workflow.No current public operation type

Family values with no current public operation type are valid policy values, but they do not match a public SDP flow until an operation is emitted in that family.

Exact operation types

Operation typeFamilyWhat it controls
payment_transfer_executepaymentExecutes one outbound payment transfer from the wallet.
payment_transfer_batch_executepaymentExecutes an outbound batch and evaluates the aggregate amount before submission.
ramp_onramp_quoterampCreates an on-ramp quote that delivers crypto to the destination wallet.
ramp_offramp_quoterampCreates an off-ramp quote that draws crypto from the source wallet.
issuance_mint_executeissuanceExecutes a token mint using the configured signing wallet.
issuance_burn_executeissuanceExecutes a token burn using the configured signing wallet.
issuance_force_burn_executeissuanceForce-burns tokens from a holder account.
issuance_seize_executeissuanceSeizes tokens into the issuer-controlled destination.
issuance_update_authority_executeissuanceChanges a token authority using the current authority wallet.
earn_vault_depositprogramDeposits custody-wallet assets into a supported non-custodial Earn vault.
recurring_payment_createpaymentCreates a recurring payment schedule.
recurring_payment_updatepaymentUpdates a recurring payment schedule.
recurring_payment_collectionpaymentCollects a due recurring payment.

Operation-type matching is exact and case-sensitive. The API accepts only the supported identifiers above, so a rule cannot be activated for an operation SDP never emits.

Policy evaluation and audit records

Each supported action against the wallet is recorded as a Wallet Operation — the attempt, whatever the outcome — before evaluation runs, and its Policy Evaluation — the verdict, plus a point-in-time snapshot of the operation and the policy that decided it — is recorded once SDP decides what should happen. audit.recentEvaluations exposes the most recent decisions for the wallet so support and customer operators can review:

  • what operation was evaluated (walletOperationId, operationFamily, operationType)
  • what decision SDP made (decision, reasonCode, reason, requiresApproval)
  • whether an approval request was created (approvalRequestId)
  • when the operation was created, updated, and evaluated

Use these records to explain allow, deny, approval-required, and review outcomes. They are an operational audit summary, not the full raw provider payload.

Provider mapping status

providerMappingStatus describes whether an SDP policy revision has also been mapped into a custody or ramp provider's native policy system. Current payment wallet controls are SDP-enforced first. A value of not_applicable means there is no provider-native mapping for that revision. pending, partial, or failed mean SDP still evaluates the operation, but you should not assume the same rule has been fully synced to the provider.

Destination allowlist

This is a wallet-policy control on outbound destinations — distinct from a token's Token Access Control allowlist, which gates who may hold the token itself. It is a list of on-chain destination addresses (32-44 character base-58 Solana pubkeys). When set, POST /v1/payments/transfers rejects any destination not in the list.

  • Maximum entries: 500 addresses per wallet.
  • Each entry is a single on-chain address — no patterns, ranges, or address books.
  • Empty allowlist ([]) means no destination restriction. To enable enforcement, populate at least one entry.

The allowlist exists to bound risk on automated wallets (a hot wallet that should only ever pay a known set of counterparties) and to enforce treasury controls (only the corporate cold-storage address can drain the operating wallet).

Transfer limits

A transfer limit always targets one Policy Asset — identified by mint, with native SOL under its well-known mint — never a blanket cap across assets. Two limits, both optional:

  • maxTransferAmount — per-transfer cap. Any single POST /v1/payments/transfers exceeding this amount is rejected. Compared against the request's amount directly, so it is naturally per-token.
  • maxDailyAmount — UTC-calendar-day cap, enforced per token. The day window resets at 00:00 UTC; the projected total is the sum of that wallet's outbound transfers for the same token in the current day (pending/processing/confirmed/finalized) plus the new request's amount. Different tokens are summed independently — see the per-token note under Reading a wallet's policies.

Set both for defense in depth: a per-transaction cap that catches obvious mistakes, plus a daily cap that bounds blast radius if many small transfers are submitted in a coordinated attack.

Updating policies

PUT /v1/payments/wallets/{walletId}/policies has full-replace semantics — the request body becomes the new policy state in its entirety. To remove the allowlist, send destinationAllowlist: []. To drop a transfer limit, omit the field on the next PUT.

Terminal
curl -X PUT https://api.solana.com/v1/payments/wallets/wal_abc123/policies \
  -H "Authorization: Bearer sk_test_..." \
  -H "Content-Type: application/json" \
  -d '{
    "destinationAllowlist": [
      "7xKXz...9fGh",
      "5aBCd...2eFg"
    ],
    "maxTransferAmount": "100.00",
    "maxDailyAmount": "1000.00"
  }'

Because PUT is full-replace, treat policy edits like ordinary form submissions: read the current state, present it for editing, and PUT the complete updated object. Don't construct partial patches.

Interaction with compliance

Wallet policies enforce structural constraints (where funds can go, how much, how fast). They do not replace compliance screening. If your organization has compliance screening enabled, screening happens on the destination address as a separate check, in addition to the allowlist match. A transfer can be rejected by either layer; both must pass to reach the network.

See the Compliance API reference for address-screening flows that complement policies.

Is this page helpful?