Skip to main content

What happened

A non-custodial payout was waiting for the end user to approve it, but the signing window expired before the user acted. No funds were moved. The payout is in a terminal failed state with failureCode: USER_SIGNATURE_TIMEOUT. The default signing window is 900 seconds (15 minutes) in production. In sandbox the same window applies, but sandbox testing typically uses POST /v2/sandbox/payouts/:id/simulate/cosign directly rather than waiting for the timer.
{
  "type": "USER_SIGNATURE_TIMEOUT",
  "title": "User signature timeout",
  "status": 422,
  "detail": "The customer did not approve the payout before the signing window expired.",
  "resolution": "Submit a new payout when the customer is ready to sign.",
  "docs": "/errors#user-signature-timeout",
  "instance": "/v2/payouts/txn_abc123",
  "correlationId": "corr_xyz789",
  "timestamp": "2026-01-15T09:30:00.000Z"
}

Common causes

  • User did not open the signing link — the signing prompt was not surfaced to the user in time
  • Session interrupted — the user started the signing flow but did not complete it within the window
  • Background/locked screen — the user’s device locked before they could approve

Recovery

This is a terminal state. The payout cannot be recovered; a new payout must be submitted.
1. Confirm the terminal state
curl -X GET https://api.conduit.financial/v2/payouts/txn_abc123 \
  -H "x-api-key: YOUR_API_KEY"
2. Submit a new payout when the user is ready Once the user is available to sign, submit a new payout with a fresh idempotency key:
curl -X POST https://api.conduit.financial/v2/payouts \
  -H "x-api-key: YOUR_API_KEY" \
  -H "Idempotency-Key: idem_NEW_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "customerId": "cus_abc123",
    "assetAmount": {"code": "USDC", "amount": "50.000000", "chain": "ETHEREUM"},
    "destination": {
      "recipient": {
        "rail": "CRYPTO",
        "chain": "ETHEREUM",
        "address": "0xabc..."
      }
    }
  }'

Prevention

  • Surface the signing prompt immediately — send a push notification or in-app prompt as soon as the payout enters the signing step; do not rely on the user checking back later
  • Warn before the window closes — if your UI has visibility into the payout state, show a countdown or alert when the window is about to expire
  • Handle transaction.failed with this code — branch on failureCode === 'USER_SIGNATURE_TIMEOUT' to prompt the user to resubmit
The transaction.failed event fires when the window expires:
{
  "type": "transaction.failed",
  "data": {
    "transactionId": "txn_abc123",
    "failureCode": "USER_SIGNATURE_TIMEOUT"
  }
}