Skip to main content
A payment can fail while Melio collects the funds from your internal account or while it delivers them to the payee. Either way the payment moves to failed, and Melio settles the money automatically. Here is what happens and how to recover.

What can fail

  • Collection - the debit from your internal (funding) account does not go through.
  • Delivery - the funds cannot be delivered to the payee, or the transfer is returned.
  • Risk review - the payment is declined before any money moves. See Payment lifecycle.

How Melio handles it

Melio does not automatically retry a failed payment. To try again, create a new payment.

The failureReason object

A payment with status: "failed" carries a failureReason object explaining why. It is absent on every other status.

Failure types

This set is fixed - every failure is one of these three.

Failure codes

Codes are named after the industry code that produced them, so a code can be matched against a bank return without a translation table:
  • R##_* - a NACHA ACH return code.
  • CARD_* - an ISO 8583 card issuer decline.
Everything else is a Melio-level outcome with no industry code behind it.
Always read code together with type. The same ACH return can arrive on either leg, so R02_ACCOUNT_CLOSED means your account under failed-to-collect and the payee’s account under failed-to-deliver.

ACH return codes

Can appear under failed-to-collect or failed-to-deliver.

Card decline codes

Appear under failed-to-collect only, since only the payer funds a payment with a card.

Delivery outcomes

Appear under failed-to-deliver only. These have no industry code behind them.

Risk decline codes

Appear under risk-declined only.
A decline is reported as the bare RISK_DECLINED whenever the underlying reason cannot be disclosed. It does not mean the reason is unknown, and you should not infer anything from it beyond the payment having been declined.

Generic codes

Each type has one generic code, reported when the specific reason is unavailable or cannot be disclosed. These are the codes to fall back to when you receive one you do not recognize. RISK_DECLINED is listed under Risk decline codes as well. It is the only code that serves both roles: a reason in its own right and the generic for its type. The two are indistinguishable by design, so never treat its presence as meaning the reason was unavailable.

Handling codes safely

New code values are added over time as payment rails and review rules change. To stay forward-compatible:
  1. Branch on type first. The three types are fixed and cover every failure.
  2. Then branch on the code values you care about.
  3. Treat an unrecognized code as the generic code for its type - COLLECTION_FAILED, DELIVERY_FAILED or RISK_DECLINED - rather than as an error.
  4. Never parse message. Show it to a user if useful, but do not match on it.

How you find out

Melio sends api.payment.updated; fetch the payment to see it is now failed and read its failureReason.
The business is also emailed about the failure. For a delivery failure the email confirms the funds are being refunded and the expected timing. If you would rather handle these notifications yourself, ask Melio to configure which failure emails are sent.

How to recover

There is no retry on the same payment; recovery is always a new POST /payments. What to fix first depends on the type:

Test failures in the sandbox

Drive a payment to failed in a non-production environment with the simulation endpoint, so you can build and verify your handling before going live. It is available in non-production only and returns 404 in production.
Use fail-collect for a collection failure, fail-deliver for a delivery failure, and risk-decline for a risk decline. action is the only required field; an optional metadata object is echoed to logs and not persisted on the payment. No other fields are accepted - the request is rejected with 400 VALIDATION_ERROR if you send one.
The simulation controls which type you get, not which code. Use it to verify that your handling reacts correctly to each type and to an unrecognized code; it is not a way to reproduce a specific ACH return.
Related: an undeposited check is automatically voided after 90 days, and the payment is refunded like any other delivery failure. It is reported as CHECK_RETURNED.