Skip to main content
Some internal accounts can’t be created from raw details - a Plaid bank account or a card. For these, you mint a short-lived, Melio-hosted link and embed it, so bank and card details never touch your servers. The outcome of this flow is a new internal account for the entity (the funding source it pays from). This page shows the Plaid flow end to end, and the card flow that works the same way.

Flow at a glance

Hosted Links Flow
Create the link with the accounts endpoint, passing type: "plaid" (or "card"). See the API reference.
The response returns a short-lived url and its expiration. Mint a fresh link if it expires before the user opens it.
This is a UI screen, so embed the Sonar SDK and forward the Melio-Sonar-Token on the call (see UI requirements).
If the link can’t be minted, the call returns:
  • 400 - the request is invalid.
  • 403 BUSINESS_NOT_ELIGIBLE - a risk or compliance limitation blocks the entity from adding an internal account (check GET /limitations).
  • 404 - the entity was not found.

2. Embed it

Embed the returned url in an iframe (or redirect the user to it). The hosted page runs the flow and creates the internal account for you.
Plaid Flow
Choosing an account: if the connected bank has more than one account, Plaid Link lists them and the user selects which one to link. That single account becomes the internal account. To add another, mint another link.
Testing in the simulator: to run the full Plaid flow in the sandbox, choose “Continue without phone number” in Plaid Link. This skips phone verification so you can complete the flow end to end.

3. Listen for the result

When the flow finishes, the embedded page posts a melio:accounts:internal message to the parent window. Listen for it, and always filter on event.data.type:
The event uses one shared shape for every outcome: { type, flow, kind, status, accountId?, message? }.
  • flow is "add" (adding a new account) or "verify" (verifying an existing one - Plaid only; see Verify links).
  • kind is "plaid" or "card".
  • status is "success", "cancelled", or "error".
  • accountId is present only when status is "success"; message is present only when status is "error".
Not every combination occurs. The events that can actually fire are: The card flow has no cancelled event (there’s no Plaid Link to exit), and there is no verify + card combination.
As a best practice, also verify event.origin matches Melio’s hosted domain before acting on the message.

Success

The user completed the flow. accountId is the new acct_... internal account. (A Plaid-backed account is returned with type: "ach".)
For a Plaid bank account the account is either instantly verified, or returned as isVerified: false with micro-deposits to follow (complete it later with a verify link - see below). You can’t originate a payment from an unverified account.

Cancelled (the exit event)

This is the exit event, and it’s Plaid only. If the user exits Plaid Link inside the flow - closes it, taps back, or abandons a step - the flow reports cancelled. There is no accountId, and no account is created. The card flow never emits this status.
A cancelled event only arrives if the exit happens inside the flow. If the user closes the browser or tab entirely, or loses connectivity, no window message is delivered at all. Never rely on the window message alone: reconcile with the api.account.created webhook, or poll GET /accounts. If no account was created, the flow did not complete, and you can offer the user a fresh link.

Error

Something went wrong during the flow. message describes what; let the user retry.

When the account is created, and the webhooks

Melio creates the internal account when the user completes the hosted flow (for Plaid, after they select an account and confirm; for card, after the card is tokenized). You do not call a create endpoint yourself. The window message is a UI signal; the webhooks are the source of truth. Subscribe to: So the reliable pattern is: on the success message, reconcile against api.account.created (or GET /accounts); then watch api.account.updated to know when an unverified account clears micro-deposit verification. How this affects status:
  • Completed (success): the internal account exists. isVerified is true (usable right away) or false (micro-deposits pending - not usable for payments until it verifies).
  • Cancelled or browser closed: no account is created and no status changes. Nothing to clean up.

Get and use the account

On success, use the accountId to fetch the full account:
You can also list the entity’s internal accounts with GET /accounts?ownershipType=internal. Once isVerified is true, use the account as the funding source on a payment by passing its id as originatingAccountId:
Set externalId on the account to map it to your own ledger id, then look it up later with GET /accounts?externalId=... - no need to store Melio ids.

Card flow

The card flow works the same way: mint the link with type: "card", embed the url, and listen for the same melio:accounts:internal message (with kind: "card"). The hosted page shows PCI-compliant fields for the card number, expiry, and CVC, plus cardholder and billing address; the card is tokenized in the browser, so full card details never reach your servers. Because there’s no Plaid Link to exit, the card flow emits only success or error - never cancelled.
Card Flow
An internal Plaid account that came back unverified (isVerified: false) is completed with a verify link: POST /accounts/{accountId}/verify/link. It reuses the Plaid flow and emits the same message, with flow: "verify" and kind: "plaid" - success, cancelled, or error, just like the add flow. When it clears, api.account.updated fires.