Skip to content

Approval flow

Any payout where the policy engine returns require_approval triggers this flow — the request does not fail, and it does not block. POST /v1/withdraws / request_payout return right away with status: "pending_approval".

  1. Agent calls request_payout (MCP) or POST /v1/withdraws (REST) with an amount/destination that trips one of the threshold rules.

  2. nuez creates the payout attempt (status: "pending_approval") and an approval record with a 6-character challenge token and an expiry (ApprovalExpiryMinutes, default 5 minutes). The call returns immediately:

    {
    "id": "b3f1...",
    "status": "pending_approval",
    "decision": "require_approval",
    "reasons": ["per_tx_limit"],
    "approval_id": "9a02...",
    "expires_in": 300
    }
  3. A notification is sent to whichever channel the user has linked — WhatsApp or Telegram. It includes the amount, destination, and Approve/Reject buttons (or plain-text instructions).

  4. The owner replies. Buttons send a fixed string; plain text also works if typed manually:

    • APPROVE:XXXXXX or A-XXXXXX → approve
    • REJECT:XXXXXX or R-XXXXXX → reject

    (XXXXXX is the 6-character challenge token from step 2.) nuez’s WhatsApp/Telegram integration receives the reply and resolves the approval.

  5. If approved: the payout attempt moves to approved, then nuez submits it to the bank rail (submitted, later confirmed). Webhooks fire in order: payout.approvedpayout.submittedpayout.confirmed.

    If rejected: the payout attempt moves to rejected and a payout.rejected webhook fires. There is no error response to catch — you find out by polling or by webhook.

If no reply arrives within the expiry window, a background worker (polling every WORKER_INTERVAL_SECONDS, default 30s) marks the approval expired and the payout attempt expired, firing a payout.expired webhook. There’s no automatic retry — the agent has to call request_payout again with a fresh idempotency_key if it wants to retry.

Since neither call blocks, poll for resolution:

  • MCP: get_payout_status with the payout_id
  • REST: GET /v1/withdraws/{id}

or just listen for the webhook — see Webhooks for the full event list.

Set a low require_approval_above_ars via PUT /v1/thresholds (or the propose_threshold_change tool, which itself always requires a WhatsApp confirmation) against the sandbox environment. Every payout above that amount will trigger the flow without moving real money.