Approval lifecycle
At a high level:- You create a withdrawal (
POST /v2/portfolio-wallets/:id/withdraw). - The withdrawal processor sources liquidity (unwind/bridge as needed).
- When ready, Braid creates a Turnkey signing activity for a payout leg.
- The payout transitions through broadcast + finality and becomes
completed.
payouts[].status = pending_customer_approval: a Turnkey approval is requiredpayouts[].status = pending_broadcast: approved/signed, waiting to broadcastpayouts[].status = broadcasted: broadcast submitted (finality pending)payouts[].status = completed: terminal success
Head-of-line gating: one outstanding approval per wallet+chain
Braid enforces an EOA-compatible safety rule:- At most one customer approval can be outstanding per
portfolioWalletId + chain.
withdrawal.status = queued_for_customer_approvalcustomerApprovalState = "queued"blockedBydescribes the blocking payout leg (when known)- The waiting payout leg uses
payouts[].status = waiting_for_prior_approval
completed / failed / cancelled / skipped), Braid wakes the next queued withdrawal and requests the next approval.
Automation patterns
Two common patterns work well:- Webhook-driven:
- Subscribe to
portfolio_wallet.withdrawal.payout.status_changed. - When you see
pending_customer_approval, prompt/approve in Turnkey. - When you see
completed, consider the withdrawal leg finished.
- Subscribe to
- Polling-driven:
- Poll
GET /v2/portfolio-wallets/:id/withdrawals/:withdrawalId. - Use
customerApprovalStateto branch:"required": customer action is needed now"queued": you are blocked behind a prior approval; inspectblockedBy"not_required": still sourcing liquidity / no approval needed
- Poll