Handling declines
How kepa classifies declines, what it retries automatically, and what needs the customer.
Most card-not-present declines are recoverable: the funds land a day later, or a retry through a different acquirer clears. How you handle one depends on how it’s classified.
Decline classification
Every result carries a category (a normalized reason, stable across card networks) and a retryClass indicating whether the payment is worth attempting again, rather than a raw network code.
{
"outcome": "DECLINED",
"category": "INSUFFICIENT_FUNDS_OR_LIMIT",
"retryClass": "SCHEDULED_RETRY",
"customerMessage": "The card has insufficient funds or has exceeded a limit."
}INSUFFICIENT_FUNDS_OR_LIMIT51 · 61 · 65Funds or a velocity limit, usually temporary.SCHEDULED_RETRYISSUER_OR_NETWORK_UNAVAILABLE91 · 96The issuer or network didn't answer.IMMEDIATE_CASCADEEXPIRED_CARD54The card is past its expiry.CUSTOMER_ACTION_REQUIREDAUTHENTICATION_FAILED55 · 75Wrong PIN or failed authentication.CUSTOMER_ACTION_REQUIREDDO_NOT_HONOR05 · 57 · 62 · 63A generic issuer refusal: restricted or suspected fraud.NEVERPICKUP_CARD04 · 07 · 41 · 43The issuer wants the card retained, often lost or stolen.NEVERRetry classes
Two of the four retry classes recover the payment for you, and they work differently:
IMMEDIATE_CASCADE: the issuer or network didn’t answer, so kepa reroutes the same payment to another acquirer right away. Nothing was wrong with the card.SCHEDULED_RETRY: a temporary decline like insufficient funds. kepa reattempts on a schedule (by default the next day, then a day later, then once more), since the funds often land in between.
Either way you do nothing; a later attempt clears and you get a webhook when it does, or the payment is dead-lettered once the policy’s attempts run out so it can’t retry forever.
The other two are terminal. NEVER (do-not-honor, restricted, pickup) will not clear on a retry, and reattempting it counts against you with the issuer; kepa doesn’t retry these, and neither should you. CUSTOMER_ACTION_REQUIRED (an expired card or a failed authentication) needs the customer, not another attempt.
Retry policy
The retry schedule, the number of attempts, the cascade order across acquirers, and the final action when attempts run out (a payment that never clears is dead-lettered so it can’t retry forever) are all governed by a retry policy. There is a sensible default; the knobs exist for when you need them. None of it requires a line of code on your side.
What needs the customer
The CUSTOMER_ACTION_REQUIRED and NEVER classes are about the customer, not the code:
EXPIRED_CARD: ask for a new card. For a subscription, this is the moment to send the customer to the billing portal.DO_NOT_HONOR: show the neutralcustomerMessagekepa returns (“The card was declined. Use another payment method.”). Never surface the raw network code; issuers keep the real reason vague to the merchant on purpose.
Retry safely
When you do reattempt a payment yourself, send a fresh Idempotency-Key per attempt. Reusing one returns the original decline instead of trying again. A retry is a new attempt; give it a new key.