Payments
Bank payments (ACH) & bank verification
Bank (ACH) debits behave differently from cards: they settle asynchronously and a manually-entered bank must be verified before any money moves. This page explains the full lifecycle so you can build a checkout that handles bank payments correctly — and the new saved-bank experience that makes repeat bank payments one tap.
Bank transfer is offered inside the embedded checkout, paylinks and invoices when the payment is in USD and your account has ACH enabled. There is nothing extra to call — the flows below are handled for you. Your only job is to treat bank payments as pending until a webhook confirms them.
The golden rule: never fulfil on pending
A bank payment is only money in the bank once you receive the payment.success webhook. Keep the order in a pending/awaiting payment state and release goods or services only on that webhook (see Handling orders).
The two ways a customer provides a bank
When a buyer picks Bank transfer in the checkout, they choose one of two methods:
- Connect bank instantly — the buyer signs into their bank through our secure bank-verification partner. The account is verified in the moment, so the debit starts processing right away.
- Enter details manually — the buyer types their routing and account number in Vezmo's own white-label form. A manually-entered account is unverified, so it must be verified by micro-deposits before the payment can complete.
What your integration sees
For both methods the embed fires pending (never success) at submit time, and the buyer sees an in-frame confirmation rather than a "paid" screen. The difference is what happens next:
| Method | At submit | Completes when… |
|---|---|---|
| Instant connect | Debit starts processing. SDK fires pending; buyer sees "Payment initiated". | The debit clears (typically a few business days) → payment.success webhook. |
| Manual entry (new bank) | No debit yet. SDK fires pending; buyer sees "Payment submitted — verify your bank". | The buyer verifies the micro-deposit, then the debit processes and clears → payment.success webhook. |
| Manual entry (saved bank) | Buyer confirms with a one-time email code; debit starts processing. SDK fires pending. | The debit clears → payment.success webhook. No re-verification. |
If a bank payment ultimately fails (verification expires, the debit is returned, insufficient funds), you receive payment.failed instead. There is no charge in that case.
While a bank payment is still processing or awaiting_verification you can cancel it outright — free, and the buyer is never debited — until the payments partner submits the debit to the bank (usually the same business day). From the dashboard, open the transaction and choose Cancel payment; from the API, call POST /v1/merchant/payment/:id/cancel (see Payments → Cancel a bank payment). You receive payment.canceled; after the window, refund it once it settles instead.
Bank verification (micro-deposits)
When a buyer enters bank details manually, the account has to be confirmed before we can debit it. This is fully managed by Vezmo — you don't have to build or call anything. If you want the step inside your own app instead, see Verify from your own app below.
- The buyer submits the manual bank on your checkout. The embed shows "Payment submitted — verify your bank" and fires
vezmo:secure-payment:pending. - Within 1–2 business days we send a small deposit (or two small deposits, depending on the bank) to the buyer's account, and email them a branded verification link.
- The buyer opens the link to a Vezmo-hosted page and enters the code shown with the deposit (or the two deposit amounts).
- On success, the debit begins processing and later settles. You get the
payment.successwebhook then — same as any ACH payment.
We send one reminder if the buyer hasn't verified after a few days. The verification window is roughly 10 days; if it lapses, the payment is cancelled with no charge and you receive payment.failed. Because the buyer leaves and returns later, always pass a real customer email in the client object when you create the session — it's how the buyer receives the verification link (and the saved-bank code).
Verify from your own app
Everything the hosted page does is also available to your integration, so you can show the verification step in your own dashboard, support tool or customer portal, or let your team enter the deposit for a customer who calls in.
- Know what the buyer needs. Every
awaiting_verificationpayment — onGET /v1/merchant/payment/:id, on?state=awaiting_verificationrows and in thepayment.awaiting_verificationwebhook — carries averificationobject:
{
"type": "descriptor_code", // or "amounts"
"url": "https://user.vezmo.com/verify-bank/bv_…", // the branded verify page
"deadlineAt": "2026-09-24T09:12:44.000Z",
"attempts": 0,
"emailSentTo": "buyer@example.com", // null when the checkout had no email
"emailSentAt": "2026-09-14T09:12:44.000Z",
"reminderSentAt": null
}- Share the link.
urlis the same page the buyer is emailed. Show it in your order page, or send it over your own channel (SMS, chat). - Submit the deposit yourself.
POST /v1/merchant/payment/:id/verify-bankwithdescriptorCode(whentypeisdescriptor_code) oramounts(two integers, in cents). See Payments → Verify a bank payment for the request, responses and the retry rules. - Re-send the email.
POST /v1/merchant/payment/:id/verify-bank/resend, with an optional correctedemail. - Hear the outcome. On success the payment moves to
processing(payment.processing), thenpayment.successat settlement. If the window lapses or the bank is locked after too many wrong attempts you receivepayment.failedwithdeclineCodeverification_expiredorverification_attempts_exceeded— no money moved.
The buyer's bank is never saved to their VezmoPay network profile through the API — that opt-in is the buyer's own, on the hosted page.
Saved bank — the VezmoPay network
After a buyer verifies a bank once, they can save it for faster checkout. A saved bank is part of the VezmoPay network: it is recognised by the buyer's email at any VezmoPay merchant, not just the one where they first used it.
On a later checkout, when we recognise a saved bank:
- The embed offers "Pay with your saved bank".
- The buyer confirms ownership with a one-time 6-digit code emailed to the saved address (valid 10 minutes). This is required every time a saved bank is used — it's what keeps a saved bank safe.
- The debit starts processing immediately, with no new micro-deposit verification — then settles as usual.
This is entirely handled inside the checkout. From your integration's point of view nothing changes: you still get pending at submit and payment.success on settlement. You can see which of your customers have a saved bank under VezmoPay → Customers → Saved banks (emails are masked; the list is read-only — customers manage their own saved banks at checkout).
Per-merchant note: the very first bank payment a buyer makes at your business still goes through one micro-deposit verification (even if they saved that bank elsewhere on the network). After that first verified payment, every later bank payment from that buyer at your business is the one-tap saved-bank flow above.
Handling orders correctly
The whole model comes down to three rules:
- On
pending: hold the order. Show the buyer "payment processing / awaiting bank verification" and do not deliver yet. - On the
payment.successwebhook: fulfil. This is the only signal that funds are in. Re-fetch the Payment bydata.paymentIdbefore fulfilling if you want belt-and- braces. - On the
payment.failedwebhook: release the order. The bank payment didn't complete; nothing was charged.
Reconciling from your backend? Subscribe to the payment.processing webhook — it fires the moment a bank payment goes in-flight, so you can record it as pending in real time instead of polling. Note that in-flight bank payments are held separately and do not appear in the default GET /merchant/payment list; retrieve them with ?state=processing (or ?pending=true). They move into the normal list as succeeded once settled.
Cards are the opposite: a card success event means the Payment is already CAPTURED, so you can fulfil immediately. Use the type /status on the webhook (and the pending vs success embed events) to branch.
// Embed event handling (vezmo.js)
v.on('pending', () => {
// Bank payment: processing or awaiting verification.
// Mark the order AWAITING_PAYMENT — do NOT fulfil.
showOrderPending();
})
.on('success', () => {
// Card / wallet: already captured -> safe to fulfil now.
fulfilOrder();
})
.on('error', ({ message }) => showError(message));
// Webhook handling (the source of truth for bank payments)
function onVezmoWebhook(event, data) {
if (event === 'payment.success') fulfilOrder(data.paymentId); // bank cleared
if (event === 'payment.failed') releaseOrder(data.paymentId); // bank failed/expired
if (event === 'payment.refunded') refundOrder(data.paymentId);
}See Webhooks for signature verification and the full payload, and Refunds for reversing a settled bank payment.
Webhooks you should handle for bank payments
| Event | Meaning |
|---|---|
payment.success | The bank debit cleared and settled. Fulfil now. |
payment.failed | The bank payment failed — verification expired, the debit was returned, or there were insufficient funds. No money moved. |
payment.canceled | The in-flight bank payment was canceled before the debit was submitted — by you (dashboard or API) or at the payments partner. Terminal; no money moved; the buyer is emailed. |
payment.refunded | A settled bank payment was refunded (full or partial). |
Test mode (sandbox)
With a test API key the entire bank flow runs against the sandbox — no real money, no real deposits, and the micro-deposit step is simulated so you can verify instantly. Use these test values inside a sandbox checkout:
| Step | Value | Result |
|---|---|---|
| Manual bank | routing 110000000, account 000123456789 | Accepted → goes to bank verification (micro-deposit) in the sandbox. |
| Micro-deposit — amounts | 32 and 45 (cents) | Verifies the bank → payment starts processing. |
| Micro-deposit — code | the 6-character code shown on the sandbox verify page | Verifies the bank → payment starts processing. |
In the sandbox, the verification email and hosted verify page work exactly as in production, and the saved-bank one-time code is delivered the same way — so you can rehearse the complete journey end to end before going live.
FAQ
Do I need to build the verification or saved-bank screens? No — and you can if you want to (verify from your own app). The deposit email, the hosted verify page, the saved-bank recognition and the one-time code are all provided by Vezmo. You handle outcomes via embed events and webhooks only.
How long until a bank payment completes? An instant- connected bank typically clears in a few business days. A manually-entered bank adds the verification step (the buyer sees the deposit in 1–2 business days, then verifies), after which it clears like any ACH debit.
Can a buyer use a saved bank at a brand-new store? Yes — it's recognised network-wide by email. Their first payment at a new business completes one micro-deposit verification; after that it's the one-tap saved-bank flow.
What stops someone misusing a saved bank? Every use of a saved bank requires the one-time 6-digit code emailed to the saved address (10-minute expiry, limited attempts). Knowing the email is not enough.