>_ Analyst Engineering

ISO 20022 Reason Codes: AC01 to RR04, the Rejection Codes That Matter

Written by Ahmed at Analyst Engineering, a Senior Technical Business Analyst with 10+ years in banking and payments delivery.

Cover for a reference guide to ISO 20022 reason codes such as AC01, AC04, and AM04.

Key takeaways

  • ISO 20022 reason codes ride alongside a status (usually RJCT) or a return, and their prefix tells you the family: AC for account, AM for amount, AG for agent-level refusals, RC for routing identifiers, RR for regulatory, MS for unspecified.
  • Each code implies a different next action, which is the entire point: AC01 (wrong account number) invites a correction, AC04 (closed account) forbids retrying the same account, AM04 (insufficient funds) is about the debtor, not the beneficiary.
  • Codes come from the standard's external code sets, but each scheme permits its own subset and its own usage: the same failure can carry different codes on different rails.
  • The reference table is only half the work; mapping each code your system can emit to a correct customer message, and testing the codes it actually produces, is the other half.

An ISO 20022 reason code is the explanation attached to a failure: why the RJCT, why the return. The prefix names the family, the code names the cause, and the cause implies the action, which is why AC01 and AC04 must never share a customer message. Here are the codes a payments analyst actually meets, and what each one demands.

ISO 20022 reason codes explain why a payment was rejected, returned, or recalled, and they travel wherever failures travel: alongside an RJCT in a pacs.002 or pain.002 status report, inside a pacs.004 return, in a camt.056 recall request. The codes are defined centrally in the standard’s external code sets, so the same vocabulary works across rails, but each scheme’s rulebook permits its own subset with its own rules, the same usage-guidelines discipline that governs everything in ISO 20022. What makes the codes worth learning is not completeness, the full list runs to hundreds, but the working set below, because each code implies a different next action, and translating code to action is exactly the reason code mapping work that keeps customers from retrying the unretryable.

The reason code families

The first two letters are the family, and the family alone tells you where to look:

PrefixFamilyTypical cause lives with
ACAccountThe account: wrong, closed, blocked
AMAmountThe money: insufficient, duplicate, out of bounds
AGAgentA bank’s refusal: transaction not allowed
RCRoutingAn identifier: bad BIC or clearing code
BEBeneficiary/party dataMissing or inconsistent party details
FFFile/formatThe message itself: malformed content
MSMissing/unspecifiedNo specific reason given
RRRegulatoryCompliance and regulatory requirements
DTDateInvalid or impossible dates

The codes you will actually meet

CodeMeaningThe action it implies
AC01Incorrect account numberCorrect the account identifier and retry
AC04Closed accountDo not retry this account; obtain a valid one
AC06Blocked accountBeneficiary must resolve with their bank
AG01Transaction forbiddenThis account cannot receive this payment type
AM04Insufficient fundsDebtor-side problem: funds, not data
AM05DuplicationSuspected duplicate: investigate before resending
BE04Missing creditor addressSupply the required party data (see structured addresses)
DT01Invalid dateCorrect the execution or value date
FF01Invalid file formatA message construction defect: fix the producer
MS02Not specified (customer generated)The counterparty’s customer refused, no detail
MS03Not specified (agent generated)A bank refused without detail: chase if habitual
RC01Bank identifier incorrectCorrect the BIC or clearing identifier
RR04Regulatory reasonCompliance-driven: usually deliberately unspecific
DUPLDuplicate payment (return/recall context)Money moved twice: reconcile and recover
TECHTechnical problem (recall context)Sender-side recall justification
FRADFraudulent origin (recall context)Fraud-driven recall: time-critical handling

Three reading rules keep the table honest. First, the same failure can carry different codes on different rails, because schemes constrain the set: verify against the rulebook in scope and against what your counterparties actually send. Second, context changes meaning: DUPL as a rejection reason stops a duplicate before settlement, while DUPL on a return or recall means money already moved and must come back, two very different operational days. Third, the unspecific codes are findings: a counterparty that habitually returns MS03 is exporting its ambiguity to your customer service desk, and that pattern is worth escalating, not just mapping.

Because the reason code becomes the customer’s next move, whether or not anyone designs it. AC01 tells the customer to check the number and try again; AC04 tells them the account is gone and retrying is futile; AM04 tells the debtor something, not the beneficiary; RR04 deliberately tells them very little, because compliance rejections should not explain themselves. A platform that renders all of these as “payment failed” manufactures repeat failures and support calls, and one that renders them precisely prevents both, which is why the mapping from cause to code to message deserves to be a maintained, tested artifact rather than tribal knowledge.

For the QA analyst, every row above is a test case with a specific assertion: submit the closed-account payment, assert RJCT with AC04, not AC01, not MS03, and assert the customer message that AC04 maps to. Systems routinely default to a generic code for every failure, and only testing the rejections reveals it; the elegant mapping in the spec is a hypothesis until the pacs.002 comes back.

The takeaway

ISO 20022 reason codes are the standard’s explanation layer: the prefix names the family, the code names the cause, and the cause implies the action, correct and retry for AC01, never-retry for AC04, investigate for AM05, escalate habitual MS03. The list is shared across rails but constrained per scheme, so the rulebook in scope is the final word, and the code your system actually emits is a claim you verify by testing, not by reading the spec.

About the author

Analyst Engineering is written by Ahmed, a Senior Technical Business Analyst with 10+ years of banking and payments delivery experience: ISO 20022 and SWIFT messaging, payments API integration, Kafka event validation, and production support. Every article comes from real delivery work, and each one is reviewed and updated as tools and standards change.

Newsletter

Subscribe

Practical, no-fluff playbooks for technical analysts who analyze, code, test, and support. New articles straight to your inbox.

No spam. Unsubscribe anytime.