Sign approvals with your registered passkey or machine key. Ground receives the signature, never your private signing key.
When approvals happen
Set up signing before you need to withdraw. In production, a request that
would take your organization’s cumulative withdrawals above $200 returns `withdrawal_signing_required` until an organization owner enables signing and
enrolls a signer. Exactly $200 is allowed without signing. The total includes
pending withdrawals across all organization wallets; cancelled and failed
withdrawals do not count. Set up signing in
Ground Portal → Settings → Signing.
Organizations with limited self-serve access have a separate $100 cumulative
production withdrawal limit.
- Withdrawals (
POST /v2/wallets/{id}/withdrawals): when signing is enabled, each payout leg transitions topending_customer_approvalwhen it’s ready for signing.
walletId + chain. If multiple activities become ready on the same chain, later activities queue behind the earlier approval automatically.
What does not require approval
Strategy updates (PATCH /v2/wallets/{id}/strategy) do not require this organization approval flow. They are API-authenticated requests; later rebalances may still be executed by Ground under the wallet’s configured policy.
Once the required chain policy is in place, Stellar setup and cross-chain allocations happen automatically without per-transaction customer approval. Enabling a new chain policy for an organization that controls its signing policy requires a separate approval in the portal. External payouts continue through the customer approval flow below.
Approving a signing policy change
For an existing organization, Ground can remove its own policy-editing permissions using its existing authority. That one-time removal does not require your signature or change your root signers. After removal, policy changes require your configured root quorum through the flow below. Ground may ask your organization to approve a policy change when enabling a new chain or a new transaction route. A policy approval is separate from approving an individual withdrawal. It is effective only after your root quorum approves it and the change completes.- In the Portal Approvals tab, select the policy change row in Pending Activities to open its details in the side drawer. Review the current policy and proposed policy for each step. The Portal checks the live policy and your signer eligibility before offering the approval action for the current step. Technical verification fingerprints are available under Technical verification details inside the exact policy change.
- Each configured root signer opens the same proposal, reviews the full request, and selects Begin signing. The drawer guides them through each remaining signature, naming the policy being signed and marking applied changes complete. The drawer shows who has approved and how many more approvals are needed. Your first signature counts toward quorum; each root user counts once. A machine root signer can use the activity-approval API with the displayed activity ID. Those votes also appear in the proposal details.
- Sign each change when prompted. If more root approvals are required, the drawer waits for quorum before advancing. You can close it and return to continue from the current change. After the proposal is applied, it appears as a Completed row in Completed Activities. Select the row to open a side pane with its explanation, signer votes and exact policy changes. Then poll the wallet or feature status again.
Exporting your signing policies
If signing policy export is enabled for your organization, open Settings → Signing → Signing policy export and select Export signing policies. The JSON download contains a timestamp, your organization and signing-account IDs, the current root quorum, and the current policy names, IDs, effects, conditions, consensus rules and notes. Keep this snapshot with your organization’s signing records; it contains no private keys. When export is being enabled through a policy proposal, the export control appears after every step is approved and verified. Partial approvals and rejected proposals leave it unavailable. If an export fails, retry; contact Ground if the failure persists.Detecting a pending approval
When a withdrawal payout needs approval,payoutLegs[].status and the external payout step state become pending_customer_approval. Poll GET /v2/wallets/:id/withdrawals/:withdrawalId or subscribe to portfolio_wallet.withdrawal.status_changed to detect this state.
After detecting a pending payout, call GET /v2/turnkey/activities/pending to fetch the approval details and activity fingerprint for the authenticated organization. Match payout approvals with activityKind: "external_payout" and the expected withdrawalId.
Approving an activity
- Fetch the matching activity from
GET /v2/turnkey/activities/pending. - Verify its transaction details.
- Prepare the approval with
POST /v2/turnkey/activity-approval-request. - Sign the returned
signingRequestwith your registered signing key. - Submit
customerApprovalStamptoPOST /v2/turnkey/activities/{activityId}/vote.
GET /v2/turnkey/activities/pending, verify its amount,
recipient, chain, and token, then use its activityId unchanged. The example below
prepares and submits a vote using a registered P-256 machine key. Set BASE_URL,
GROUND_API_KEY, ACTIVITY_ID, and SIGNING_KEY_PATH (the exported PEM file).
ACTION defaults to approve; use reject to reject the activity.
sign("sha256", ...). Return context unchanged. Prepare
again after five minutes; existing votes remain on the same activity.
For passkeys, the Portal handles signing. Custom browser integrations use the
returned signingRequest.webauthn.challenge unchanged with user verification
required. Submit version, context, and a webauthn object containing
credentialId, authenticatorData, clientDataJSON, and signature, each encoded
as base64url, in customerApprovalStamp.
Votes return HTTP 202 while pending or processing and 200 when
terminal. approvalProgress counts distinct customers; a person’s passkey and
machine key count as one vote. Partially approved activities stay pending.
currentUserApproved: true means your vote is recorded. The pending list returns
approvalStatus: null and canApprove: false when status cannot be verified.
A null progress count means the count is unknown, not zero.
Prepare rejections too. When signingRequest is null, submit only
{ action: "reject" }; otherwise sign normally. A submitting transaction cannot
be cancelled. Refresh the activity list after a 409; a replacement payout needs
fresh approvals. If preparation returns 503, retry when status is available.
Approval completion is separate from payout settlement—monitor the withdrawal
until it completes.
Verify before you approve
When your system receives a pending approval, verify the activity before signing. Once an approved transaction is broadcast, it cannot be reversed.Withdrawal payouts
- Destination address — confirm the pending activity’s
destinationAddressmatches the address your system originally submitted in the withdrawal request. Reject it if the address does not match. - Amount — confirm the displayed amount is reasonable for the withdrawal and yield source. Asynchronous sources may not exactly match the originally requested amount.
- Chain and token — confirm
destinationChainanddestinationTokenmatch your request, including the exact sandbox network. Validate the destination using the network’s address format. - Withdrawal correlation — match the pending approval to a withdrawal your system initiated. Reject orphaned, expired, or unexpected approvals.
Automation patterns
Two patterns work well for handling approvals:- Webhook-driven — subscribe to
portfolio_wallet.withdrawal.status_changedfor withdrawal payouts. When an approval becomes pending, fetchGET /v2/turnkey/activities/pending, verify the details, requestPOST /v2/turnkey/activity-approval-request, sign the returnedsigningRequestlocally, and submit the vote. - Polling-driven — for withdrawals, poll
GET /v2/wallets/:id/withdrawals/:withdrawalIdand checkpayoutLegs[].statusandpayoutLegs[].steps[].state. PollGET /v2/turnkey/activities/pendingas the approval backstop.
Status updates
Subscribe to these events to monitor progress:portfolio_wallet.withdrawal.status_changedportfolio_wallet.withdrawal.payout.status_changedportfolio_wallet.approval.status_changed
failed with a failureReason.
Approval authorizes signing. Continue tracking each payout and its withdrawal until completed; see Withdraw Funds.