Skip to main content
Recommended approach: fund your sandbox account with a simulated deposit. Use testnet coins only when you need to test the on-chain flow itself.
This page is structured in five parts:
  1. Fund your account with a simulated deposit
  2. Deposit testnet coins, with a USDC deposit on Ethereum Sepolia
  3. Withdraw the USDC back to your test wallet
  4. Sample scripts that run the deposit and withdrawal flows end to end
  5. Bitcoin on-chain and Lightning flows
Before you start:
  • Set up local development and check your credentials.
  • Confirm with your Payward contact that crypto deposits and withdrawals are enabled for your sandbox account.
  • Check Sandbox test networks for the supported networks and tokens.
Every request below goes to https://api.sandbox.services.payward.com, signed as described in Authentication. The Deposits and withdrawals guide covers each request in full. To send these requests without writing code, use the Postman collection, which signs each request for you. Set its baseUrl variable to the sandbox base URL.

Fund your account with a simulated deposit

To get a test balance without sending testnet coins, call Simulate funding deposit. It credits a synthetic, successful deposit to the account. It’s available only in the sandbox.

Deposit testnet coins

To run these steps as one program, use the deposit script.

Select and activate the method

  1. Call List deposit methods (GET /v1/accounts/{account_id}/funds/deposits/methods?asset_symbol=USDC).
  2. Select the method whose network is Ethereum Sepolia Testnet, and keep its id and minimum. Match on network, not on the method name: several networks use the name Standard. In the sandbox, the Ethereum method uses Sepolia, even though its network_info shows the mainnet explorer and contract. See Select the sandbox method. The maximum page_size is 25.
  3. If the method’s activation_status is needed, activate it with Activate deposit method (POST /v1/accounts/{account_id}/funds/deposits/methods/{method_id}/activation). A 204 response means the request was accepted; list the methods again until its activation_status is completed.
  4. Check the account’s deposit limits with List deposit limits (GET /v1/accounts/{account_id}/funds/deposits/limits/{asset_symbol}).

Get a deposit address

List deposit addresses (GET /v1/accounts/{account_id}/funds/deposits/addresses) for the method, and reuse one if it exists. Otherwise, claim one with Claim deposit address (POST /v1/accounts/{account_id}/funds/deposits/addresses), and send the method’s id as method_id in the body.
Claiming an address isn’t idempotent. Each successful request can create another address, up to a limit that returns 409 deposit_address_limit_reached. If a request times out, list addresses before you try again.

Send the deposit

  1. Set your wallet to Ethereum Sepolia, and add Circle’s test USDC token, 0x1c7D4B196Cb0C7B01d743Fbc6116a902379C7238.
  2. Get test USDC from Circle’s faucet, plus Sepolia ETH for the transaction fee. See Get test funds.
  3. Send at least the method’s minimum (2 USDC for Ethereum USDC in the sandbox), and stay within the deposit limits.
  4. Keep the transaction hash. You use it to find your deposit in the next step.

Confirm the credit

Poll List portfolio transactions (GET /v1/accounts/{account_id}/portfolio/transactions?types=deposit) until the deposit’s status reaches successful or failed.
  • Match your deposit by details.crypto_transaction_id, the on-chain transaction hash in the transaction’s details object. Results are newest first, so don’t assume the first deposit is yours.
  • Don’t filter by status while you wait. That hides deposits that are still in_progress.
  • Follow next_page_token until it’s absent, even when a page is empty.
  • In the sandbox, a USDC deposit on Ethereum Sepolia usually reaches successful within a few minutes of being mined.
  • A confirmed transaction in the block explorer doesn’t mean your account has been credited. Wait for the portfolio transaction.

Withdraw

