Skip to main content
Crypto arrives at Conduit in order. You create a wallet and wait for it to provision. You share that wallet’s address with whoever is sending you funds. The funds arrive on-chain, Conduit detects them and opens a deposit, the deposit settles, and the balance lands on the wallet. Each step below answers four questions: what state you’re in, what you do to advance, what webhook fires, and what can go wrong. Receiving is submit-and-listen. Once the address exists there is nothing to call. You react to webhooks. You can poll the wallet’s GET endpoint, but you should never have to. Receiving works identically for custodial and non-custodial wallets. The deposit address is the wallet address either way. Detection and settlement are the same, and the balance lands in the same place. Custody only changes who controls the keys when funds leave — see the Non-Custodial Payout Lifecycle. Inbound, there is no difference.
Virtual Accounts are fiat-only. Crypto never credits a Virtual Account — an inbound deposit always credits the destination wallet. Funding with dollars over a bank rail is a different story: see the Money Movement Lifecycle and Virtual Accounts. Do not look for crypto in a Virtual Account. It will not be there.
Test this flow in sandbox. Drive it end-to-end with simulated money and deterministic controls — start with the sandbox quickstart, then deposit simulation for this flow, and the cheat sheet for every magic value and simulate endpoint.

Prerequisites

  • An onboarded customer. See Onboard a Customer.
  • The crypto wallets feature active on that customer.
  • A webhook endpoint subscribed to the crypto_wallet.* and transaction.* events. See Webhooks.

The journey at a glance

Supported chains (lowercase): ethereum, base, polygon, solana, tron. Amounts use the Money shape. Echo what Conduit sends you. Never round-trip an amount through a float.

Step 1 — Provision a wallet with a usable address

1

State: no wallet, or a wallet whose address is still null

A deposit needs somewhere to land. Create a wallet on the chain you want to receive on:
The wallet (id prefix wlt_) is created immediately, but its address is null while it provisions. Conduit assigns the address when provisioning finishes. There is no separate “generate deposit address” call. The address is a property of the wallet. A wallet without an address cannot receive anything.
2

Advance: wait for provisioning to finish

Provisioning runs in the background. There is no call to make. You wait for one webhook.For non-custodial wallets, provisioning also depends on the customer’s signers enrolling. The Non-Custodial Payout Lifecycle covers that branch. Whether custodial or non-custodial, the same event tells you the address is live.
3

Webhook: crypto_wallet.completed

crypto_wallet.completed fires when provisioning finishes and the wallet is ready to receive funds. After it, the wallet’s address is populated and usable. Read it back at any time:
Do not share an address while it is null. Gate sharing on crypto_wallet.completed (or on a GET that returns a non-null address) rather than reading the field right after creation. Funds sent before an address exists have nowhere to land.

Step 2 — Share the address with the sender

1

State: the wallet has a populated address

The wallet’s address field is the deposit address. There is nothing to generate, register, or activate. Hand that one string to whoever is sending you crypto, along with the chain. An address is only valid on its own chain.
2

Advance: send the address out of band, then wait

Give the sender the address and the chain. From here there is nothing to call. There is no API call to start a deposit — like the fiat funding story, you do not tell Conduit a deposit is coming. Conduit watches the chain and opens the deposit when funds arrive.
One address per wallet, reusable for every deposit on that chain. You do not need a fresh address per payment. To receive on a different chain, create another wallet for it (Step 1).

Step 3 — Funds arrive and Conduit opens a deposit

1

State: funds confirmed on-chain, deposit pending

When the sender’s transfer confirms on-chain, Conduit detects it and opens a deposit transaction in status pending. The credited-but-not-final amount appears in the wallet’s balances[] under pending.
2

Advance: nothing — react to the webhook

There is nothing to do. Listen for the deposit event and reconcile it against your records.
3

Webhook: transaction.created

transaction.created fires with type: "deposit". The source is type: "external_crypto" and carries the sender’s address; the destination is type: "wallet" and carries the walletId, the wallet address, and the assetAmount ({ code, chain, amount }).
A side can also be type: "deposit_address" — a Conduit-provided funding address. It carries address and assetAmount but no walletId, so handle it as its own case rather than reading walletId off it. See Deposit-Funded Orders.
What can go wrong
  • A compliance review parks or holds the deposit → transaction.failed with failureCode: compliance_hold. See COMPLIANCE_HOLD.

