The customer signer must generate the Turnkey approval stamp outside Ground’s servers. Ground receives the stamp and validates it against the pending activity before forwarding the vote to Turnkey.
When approvals happen
- Withdrawals (
POST /v2/wallets/{id}/withdrawals): each payout leg transitions topending_customer_approvalwhen it’s ready for signing.
walletId + chain. If you create multiple withdrawals rapidly, later payouts queue behind the earlier approval automatically.
What does not require approval
Strategy updates (PATCH /v2/wallets/{id}/strategy) do not require this customer approval flow. They are API-authenticated requests; later rebalances may still be executed by Ground under the wallet’s configured policy.
Detecting a pending approval
When a payout leg needs approval,payoutLegs[].status and the external payout step state become pending_customer_approval.
Poll GET /v2/wallets/:id/withdrawals/:withdrawalId or subscribe to the portfolio_wallet.withdrawal.status_changed webhook to detect when a payout enters pending_customer_approval. Then call GET /v2/turnkey/activities/pending to fetch the pending approval details and activity fingerprint for the authenticated organization.
Approving an activity
- Capture the pending payout leg/step from the withdrawal response or webhook.
- Fetch the matching activity from
GET /v2/turnkey/activities/pending. - Verify the transaction details (see best practice below).
- Request the Turnkey approval or rejection payload from
POST /v2/turnkey/activity-approval-request. - Stamp
stampPayloadwith the customer signer in customer-controlled infrastructure. - Submit the stamp with
POST /v2/turnkey/activities/{activityId}/vote. - Processing continues automatically once approved.
turnkeyRequest object and a stampPayload string. Stamp stampPayload locally with the customer signer, then submit the resulting Turnkey stamp:
Stamping the approval payload
Ground does not stamp the Turnkey approval for you. Your integration must createcustomerApprovalStamp by passing the exact stampPayload string to a Turnkey-compatible stamper owned by the customer.
For a customer-held Turnkey API key, stamp the payload from your own backend or signing service:
TURNKEY_API_PRIVATE_KEY, raw passkey material, or any other customer signing secret to Ground. Ground only needs the customerApprovalStamp object, the original turnkeyRequest, and the intended action.
Verify before you approve
When your system receives apending_customer_approval signal, it should verify the transaction before approving. This is critical because an approval is an irreversible cryptographic signature — once signed and broadcast, the transaction cannot be reversed.
What to verify:
- Destination address — confirm the pending activity’s
destinationAddressmatches the address your system originally submitted in the withdrawal request. Reject if it doesn’t match. - Amount — confirm the displayed amount is reasonable for the withdrawal and yield source. Do not assume it must exactly match the originally requested amount for asynchronous sources.
- Chain and token — confirm
destinationChainanddestinationTokenmatch your expectations. - Withdrawal correlation — match the pending approval back to a withdrawal your system actually initiated. Reject orphaned or unexpected approval requests.
Automation patterns
Two patterns work well for handling approvals:- Webhook-driven — subscribe to
portfolio_wallet.withdrawal.status_changed. When a payout leg or step enterspending_customer_approval, fetchGET /v2/turnkey/activities/pending, verify the pending approval details, requestPOST /v2/turnkey/activity-approval-request, stamp the returnedstampPayloadlocally, and submit the vote. - Polling-driven — poll
GET /v2/wallets/:id/withdrawals/:withdrawalIdand checkpayoutLegs[].status/payoutLegs[].steps[].state.
Status updates
Subscribe to these events to monitor progress:portfolio_wallet.withdrawal.status_changedportfolio_wallet.withdrawal.payout.status_changed
failed with a failureReason.