Ground uses Turnkey to manage signing flows, but you do not need a direct relationship with Turnkey to sign approvals.
When approvals happen
- Withdrawals (
POST /v2/wallets/{id}/withdraw): each payout leg transitions topending_customer_approvalwhen it’s ready for signing. Strategy updates (PATCH /v2/wallets/{id}/strategy) return the updated wallet object directly. They do not surface a Turnkey payout approval flow.
walletId + chain. If you create multiple withdrawals rapidly, later payouts queue behind the earlier approval automatically.
Detecting a pending approval
When a payout leg needs approval, it exposes three fields:| Field | Description |
|---|---|
turnkeyActivityId | The Turnkey activity ID to approve |
approvalRequestedAt | When the approval was requested |
approvalValidBefore | ISO-8601 deadline — the approval must be submitted before this time |
pending_customer_approval. Once the payout advances (e.g. broadcasted, completed), they return null.
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.
Approving an activity
- Capture the
turnkeyActivityIdfrom a payout leg in the withdrawal response or webhook. - Fetch the activity from Turnkey to get its
fingerprint. - Verify the transaction details (see best practice below).
- Submit an approval vote via the Turnkey SDK.
- Processing continues automatically once approved.
Node
activity.status: ACTIVITY_STATUS_CONSENSUS_NEEDED, additional approval votes are required per your Turnkey policy/quorum. Poll getActivity until approved, or subscribe to Turnkey webhooks.
Verify before you approve
When your server receives apending_customer_approval signal, it should programmatically 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 payout’s
destinationAddressmatches the address your system originally submitted in the withdrawal request. Reject if it doesn’t match. - Amount — confirm the payout
amountis 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
chainandtokenmatch your expectations. - Withdrawal correlation — match the
turnkeyActivityIdback 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 enterspending_customer_approval, verify and approve it. - Polling-driven — poll
GET /v2/wallets/:id/withdrawals/:withdrawalIdand check payout statuses.
Status updates
Subscribe to these events to monitor progress:portfolio_wallet.withdrawal.status_changedportfolio_wallet.withdrawal.payout.status_changed
failed with a failureReason.