Withdraw a small amount back to your test wallet. Follow these steps in order. To run them as one program, use the withdrawal script.
  1. List withdrawal methods. Call List withdrawal methods (GET /v1/accounts/{account_id}/funds/withdrawals/methods) for USDC. Withdrawal method IDs are separate from deposit method IDs. Pick the one whose network is Ethereum Sepolia Testnet.
  2. Check limits. Call List withdrawal limits (GET /v1/accounts/{account_id}/funds/withdrawals/limits/{asset_symbol}).
  3. Register the destination. Reuse or create a funding address for your test wallet with Create funding address (POST /v1/accounts/{account_id}/funds/addresses). Scope the address to one withdrawal method with scope.method_id, or to every method on its network with scope.network_id, using the method’s network_id. Either way, register your wallet again for each network you withdraw on.
  4. Quote the fee. Call Calculate funding fees (POST /v1/accounts/{account_id}/funds/fees) to get the fee and a short-lived fee_token. Do this right before you create the withdrawal, so the token doesn’t expire.
  5. Create the withdrawal. Call Create withdrawal (POST /v1/accounts/{account_id}/funds/withdrawals) with an Idempotency-Key header and the fee_token.
  6. Verify the address, if asked. A 409 withdrawal_verification_required response means you need to verify that the user owns the destination. Complete a self_attestation verification for the user, then submit a new withdrawal. When you withdraw from your own main account, the user ID is your Console Client ID. See Address-ownership verification.
  7. Confirm completion. Poll portfolio transactions with types=withdrawal, without a status filter. Match the transaction whose reference.kind is funding and whose reference.id is the withdrawal_id from the response, and wait until the withdrawal reaches successful or failed. Its details.crypto_transaction_id holds the on-chain transaction hash. Then check that the funds arrived in your wallet. In the sandbox, a small USDC withdrawal on Ethereum Sepolia usually completes within a few minutes.
If a withdrawal request has an unknown outcome, reconcile it through the portfolio before you retry. Use the same idempotency key and the same request body. A withdrawal can fail even when your account shows enough balance, because the sandbox pays withdrawals from a shared testnet wallet. See Keep sandbox withdrawals small.

Sample scripts

These scripts run the deposit and withdrawal flows above as one program each. They read the variables from Set up local development, plus PWS_ACCOUNT_ID. Each was run end to end against the sandbox.

Helpers

Save the helpers next to the scripts. They sign each request as described in Authentication.

Deposit script

Run it once to get the deposit address. Send at least the method’s minimum, then run it again with TX_HASH set to the transaction hash.

Withdrawal script

Set WALLET_ADDRESS to your test wallet, and optionally AMOUNT (the default is 1 USDC).

Bitcoin

Bitcoin uses two different testnets in the sandbox. Use a separate wallet for each.

On-chain Bitcoin on Signet

Follow the same deposit and withdrawal steps as above, with asset_symbol=BTC, and pick the methods whose network is Bitcoin. Set your wallet to the default Signet: coins sent from testnet3, testnet4, or a custom signet are never credited, even though the addresses look the same. Bitcoin differs from the Ethereum Sepolia flow in a few ways:
  • A deposit appears as in_progress within seconds of being broadcast, and becomes successful after 3 confirmations. Signet blocks are irregular, so allow up to an hour. The sample scripts stop polling after 15 minutes, so raise their timeout for Bitcoin.
  • Bitcoin transaction hashes have no 0x prefix. Compare details.crypto_transaction_id with the hash exactly as your wallet reports it. The deposit’s details.vout is the output index within that transaction.
  • Withdrawals have a network fee, 0.000015 BTC in the sandbox. The withdrawal method’s fee shows it, and Calculate funding fees returns the exact amount. In the sandbox, a 0.0005 BTC withdrawal reached successful in about 20 minutes.

Lightning on testnet3

For Lightning deposits, use Create Lightning invoice (POST /v1/accounts/{account_id}/funds/deposits/lightning-invoices) instead of claiming an address. Send asset_symbol BTC and the exact amount the invoice should request, for example 0.0005. Each invoice is single-use, for an exact amount, and must be paid in full before it expires. Creating an invoice isn’t idempotent, so don’t retry a timed-out request automatically. Create a new invoice instead, and let the unpaid one expire. Pay the invoice from a Lightning wallet on testnet3. Test Bitcoin withdrawals on Signet.

If a test fails

Send your Payward contact the account ID, asset, network, method IDs, request IDs or references, the transaction hash, and the last status the API returned. Don’t include API keys, secrets, or signed request headers.

Next steps