Card Issuing
Real-time Decisioning
Column allows your platform to make card authorization decisions in real-time. When Column receives authorization requests from the card networks, the Bank will send a webhook to your system and await a decision. This functionality can be used in conjunction with Column's authorization policy API to ensure only permitted transactions are approved.
Decisioning Flow
When an approval request is received by Column from the card networks, the following approval process is followed:
- Column will match the request to a Card & Card Account in the system. If Column cannot resolve the request to a card, it is automatically declined. If your platform is allocated an entire BIN, Column can send an event webhook when an authorization is declined due to an invalid account number on your behalf.
- Column will check the status of the Card & Card Account, and if the status is not active, Column will decline the transaction with the appropriate decline code and send your system an event webhook.
- Column checks the account type, address verification (AVS) results, and currency of the request, and declines the transaction if any of these checks fail.
- Column evaluates any authorization policies configured by your system using the Authorization Policy API. If a policy is triggered, Column will decline the transaction on your behalf and send an event webhook.
- Column performs fraud checks on the request.
- Column will perform a balance check to ensure sufficient funds are available, if not, the transaction will be declined, and an event webhook will be sent to your system. On debit card programs with just-in-time funding, Column sends the transaction to your system instead so you can fund it. Other funding models such as transfer upon approval can be configured, please reach out to design a flow of funds if these models are needed.
- Column will send a Real-time decision webhook to your system and will wait a total of 3 seconds for a response. If no response or an invalid response is received, Column will decline the transaction.
Note: all decisioning information is included in the decisioning_details struct in the Card Transaction Event.
Webhook Setup
To enroll an endpoint to receive real-time decisioning events the existing webhook endpoints API can be used with
the specific event type of decision.issuing.card_transaction. Real-time decisioning events will have the same
Column-Signature header that can be used to validate the request originated from Column.
It is recommended to use a different endpoint for real-time decisioning that is optimized for latency since there is a 3-second deadline for responses.
Column sends each decision request to exactly one endpoint: the one with the most specific matching subscription.
Wildcard subscriptions match too. An endpoint subscribed to the exact event type wins, then decision.issuing.*, then
decision.*, then *. For example, if one endpoint is subscribed to * and another to decision.*, only the decision.*
endpoint receives authorization requests. Subscribe a dedicated endpoint to decision.issuing.card_transaction so
authorization requests aren't sent to an endpoint that can't return a decision.
The endpoint must return the real-time decision response shape, such as a decision value of approve, decline, or
partially_approve. Responses that do not include a recognized decision value are treated as invalid and Column will
decline the authorization. If you are using the local example webhook server, register the authorization route
/webhooks/cards/authorizations for this event type. This route is distinct from the token provisioning routes, such as
/webhooks/cards/tokens.
Note: if your system fails to respond to a real-time decisioning request, Column will not retry the request and decline
the authorization. Column also has a deadline to respond to the card networks, and it does not provide sufficient time for
retries. Column will send a webhook to notify you of the decline, and the decision_reason in decisioning_details
indicates what went wrong:
realtime_authorization_endpoint_missingdecision.issuing.card_transaction.realtime_authorization_request_timeoutrealtime_authorization_connection_errorrealtime_authorization_invalid_responsedecision value or an invalid partial approval.errordecision_source is platform_owner.Real-time Decision Object
The real-time decision object contains the fields card_transaction, proposed_event, and, when Column has computed the
card account's balance, available_balance. If the decision relates to a new transaction, the transaction and proposed
event will both be created after the response is returned. If the decision relates to an existing event (for example, the
request is for an incremental auth), the card_transaction will contain the existing card transaction, and the proposed
event will be appended to the existing transaction.
3DS Authentication Decisioning
You can also make real-time decisions during 3DS authentication, before a challenge is shown. Subscribe a webhook endpoint
to decision.issuing.card_authentication. Column sends the
3DS authentication decision request to that
endpoint with the authentication attempt and available network context. Return a
3DS authentication decision response with
one of these decisions:
frictionless_approverecommends that the network's risk system authenticate without a cardholder challenge.challengeproceeds to a cardholder challenge.decline_authenticationrejects the authentication. You may returndecline_reasonassuspected_fraudortransaction_not_permitted; Column defaults tosuspected_fraudwhen the reason is omitted.
Column waits up to 1 second for a valid response. A timeout, connection error, non-success response, or invalid response
fails safe to challenge. This differs from card authorization decisioning, which waits up to 3 seconds and declines when
your endpoint does not return a valid decision. Column does not retry either decision request within the network deadline.
frictionless_approve is a recommendation to the network's risk system and might not produce a frictionless result in
every flow. A challenge decision fails authentication if the cardholder has no supported challenge method, such as an
email address on the person entity. Data-only authentications with merchant challenge indicator 06 do not invoke your
decision endpoint.
3DS Events
Whether or not you use 3DS authentication decisioning, Column sends these webhooks for 3DS authentications:
issuing.3ds.otp_generatedotp_value to the cardholder through otp_method_type, using the matching otp_email or otp_phone_number.issuing.3ds.authentication_completedattempt_status is authenticated or rejected.issuing.3ds.data_only_received06).The payloads are the 3DS attempt, the 3DS OTP generated webhook payload, and the 3DS data-only authentication objects. See Card 3DS Events.
Sandbox Testing
Call POST /simulate/issuing/cards/:card_id/threeds/challenge in sandbox. Set realtime_decision to exercise a specific
decision without calling your endpoint, and optionally set decline_reason for a declined authentication. Omit
realtime_decision to invoke a sandbox endpoint subscribed to decision.issuing.card_authentication. If no subscribed
endpoint exists, the simulator preserves its default behavior and creates an OTP challenge.
Decline Reasons
If you decide to decline a transaction, you can optionally specify a decline_reason to provide more information to the
merchant who originated the request. Generally, it is best practice and more user-friendly for cardholders to include a
reason for a decline. Additionally, some card networks assess additional fees if general decline codes are used.
If a reason isn't specified in the real-time decision response, Column will return a generic "do not honor" response code.
Partial Approvals
Some card networks and merchants support partial approvals. This allows your platform to approve a subset of the requested amount. An example of this is if a debit card had a remaining balance of $10 but is used to purchase a $50 item. If you opt to partially approve $10, the merchant will apply $10 and then request an alternate form of payment for the remaining $40.
In the real-time decisioning request, the field can_support_partial_approvals field indicates whether the merchant supports
partial approvals. You can then return the decision choice of partially_approve with an amount set in partially_approve_amount.
If your system returns partially_approve when it is unsupported, without an amount, with a zero amount, or with an amount
greater than the requested amount, Column will decline the transaction with the realtime_authorization_invalid_response
reason. A partially_approve response with an amount equal to the requested amount is recorded as a partial approval of
the full amount.
Partial approvals are not supported for incremental authorizations or reauthorizations. Column declines a
partially_approve response to those requests with the realtime_authorization_invalid_response reason.
For automated fuel dispenser transactions that support partial approvals, if the card account's available balance is lower
than the requested amount, Column limits the proposed event to the available balance before sending the real-time decision
request instead of declining for insufficient funds. Respond with partially_approve (or decline) in this case; an
approve response is declined with the realtime_authorization_invalid_response reason.
Just-in-time Funding
Just-in-time funding allows you to fund debit accounts on-the-fly when there are insufficient funds in the account to
cover an authorization. Column enables it per debit card program, shown by jit_funding_enabled on the
card program; in Sandbox, set it when you
create a card program. Credit and charge card programs cannot use it.
On these programs, a purchase that exceeds the card account's available balance is not declined at the balance check.
Column sends it to your real-time decisioning endpoint, with the balance in available_balance. To fund it, respond
with approve or partially_approve and the bank account to fund it from:
{
"decision": "approve",
"funding_account_id": "bacc_2x8gszy5folpA9s0TOCseE9ABDM"
}Column moves the shortfall (the approved amount minus the available balance, plus any amount the card account is
overdrawn) from the funding account into the card account's bank account, then approves the authorization. Nothing
moves when the available balance already covers the approved amount. The card transaction event's
decisioning_details.funding_account_id records the account you named either way.
The funding account must be a checking account on your platform in the card account's currency, must not be frozen or closed, and cannot be the card account's own bank account. Otherwise Column declines the authorization:
insufficient_fundsfunding_account_id.realtime_authorization_invalid_funding_accountfunding_account_insufficient_fundsrealtime_authorization_invalid_responsefunding_account_id is not a bank account ID.Only initial purchase authorizations and single-message financial transactions are funded. Incremental authorizations and reauthorizations are still declined for insufficient funds when the card account cannot cover them.
The card transaction's funding_details records the amount funded in
funded_amount. As the hold is released by a reversal, an expiration, or a capture for less than the authorized amount,
Column returns the released amount to the funding account, up to the amount still funded, and records it in
returned_amount. If a release is later reversed, such as by a clearing reversal, Column moves the returned funding back
into the card account and records it in restored_amount. Funding moves as part of the card transaction rather than as
a separate transfer: each movement appears on both accounts' statements and activity as a debit card line of the card
transaction event that moved it, described as Debit card authorization funding or Debit card authorization funding returned.