A client matching on the text of an error breaks when the wording is improved, translated or corrected — and every one of those will happen.
{
"error": {
"code": "payment.card_declined",
"message": "Your card was declined.",
"retryable": false,
"fields": { "card_number": ["invalid_checksum"] }
}
}
The stable identifier separate from the human string costs nothing at the point the first error is designed and cannot be retrofitted once clients exist. retryable earns its place quickly: without it every client invents its own rule about which failures deserve a retry button, and those rules disagree with each other and with the server. Per-field codes rather than sentences are what let a form highlight the right input in the right language.