Prerequisites
- Payward Services API credentials (see Authentication Guide)
- A verified user with at least one account
Deposit addresses are issued by Payward and managed under
/v1/accounts/{account_id}/funds/deposits/addresses.
Withdrawals use a registered funding address or a supported linked bank account. Manage registered addresses under
/v1/accounts/{account_id}/funds/addresses and select the destination when creating the withdrawal.Enrich funding method identifiers
Use Look up funding method metadata to resolve public names, assets, networks, payment rails, and reported requirements for the selected account. Batch method IDs using repeated query parameters. Supply 1–25 entries, counted before duplicate removal:network_id to resolve its methods. A network can return methods for multiple assets
and directions. Supply either method_ids or network_id.
contract_address and
on_chain_asset_symbol can be present without an explorer. Confirmations can be a numeric string or a
network-specific description. Explorer strings can contain transaction URL templates.
Fiat metadata can have no network and can return payment_rails: [], including for SEPA methods.
The following illustrative entries show a reported payment rail and
a bank-link requirement:
Metadata does not establish transaction availability. Use List deposit
methods and List withdrawal
methods to discover supported operations, withdrawal
limits for limits, and Calculate funding
fees for fee quotes.
Deposits
Deposit workflow
1
List deposit methods
Query available deposit methods for the receiving account and asset.
GET /v1/accounts/{account_id}/funds/deposits/methods?asset_symbol={asset_symbol}2
Check deposit limits
Retrieve the current limits for each available method.
GET /v1/accounts/{account_id}/funds/deposits/limits/{asset_symbol}3
Prepare the selected method
Fiat and crypto deposits use different preparation steps.
Fiat deposits
Bank transfer: If
activation_status is needed, request activation once.
If it is pending, wait and list the methods again. When it is completed, not_needed, or omitted,
retrieve the bank-transfer instructions. Display every returned field so the
user can make the transfer from their bank.Linked bank account: Methods with bank_link in requirements use a linked account instead of bank-transfer
instructions. Link the bank account or reuse an active link,
then create the deposit to request the funds. Linking alone does not move funds.Crypto deposits
Deposit address: List existing addresses and reuse a suitable one.
If none is available, claim an address. Display the address and any
required tag or memo so the user can send the deposit.Bitcoin Lightning: Create an invoice for the exact BTC amount instead of
claiming an address. The user must pay it before it expires.
4
Track the deposit
Reconcile deposits on the receiving account through
GET /v1/accounts/{account_id}/portfolio/transactions?types=deposit.The examples below use the
signed_headers (signedHeaders) helper from the Authentication
Guide. For GET requests, build the path with its query
string once, then sign it and request it.Bank-link deposit flow
Use Create deposit to pull funds from a linked bank account through a supported deposit method. Currently supported: Plaid US (USD). Discover methods for the receiving account and asset before initiating a deposit.- List deposit methods for the receiving account and asset. Select a supported method whose
requirementscontainsbank_link. - Check the deposit limits for the selected method.
- Complete the Bank links flow, or reuse an existing link. The account link must have
status: active, and its bank link must havestatus: linked. - Submit the method’s
idand the linked account’saccount_link_idtoPOST /v1/accounts/{account_id}/funds/deposits. Use the same receiving account throughout the flow.
202:
GET /v1/accounts/{account_id}/portfolio/transactions?types=deposit and match the returned reference to the transaction’s reference.id. The Portfolio transaction id is a separate identifier. Bank-link deposits return a reference.
Step 1: list deposit methods
Query available deposit methods for an asset. Supplyasset_symbol as a query parameter, and follow next_page_token to read every page. Use the method’s id as method_id when listing deposit destinations or claiming a crypto address. Method discovery describes availability and method metadata. Retrieve account-specific rolling limits separately in Step 2.
Check requirements on the selected method:
[]: no additional requirements are reported. Follow the applicable activation and deposit-instructions steps.["bank_link"]: a linked bank account is required. If you do not already have a suitable link, use Create bank link with the returned methodidasmethod_id, then send the user through the hosted linking flow.
bank_link even when the user already has a linked account. Do not infer the requirement from the method name or supports_virtual_accounts.
Example USD method excerpts:
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.page_token from next_page_token to request the next page. Omit it for the first page.
Response example
asset metadata and the customer-facing method display name. Also display network, fee,
minimum, activation_status, and network_info.est_confirmation_time when present. The optional maximum is a method’s per-transaction cap, not the account’s current deposit allowance. Pass the method’s id
back as method_id when activating a method, listing deposit instructions, or claiming a crypto address. Optional fee,
amount, and network fields are omitted when they do not apply.
For fiat methods,
supports_virtual_accounts: true means Funding can provide dedicated virtual-account instructions.
false does not identify a particular alternative flow, and omission means Funding did not return deposit metadata
for the method. Use the deposit-instructions response, not this boolean, to determine which banking fields and payment
reference are available.Compatible funding methods can share an address space and its limits, 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: check deposit limits
Retrieve the account-specific limits for the same account and asset. Follownext_page_token on both operations before matching a limit’s method_id to a discovered method’s id. Ignore limit entries with no matching method. An empty limits data array, or no limit entry for a selected method, does not mean deposits are unrestricted.
When present, maximum_amount is the largest amount currently permitted after the account’s amount-based limits, in the deposit asset. It excludes the method’s per-transaction maximum and count-based limits, and other controls may still apply. Zero means no amount remains under the aggregate amount limit. maximum_amount is omitted when no amount-based limit applies.
Check each period_limits entry as well. amount limits report a remaining amount and its asset; check that asset before comparing amounts because it can differ from the deposit asset. deposit_attempts and successful_deposits report remaining counts. daily, weekly, and monthly are rolling 24-hour, 7-day, and 30-day windows. custom uses period_seconds. A negative remaining value means a rolling limit is already exceeded. A zero remaining count may prevent another deposit even when maximum_amount is positive. Other deposit controls may still apply.
Compare the intended amount with the selected method’s minimum and per-transaction maximum, when present, and with the matching maximum_amount and rolling limits. These limits are point-in-time values. Re-check them before initiating a deposit or creating a Lightning invoice when time has passed, the amount has changed, or another deposit may have consumed a limit. If a deposit is rejected for its amount, refresh the limits and let the user adjust the amount or method.
See List deposit limits for the full field reference.
For example, use the same receiving account and asset for both requests:
method_id. Its maximum_amount is in BTC, but its daily amount limit is in USD:
WVSD33HRMGSZUBM7, the same requests return the ACH method and its matching limit:
Step 3: activate a deposit method when required
If the selected method’sactivation_status is needed, request activation using its id as method_id. A 204
response means Funding accepted the request; it does not mean activation has necessarily completed, and it does not
contain deposit instructions.
completed. If it reports pending, wait and list the methods again later.
Activation returns
400 Bad Request when the selected account is missing verification information or documents
required by the funding method. The error is associated with account_id; when available, its message names the
missing requirement, such as address.Step 4: list deposit addresses
Retrieve the crypto addresses or fiat bank-transfer instructions available for the selected funding method. Reuse a returned crypto address instead of creating a new one each time.Response example
address_details variant:
crypto.addressis required for crypto destinations.crypto.tagandcrypto.memoare present only when the network requires them.- The
fiatobject contains only the banking fields that apply to the selected method. Fields such asiban,account_number,routing_number,sort_code,bic,swift,name_on_account, andreferenceare optional individually. Whenreferenceis present, include it with the fiat deposit. expire_timeis present only when the destination expires.
page_token from next_page_token to retrieve the next page.
Completed deposits appear in
GET /v1/accounts/{account_id}/portfolio/transactions?types=deposit. Portfolio
visibility is asynchronous.Step 5: claim a crypto deposit address
If Step 4 returns no suitable crypto address, claim one using the methodid from Step 1 as method_id. Fiat deposit
methods return their instructions from Step 4 and cannot be claimed with this operation.
Compatible methods can share an address limit. List and reuse an existing address before claiming another. If the
limit is reached and no existing address is eligible to expire, the API returns
409 deposit_address_limit_reached.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. Check the selected method’sminimum and maximum, when present, and the account-specific constraints from the
deposit-limits endpoint before requesting the invoice. The selected account_id is the account that receives the
completed deposit.
Reusable crypto-address UI flow
Best practices
- 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.
- Set expectations: Show
minimumamounts andnetwork_info.est_confirmation_timefrom the methods response so users know what to expect before sending funds. - 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 throughPOST /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. Its value can be negative. 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.
Negative amounts
Thevalue in amount and fee can carry a leading minus. A negative amount can represent a deposit reversal, not
additional money credited to the customer. Keep the sign and parse value as a signed decimal. A negative value is
not an error.
transaction_id to reference.id on the Portfolio transaction whose reference.kind
is funding, within the same account.
Withdrawals
Create a fiat or cryptocurrency withdrawal to a saved funding address, or use a linked bank account for a supported USD method. Funding calculates the fee and validates the method, network, destination, limits, activation, account state, and Travel Rule requirements. Quote the fee first when you need to show it to a user before they commit.Withdrawal workflow
1
List withdrawal methods
Choose a method for the asset and inspect its
requirements and required address_fields.GET /v1/accounts/{account_id}/funds/withdrawals/methods?asset_symbol={asset_symbol}2
Check withdrawal limits
Retrieve the current maximum and rolling limits for each available method.
GET /v1/accounts/{account_id}/funds/withdrawals/limits/{asset_symbol}3
Select a destination
Reuse or register a compatible funding address, or follow the bank-link withdrawal flow
for a supported linked-bank method.
GET /v1/accounts/{account_id}/funds/addressesPOST /v1/accounts/{account_id}/funds/addresses4
Quote the withdrawal fee before you display it
Get the fee for a gross
amount. If the quote includes a short-lived fee_token, carry it into the next step.POST /v1/accounts/{account_id}/funds/fees5
Create the withdrawal
Submit the selected scope,
destination, public asset symbol, and gross amount.POST /v1/accounts/{account_id}/funds/withdrawals6
Handle address-ownership verification
If the request returns
withdrawal_verification_required, complete a supported Travel Rule verification method.7
Reconcile through Portfolio
Use the returned
withdrawal_id as reference.id with reference.kind=funding.Bank-link withdrawal flow
Use Create withdrawal with a linked bank account for supported USD ACH or RTP methods.- List withdrawal methods for the sending account and USD. Select a supported method and check its limits.
- Reuse a suitable Account Link or complete the Bank links flow. Keep the same PWS account throughout.
- Submit the selected withdrawal method as
scope.method_idand the linked account asdestination.account_link_id.
bank_link
requirement does not establish that the method is supported for withdrawal submission or that the account is eligible.
See the destination field in Create withdrawal for eligibility restrictions.
A 201 response means the withdrawal was accepted; it does not confirm that funds reached the bank account.
Reconcile the returned withdrawal identifier through Portfolio.
Step 1: list withdrawal methods
Supply the asset in the requiredasset_symbol query parameter:
next_page_token until it is absent. Keep the same account and asset_symbol on every page request.
The default page size is 20, and the maximum is 25. When you supply page_token, the token’s page size applies
and page_size is ignored. Results are refreshed on each request.
Use each method’s id as scope.method_id, or its network_id as scope.network_id. Network scope succeeds only when
Funding can resolve exactly one eligible method for the requested asset. Otherwise, retry with a specific method ID.
See List withdrawal methods for the field reference. Check account
constraints separately in Step 2.
requirements: ["bank_link"] reports that a linked bank account is required, even if you already have one.
An empty array reports no recognized requirement; it does not guarantee eligibility. Use the linked-bank flow only
for a supported method. For a registered address, collect its address_fields in Step 3.
fee and fee_percentage are optional, indicative fee terms. Missing fields do not mean the withdrawal is free.
To get a fee quote for the amount you intend to withdraw, follow Step 4.
Step 2: check withdrawal limits
Retrieve the account-specific limits for the asset after choosing a method. Use only entries whosemethod_id appears
in the withdrawal-methods response for the same account and asset.
Compare your intended amount against both the method’s maximum, when provided, and the matching limits entry’s
maximum_amount. These values describe different constraints: the method value is its per-transaction cap, while the
limits value accounts for available balance and amount-based limits. It does not replace the method cap. For example,
a method cap of 0.5 BTC still applies when maximum_amount is 0.75 BTC.
Check period_limits too: count-based limits do not reduce maximum_amount, so a withdrawal can still be rejected
when both amount bounds look sufficient. All values are point-in-time and can change before submission.
See List withdrawal limits for the full field reference.
Check funds held from withdrawal
Some deposits, including ACH bank transfers, are credited to the account before you can withdraw the funds. For example, an ACH deposit can have a seven-day withdrawal hold. The withdrawal limit uses the balance available for withdrawal, somaximum_reason: "balance" can accompany a maximum below the account balance.
Use the withdrawal-holds endpoint in the API reference to see the account’s active restrictions.
Select the reference for your environment:
assetidentifies the asset whose balance is held.equivalent_amountis the held funds’ current value in USD, including whenassetis a cryptocurrency. It is not the quantity of the original asset.release_atis the scheduled end of this restriction. A hold can end earlier, including when a deposit is returned. Other restrictions may still prevent withdrawal afterward. Refresh the holds and withdrawal limits before submitting.typeidentifies a deposit hold or another kind of restriction. For deposit holds, usedeposit_transaction_id, when present, to match the deposit webhook’stransaction_id.
next_page_token to read additional pages.
When continuing with a page token, omit page_size or reuse the previous value. A different value returns a 400
error for page_size.
Each page reflects the active holds at request time, so holds that end between requests disappear from the listing.
Holds created or rescheduled to release before the last returned hold are not included in later pages.
Restart the listing to see them.
If the API returns 400 with a page_token cause asking you to restart, omit the token and start again.
The funds.withdrawal_held webhook describes a submitted withdrawal waiting for review or checks.
Use the holds endpoint for restrictions on the account’s funds before creating a withdrawal.
Step 3: select or create a funding address
List the account’s funding addresses and reuse a compatible destination. Register a new address only when no suitable address exists. Creating, renaming, or deleting a funding address requires the Add withdrawal addresses API key permission.List existing funding addresses
The first request can includepage_size and either method_id or network_id. To continue, send the returned
page_token and the same filter. Omit page_size; the token preserves it. The token is bound to the account and filter.
Pass method_id when selecting an address for a particular withdrawal method; omitting both filters returns every funding address on the account. Because
scopes nest (method → network → network group), a matching address may be registered more broadly and report a
network_id or network_group_id in scope instead of the method_id you filtered on. An empty method_id is
rejected as a validation error, not treated as no filter. Alternatively, filter by network_id for addresses registered
for that network or its network group. This excludes addresses registered for individual methods. Do not supply both filters.
Create a funding address
Set exactly one scope:- Use
method_idwhen the destination should be registered for one withdrawal method. - Use
network_idwhen the destination should be valid for methods on that network.
address_fields in the withdrawal-method
response. Do not add local address-validation rules.
Supply exactly one of address_details.crypto or address_details.fiat. For fiat addresses, use
address_details.fiat.account_number for the account_number field and address_details.fiat.iban for iban.
The account-holder name comes from verified account information.
For example, register a EUR bank destination using the method and routing fields returned by discovery:
allowed_values is non-empty, select a listed value. For example:
403 with funding_method_activation_required or
funding_method_activation_denied.
The deposit activation endpoint does not activate withdrawal methods.
The following helpers register a crypto address:
200:
name must be unique for the account owner and is limited to 64 characters.
The same crypto destination can be registered under different names. Each registration counts toward the applicable address limit.
A create request can return these 409 codes:
Address limits vary by account and scope. Reuse an existing address or contact support when the limit is reached.
Get, rename, or delete a funding address
Use the stableaddress_id in the member path:
GET /v1/accounts/{account_id}/funds/addresses/{address_id}returns the saved scope, name, and address details.PATCH /v1/accounts/{account_id}/funds/addresses/{address_id}renames the address and returns204. If the name is already in use for the account owner, it returns409 withdrawal_address_already_exists.DELETE /v1/accounts/{account_id}/funds/addresses/{address_id}deletes the address and returns204.
404 when the address is absent or unavailable to the selected account. Update can
return 403 when the caller is not allowed to change the address. Always enforce account-to-address ownership in your
integration.
Step 4: quote the withdrawal fee before you display it
Quote the fee for the gross amount you intend to withdraw. Use a methodid from Step 1 as method_id.
Fiat and crypto quotes may return a fee_token; use it when present. Skip this step when you do not need to show
a fee before submitting.
amount is the gross amount, inclusive of the fee.
201:
net_amount is what reaches the destination, and for same-asset fees net_amount + fee equals gross_amount.
Each request returns a new quote and moves no funds.
When present, pass fee_token to Step 5 to reuse this quote’s fee terms. Treat the token as opaque and sensitive: do not parse or
modify it, do not log it, and do not reuse it across users or accounts.
The quote checks the method and the amount only. Funding still checks balance, method minimums, address compatibility,
and Travel Rule requirements when you create the withdrawal.
Step 5: create the withdrawal
amount is the gross amount debited from the account, inclusive of the fee. For same-asset fees,
net_amount + fee equals gross_amount.
To reuse the fee terms from Step 4, send its fee_token and set scope.method_id to the id of the method you quoted.
Fee tokens cannot be used with network scope. Omit fee_token to have Funding calculate the fee when the withdrawal is
created.
Funding recalculates the absolute fee from the token’s pinned terms and this request’s amount. Submit the same amount
as the quote to reproduce the fee you displayed.
If the initial request is rejected with 400 on fee_token, request a new quote before submitting again. If an earlier
attempt had an unknown outcome, reconcile it first; a later validation error does not establish that earlier outcome.
201. Acceptance does not confirm settlement:
Address-ownership verification
When the response is409 withdrawal_verification_required, read verification_methods. Funding can return
self_attestation, satoshi_test, or digital_signature. Complete self_attestation through
POST /v1/users/{user_id}/travel-rule/verifications, then submit a new withdrawal request. Contact support when the
response does not include a method that the endpoint supports.
Withdrawal webhook events
Register a webhook throughPOST /v1/webhooks and subscribe to the withdrawal lifecycle events your integration needs:
All withdrawal lifecycle events contain
type, transaction_id, user_id, and account_id. They can also contain
amount and fee:
funds.withdrawal_failed, amount and fee are values recorded for the request. They do not confirm that PWS kept
the amount debited or ultimately charged the fee. Failed withdrawals may be reversed. Canceled withdrawals do not emit
this event. Use Portfolio Transactions to reconcile cancellations and the final account state.
Use Portfolio Transactions to reconcile the current state and contact support when you need help with a held or failed withdrawal.
A withdrawal can emit a held event before it completes or fails. Treat each webhook as a notification. 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 normal funding withdrawals. They are distinct from conversion.withdrawal_* events, which
describe the outbound funding leg of a conversion.
Reconcile the withdrawal
ListGET /v1/accounts/{account_id}/portfolio/transactions?types=withdrawal. Match the transaction whose
reference.kind is funding and whose reference.id equals the returned withdrawal_id.
Portfolio is the canonical status and history surface. Subscribe to transaction.withdrawal for change notifications,
then retrieve the transaction from Portfolio. See Portfolio transaction webhooks.
If the create response was lost, you may not have a withdrawal_id for exact correlation. Inspect the account’s recent
withdrawal transactions before retrying. Absence from a single read does not prove the request failed; do not submit
with a new key while the original outcome remains unclear.
Withdrawal best practices
- Reuse saved addresses: List funding addresses before creating another destination.
- Use the exact scope: Prefer
method_idwhen multiple methods exist on the same network. - Quote the fee before you display it: Method-list fees are estimates. Quote the fee for the amount you intend to
withdraw, then send that quote’s
fee_tokenwith the same amount. - Reconcile unknown outcomes first: Generate a UUIDv4 idempotency key before creating a withdrawal and keep it for any retry. Check Portfolio first; Account Link eligibility can change between attempts.
- Protect routing fields: Preserve required tags and memos, and do not log complete destination details.