Step 4 — Clear an unregistered sender (required at or above the $3,000 Travel Rule threshold)

The under-threshold shortcut applies only to USD-pegged stablecoins (USDC, USDT). A stablecoin deposit **under 3,000creditsstraightthroughwithnosenderinformationrequiredevenfromaneverseenaddress.ThisishowConduitsatisfiestheTravelRule,theregulatoryrequirementtoexchangeoriginatorandbeneficiaryinformationonatransferatorabovethatthreshold.The3,000** credits straight through with no sender information required — even from a never-seen address. This is how Conduit satisfies the **Travel Rule**, the regulatory requirement to exchange originator and beneficiary information on a transfer at or above that threshold. The 3,000 shortcut is valued at the stablecoin’s 1:1 USD peg; any deposit that is not a USD-pegged stablecoin takes the sender-information path below regardless of amount. When this step applies, it is mandatory. A qualifying deposit (a stablecoin of $3,000 or more, or any non-pegged asset, from a sender Conduit hasn’t seen before) does not settle on its own. It parks: the amount sits in pending, is not credited to the wallet, and cannot be spent until you submit the sender’s details. Miss the deadline and the deposit fails for good — the funds are never usable. (A parked deposit can also still be held or rejected by AML review — see COMPLIANCE_HOLD.) If the sender’s address is already registered — or the deposit is a stablecoin under $3,000 — this step does not happen; the deposit goes straight from Step 3 to settlement (Step 5).
1

State: deposit parked, awaiting sender information

Instead of settling, the deposit holds in pending and Conduit fires transaction.awaiting_sender_information. Nothing is credited while it is parked. The fields you act on are the sourceAddress you must register, the assetAmount, the deadlineAt, and the daysRemaining countdown — a 30-day window in live. The example below is abbreviated to those; see Webhooks for the complete payload.
2

Advance: submit the sender's details before the deadline

Submitting the originator information for the source address is the only way to release a parked deposit. There is no other call, and the funds stay locked in pending until you do.
Pass register: true to also save the address so future deposits from it clear on their own; leave it out (or false) to clear only this deposit. See Registered Addresses for the full originator shape — it covers both individual and business senders. You can submit only once per deposit: a second submission for the same deposit returns 409.
Double-check the originator before you submit — the API accepts one submission per deposit. A second call returns 409 SENDER_INFO_ALREADY_RECORDED, so a typo in the sender’s name or address cannot be fixed through the API. If you submitted incorrect details, contact Conduit to have them corrected while the deposit is still under review.
3

Outcome: the deposit resumes — or times out

Once your submission is accepted, the deposit leaves the parked state and continues to settlement (Step 5), exactly as a registered-sender deposit would. If the 30-day window elapses with no submission, the deposit is terminated: transaction.failed with failureCode: sender_info_timeout, and the funds are never credited. See SENDER_INFO_TIMEOUT.
A parked deposit is not usable. The amount stays in pending and never moves to available until you submit sender information for the source address. There is no way to spend, convert, or withdraw it first, and once the 30-day deadline passes the deposit fails permanently. Treat transaction.awaiting_sender_information as a required action, not a notification.

Step 5 — The deposit settles and the balance is credited

1

State: completed

Once the deposit clears, it settles. The amount moves out of pending and into available on the wallet’s matching per-asset balance. The deposit transaction is now terminal.
2

Webhook: transaction.completed

transaction.completed fires for the deposit. The on-chain settlement reference, txHash, lives on the external_crypto side of the payload. The public transaction status has moved pendingcompleted.

Step 6 — Read the balance back

1

State: funds are credited and spendable

The deposit lands on the wallet’s balances[], one entry per asset. Each entry carries three buckets:On detection (Step 3) the amount sits in pending. On settlement (Step 5) it moves to available. A rejected or held deposit moves to frozen.
2

Advance: read the wallet

Read the wallet and confirm available reflects the deposit before you spend it.
Spend against available only. The balance lands on the wallet, never on a Virtual Account — Virtual Accounts hold fiat. To send the received crypto back out, follow the Non-Custodial Payout Lifecycle, or initiate a custodial payout the same way.

Terminal state

An inbound deposit stops at one of two terminal states.

Where to go next