Skip to main content

Prerequisites

  • Payward Services API credentials (see Authentication Guide)
  • A verified user with at least one account
Only cryptocurrency deposits are currently available through the Payward Services API. Cryptocurrency withdrawals, fiat deposits, and fiat withdrawals are not yet available.

Crypto deposits

Deposit workflow

1

List deposit methods

Query available deposit methods for the target asset.GET /v1/accounts/{account_id}/funds/deposits/methods/{asset_symbol}
2

List deposit addresses

Check whether the account already has an address for the selected method.GET /v1/accounts/{account_id}/funds/deposits/addresses
3

Claim deposit address

If needed, claim an address for the selected method. POST /v1/accounts/{account_id}/funds/deposits/addresses
4

Display address to user

Display the address and any required tag or memo. The user sends crypto from their external wallet to this address.
5

Track the deposit

Subscribe to funds.deposit_* webhook events, then reconcile deposits on the receiving account through GET /v1/accounts/{account_id}/portfolio/transactions?types=deposit.
Bitcoin Lightning uses one-time, amount-bound invoices instead of reusable deposit addresses. After listing methods, replace the address steps above with the Bitcoin Lightning invoice flow.
The examples below use the signing helpers from the Authentication Guide. For GET requests, sign the same ordered query parameters that you send.
Addresses and memos in the examples are illustrative. Do not send funds to them.

Step 1: list deposit methods

Query available deposit methods for a crypto asset. Use the method’s id from the response as method_id when listing or claiming an address.
Most cryptocurrency deposits are free, with minimum deposit amounts varying by asset. A few cryptocurrencies are charged an address_setup_fee (a one-time fee on the user’s first deposit to a new address) or a per-deposit fee.
Use page_token from next_page_token to request the next page. Omit it for the first page.

Response example

Key fields to display to users are network, method when present, fee, minimum, and network_info.est_confirmation_time. Pass the method’s id back as method_id when listing or claiming an address. Optional fee, limit, and network fields are omitted when they do not apply.
Each asset can have a maximum of five deposit addresses. Some funding methods share an address space, so the same address can be returned for different assets or networks. method_id selects a context in which the returned address is valid; it does not say how the address was originally claimed. List and reuse an existing address before claiming another.

Step 2: list deposit addresses

Retrieve deposit addresses available for the selected funding method. Reuse a returned address instead of creating a new one each time.

Response example

Use page_token from next_page_token to retrieve the next page. Optional tag, memo, and expire_time fields are omitted when they do not apply.
Completed deposits appear in GET /v1/accounts/{account_id}/portfolio/transactions?types=deposit. Portfolio visibility is asynchronous.

Step 3: claim a deposit address

If Step 2 returns no suitable address, claim one using the method id from Step 1 as method_id. Display the address to the user so they can send crypto from an external wallet.
You can claim a maximum of five deposit addresses per asset. List the account’s existing addresses before claiming another one.
Address claims are not idempotent. Do not retry automatically after a timeout or another response with an unknown outcome. List the account’s addresses before deciding whether to make another claim.
Some networks (e.g., XRP, XLM) require a tag or memo in addition to the address. If tag or memo is present in the response, your UI must display it and instruct the user to include it when sending funds. Deposits sent without the required tag/memo may be lost.

Bitcoin Lightning invoice flow

For the Bitcoin Lightning deposit method, create a BOLT11 invoice for the exact BTC amount the user intends to deposit. Use the limits from the deposit-methods response to validate the amount before requesting the invoice. The selected account_id is the account that receives the completed deposit.
A Lightning invoice is single-use, must be paid in full, and cannot be paid after expires_at. Invoice creation is not idempotent. If a request has an unknown outcome, create a new invoice; unpaid invoices expire on their own.

Reusable-address UI flow

