Sweeps Wallet On A Schedule

Schedule wallet sweeps that keep a reserve balance, calculate the sweep amount, and transfer funds to a bank account or parent wallet.


A scheduled trigger periodically reads a wallet's available balance, computes the amount above a reserve floor, and sweeps it elsewhere. The reserve protects against zero-balance transfers that would fail validation, and against draining the wallet below the per-transfer fee that PayMongo charges.

Problem

You operate a PayMongo wallet that accumulates balance over time and want to move it to a bank account (settlement) or to a parent wallet (consolidation) on a recurring cadence. The minimum reserve you keep on the source wallet must be honoured every run.

Workflow definition

version: 1
name: "daily-sweep-wallet-balance"
description: "Daily sweep from merchant wallet to bank, keeping a reserve"
steps:
    - name: "check_balance"
      get_wallet_balance:
        wallet_account: "${input.source_wallet}"
    - name: "calc"
      compute:
        outputs:
          # Keep PHP 100 (10000 centavos) on the source wallet to cover the transfer fee.
          min_reserve:  "10000"
          balance:      "steps.check_balance.output.available_balance"
          sweep_amount: "monetary_max(balance - min_reserve, 0)"
    - name: "send"
      send_money:
        source:
            type: "wallet"
            account: "${input.source_wallet}"
            account_name: "${input.source_wallet_name}"
        destination:
            type: "bank"
            account: "${input.bank_account}"
            account_name: "${input.bank_account_name}"
            bic: "${input.bank_bic}"
        provider: "auto"
        amount: "${steps.calc.output.sweep_amount}"
        currency: "PHP"
        notes: "Daily sweep"
version: 1
name: "daily-sweep-to-parent-wallet"
description: "Daily sweep from sub-wallet to parent wallet"
steps:
    - name: "check_balance"
      get_wallet_balance:
        wallet_account: "${input.source_wallet}"
    - send_money:
        source:
            type: "wallet"
            account: "${input.source_wallet}"
            account_name: "${input.source_wallet_name}"
        destination:
            type: "wallet"
            account: "${input.parent_wallet}"
            account_name: "${input.parent_wallet_name}"
            bic: "PAEYPHM2XXX"
        provider: "paymongo"
        amount: "${steps.check_balance.output.available_balance}"
        currency: "PHP"
        notes: "Sweep to parent"

A wallet-to-wallet sweep does not need a reserve floor. There is no per-transfer fee for provider: paymongo transfers between PayMongo wallets, so sweeping the full available balance is safe.

Trigger

A schedule trigger runs the workflow on a cron expression. Use a Manila-time prefix if you want the cron evaluated locally:

curl --request POST 'https://workflow-api.paymongo.com/v1/triggers' \
  --header 'Authorization: Basic ${YOUR_BASIC_TOKEN}' \
  --header 'Organization-Id: ${YOUR_ORG_ID}' \
  --header 'Content-Type: application/json' \
  --data '{
    "workflow_id": "wf_abc123",
    "condition": {
      "schedule": "CRON_TZ=Asia/Manila 0 2 * * *"
    }
  }'

What success looks like

A completed instance run by the trigger:

{
  "data": {
    "instance_id": "inst_xyz",
    "workflow_id": "wf_abc123",
    "status": "completed",
    "output": {
      "steps_executed": 3,
      "step_outputs": [
        {"available_balance": 5000000, "pending_balance": 0, "wallet_id": "..."},
        {"sweep_amount": 4990000, "balance": 5000000, "min_reserve": 10000},
        {"status": "completed", "transfer_id": "tr_..."}
      ]
    }
  }
}

If the wallet balance is below the reserve, sweep_amount resolves to 0 and the send_money step rejects it as invalid_workflow_definition (amount must be greater than 0). The instance ends in failed status. To avoid this you can short-circuit the workflow upstream with a guard.

Variations

  • Different cadence. Switch the cron expression: every hour at minute 5 (5 * * * *), every Monday at 09:00 Manila (CRON_TZ=Asia/Manila 0 9 * * 1).
  • Sub-hourly recurrence. Use the interval shorthand: "schedule": "@every 30m".
  • Different reserve. Adjust the min_reserve value in the compute step. Higher reserves leave more cushion for the per-transfer fee.
  • Multiple beneficiaries. Add more send_money steps after the sweep. See Split a Payout.

See also