Before you start
Ask Meridian to register an endpoint for each event you want. Meridian registers endpoints per event name, so register all four to follow a payment end to end:payment_link_transaction_createdpayment_link_transaction_status_updatedpayment_link_transaction_succeededpayment_link_transaction_failed
Route the event
Every delivery includes these headers:
The body has no envelope. It is a JSON object with
paymentLink and transaction, and the event name is only in the X-Meridian-Event-Type header. Read the header first, then act on transaction.status.
paymentLink is the same object the Payment Links API returns. See Payment Link Transaction for the full schema and an example of each event.
Amounts are decimal strings, timestamps are UTC, and fields with no value are sent as null instead of being omitted. The payload does not include payer names, bank accounts, or instrument details.
Status flow
A successful payment moves through these statuses:PENDING → PROCESSING → USER_DEDUCTED → COMPLETED
Failures
A transaction that reachesFAILED, DEBIT_FAILED, RETURNED, or REVERSED sends payment_link_transaction_failed. The statusReason field holds the failure reason when there is one.
For example, a failed payment sends status: FAILED, previousStatus: PROCESSING, and statusReason: INSUFFICIENT_FUNDS.
Reversals
A reversal sendspayment_link_transaction_status_updated at REVERSAL_IN_PROGRESS. It sends payment_link_transaction_failed only when it reaches REVERSED.
Other statuses
These statuses also sendpayment_link_transaction_status_updated: PENDING_MANUAL_REVIEW, EDD_REQUIRED, REFUND_IN_PROGRESS, REFUNDED, PARTIALLY_REFUNDED, TIMED_OUT_REVERSAL_DECISION_PENDING, RETURN_IN_PROGRESS, and NOT_RECEIVED.
Handle the webhooks correctly
1
Treat transaction.status as the source of truth
Use
transaction.status and transaction.previousStatus to decide what happened, not the event name alone.2
Expect repeated failed events
Each failure status is its own change, so one transaction can send
payment_link_transaction_failed more than once. For example, FAILED then DEBIT_FAILED, or COMPLETED then RETURNED after an ACH return. Handle repeated failed events for the same transaction.id.3
Don't trust paymentLink.status for the outcome
paymentLink is a snapshot from when Meridian sent the webhook. Meridian updates the link and the transaction separately, so the link’s status can lag the transaction. In payment_link_transaction_created the link can still be ACTIVE, and in payment_link_transaction_failed it can show its previous status. Fetch the link through the API if you need its final state.4
Process idempotently
Key on
transaction.id plus transaction.status. Each status change sends exactly one event, but retries can still deliver the same webhook more than once.5
Order by updatedAt
Meridian does not guarantee delivery order. Use
transaction.updatedAt to order events for the same transaction.6
Reconcile through the API
Delivery is best effort. Meridian retries a failed delivery up to 3 times, a few hundred milliseconds apart. It is not a guaranteed, replayable queue, so periodically reconcile your records through the API.
Behavior change
Payment link transactions no longer send the generictransaction_created, transaction_succeeded, and transaction_failed events. They send the payment_link_transaction_* events instead.
REVERSAL_IN_PROGRESS used to be reported as a failure and is now a payment_link_transaction_status_updated event. Statuses such as PROCESSING and USER_DEDUCTED were not reported before. The transaction_* events still exist and are unchanged for Meridian Accounts.