Best practices

  1. Always display tag/memo: For networks that require a tag or memo (XRP, XLM, etc.), prominently display it alongside the address. Missing tags/memos can result in lost funds.
  2. Set expectations: Show minimum amounts and est_confirmation_time from the methods response so users know what to expect before sending funds.
  3. Use fresh responses: Available methods, addresses and limits are user-specific and may change based on account standing, remaining limits, or regional regulations. Fetch fresh data before displaying options rather than relying on cached results.

Deposit webhook events

Register a webhook through POST /v1/webhooks and subscribe to the deposit lifecycle events your integration needs: All deposit lifecycle events contain type, transaction_id, user_id, and account_id. They can also contain amount and fee:
amount is the total deposit amount, inclusive of the fee. PWS includes it when both the source value and its asset can be resolved. fee is optional. PWS includes it only when the source provides both the fee value and its asset and the asset can be resolved. This includes an explicitly provided zero fee. If the fee or its asset is unavailable, PWS omits fee rather than assuming that it is zero or that it uses the deposit amount’s asset. The availability of amount and fee is independent of the lifecycle event type. Treat absent fields as unavailable; PWS does not return them as null. Returned and failed deposits also contain a required reason field. PWS normalizes it to one of account_details_mismatch, limit_exceeded, duplicate, account_restricted, or other. Other deposit lifecycle events do not contain reason. A deposit can produce multiple lifecycle events as it progresses. Treat each webhook as a notification and use the Portfolio Transactions endpoint for reconciliation. Deliveries can be repeated, so use the svix-id header as the deduplication key and return a 2xx response after accepting the event. These events describe the lifecycle of a deposit. They are distinct from conversion.deposit_* events, which describe the inbound funding leg of a conversion.

Crypto withdrawals

Withdrawals are key-based: you save an address once, then use its key in each withdrawal request.

Withdrawal workflow

1

List withdrawal methods

Query available withdrawal methods for the target asset. GET /v1/accounts/{account_id}/funds/withdrawals/methods/ {asset_symbol}
2

Validate address (optional)

Verify the destination address is valid before saving. POST /v1/funds/withdrawals/addresses/validate
3

Save address (create key)

Store the validated withdrawal address for the user. POST /v1/accounts/{account_id}/funds/withdrawals/addresses
4

Preview / submit withdrawal

Submit the withdrawal request. POST /v1/accounts/{account_id}/funds/withdrawals
5

Monitor status (webhook / polling)

Track the withdrawal through withdrawal.status_updated webhooks and reconcile it through GET /v1/accounts/ {account_id}/portfolio/transactions?types=withdrawal.

Step 1: list withdrawal methods

Call this first to determine the method’s id (passed as method_id in later requests), fee estimates, limits, and optional fee_token.

Response example

This endpoint validates the destination before you save it.

Step 3: save withdrawal address

Save the address once and keep the returned key for future withdrawals.

Step 4: preview and submit a withdrawal

Use preview=true to quote fees and totals without creating a withdrawal, then submit with preview=false.

Statuses

Withdrawal best practices

  1. Use idempotency keys: Always generate a unique UUIDv4 and send it as the Idempotency-Key HTTP header per intended withdrawal to avoid duplicate sends on retries. Replayed responses include Idempotent-Replayed: true.
  2. Preview first: Run a preview request immediately before submit so users can confirm amount, fee, and total.
  3. Refresh expired fee tokens: fee_token values are short-lived. If a withdrawal is rejected due to an expired/invalid token, fetch withdrawal methods again (or run a fresh preview) to get a new fee_token and retry.
  4. Persist key ownership: Store which key belongs to each user and enforce access checks in your app.
  5. Handle memo/tag networks: For XRP/XLM-like networks, capture and persist memo/tag fields when addresses are saved.
  6. Monitor and reconcile: Subscribe to withdrawal.status_updated and poll GET /v1/accounts/{account_id}/portfolio/transactions?types=withdrawal for recovery and reconciliation.

Common errors

API reference