Transaction Decisioning

Since all Subnet transactions are not originated through your platform, developers like to have higher level of control on transactions settling into user accounts. Especially withdrawals.

For that reason, we enable higher level of control on Subnet withdrawals, to address three needs:

  1. Ability to Pass/Fail a transaction based some custom criteria built on your side.

  2. Ability to fund the transaction through one or more accounts (funding nodes) at the time of transaction decisioning, not before.

    1. Additional ability to fund a pre-auth approved transaction to safe guard against variable final total amounts (example tip on a restaurant bill).

  3. If the network allows, only approve a transaction partially.

We also enable decisioning for incoming card based credits, but with stricter criteria:

  1. The transaction can not be a cash deposit.

  2. You can only Pass/Fail the transaction. You can not pass funding nodes or partial approval amounts.

  3. Must not be a force post transaction.

Set up

Transaction Decisioning is handled via a webhook request response flow. To enable an endpoint to receive a webhook when a Subnet withdrawal transaction is received, create a subscription with TRANS|POST|JIT scope. So something like this:

{
  "scope": [
    "TRANS|POST|JIT"
  ],
  "url": "https://webhook.site/f765c7b8-4a73-4407-89e7-e1a6794004e8"
}

To make the most of Transaction Decisioning (JIT), you will need to build out the appropriate logic on the platform side (e.g. restrict by transaction type, merchant profile, MCC, or other logic). Please review Meta Schema of RDFI ACH and Meta Schema of Card Transactions to find all the information we return.

Decisioning

To be able to decision a transaction, you need to respond in the following format:

Field

Type

Required

Description

decision

String

Yes

Supply PASS if you intend to let the transaction go through. Otherwise supply FAIL.

funding_nodes.node_id

String

No

Node ID where the supplied amount should be funded from. Not supported for credits.

funding_nodes.amount

Number

No

The amount that needs to be taken out of the supplied node id. Not supported for credits.

total_amount

Number

No

Total amount approved for the transaction. Can be lower than the amount on transaction if partial approval is allowed. Not supported for credits

additional_funding

String

No

In a pre-auth scenario, if there is a remaining balance, the remainder will be taken from the node(s) defined here. Not supported for credits.

Basic Transaction Decisioning

Upon receiving a supported subnet transaction, we will notify you via the TRANS|POST|JIT webhook url. Your response will indicate to us whether to allow ({"decision": "PASS"}) or deny ({"decision": "FAIL"}) the transaction.

If the transaction is approved (PASS), then funds can be pulled directly from the account that the subnet sits on top of.

Example Payloads

{"decision": "PASS"}

or

{"decision": "FAIL"}

Funding via Multiple Accounts (Withdrawal only)

If you wish to fund the transaction from an account other than the account the subnet sits on top of, then you can pull funds directly from one or more funding nodes (either user or platform accounts) specified in the response.

  • If funding for the transaction comes from multiple accounts (listed in the funding_nodes array supplied in the response), then you must both specify the exact amount to be pulled from each node and ensure that the overall transaction's total_amount field equals the sum of the amounts pulled from each funding node. If both of these criteria are not met, the transaction will silently fail.

  • Please also note that providing an invalid node_id for any of the listed funding nodes will also cause the transaction to silently fail.

Example Payloads

Single Node Funding

{
  "decision": "PASS",
  "total_amount": "<TOTAL_AMOUNT>",
  "funding_nodes": [
   {"node_id": "<NODE_ID>", "amount": "<AMOUNT>"}
 ]
}

Multiple Funding Nodes via JIT

{
  "decision": "PASS",
  "total_amount": "<TOTAL_AMOUNT>",
  "funding_nodes": [
   {"node_id": "<NODE_ONE_ID>", "amount": "<AMOUNT_ONE>"},
   {"node_id": "<NODE_TWO_ID>", "amount": "<AMOUNT_TWO>"}
   ]
}

Pre-Auth with Additional Funding (Withdrawal only)

The additional_funding nodes deduction only occurs if the total_amount increased and does nothing if the total_amount decreases.

{  
  "decision": "PASS",
  "total_amount": "<TOTAL_AMOUNT>",
  "funding_nodes": [
   {"node_id": "<NODE_ONE_ID>", "amount": "<AMOUNT_ONE>"},
   {"node_id": "<NODE_TWO_ID>", "amount": "<AMOUNT_TWO>"}
   ],
   "additional_funding": [{"node_id": "<NODE_ID>"}]
}

Partial Approval (Withdrawal only)

If using Partial Approval, the response will need to also supply the partially approved transaction's total_amount. If a transaction can be partially approved, you will see to.meta.partial_approval_allowed set to True. Go to Meta Schema of Card Transactions to learn more.

Testing

To be able to test Transaction Decisioning on UAT, you will need to be able to create fake subnet transactions. For card subnets, you can do that with Virtual Terminal. While for account number subnets, you can do that with Dummy Transactions API calls.

Important Considerations

Following are some things to be mindful of while building with Transaction Decisioning:

Funding Outcome

Transaction will still settle if there are insufficient funds. However, if there are insufficient funds and another cancel reason it will fail.

Timely Response Considerations

Upon being notified of a new transaction request by the TRANS|POST|JIT webhook you will need to perform your custom transaction logic, provide funding (if using Instant Auth), and provide a response within 1.00 seconds, or else the transaction will follow the default path of decisioning, in other words, as if JIT did not exist.

Negative Balance Considerations

Funding from other nodes does not perform balance checks on the nodes due to the timely response considerations mentioned above (i.e. response required within 1.00s), meaning that use of this feature with a funding node that has insufficient funds to cover a transaction will make that node's balance go negative.

It is your responsibility to both perform periodic balance checks and to maintain an estimate of funding node balances to avoid this (i.e. to have a reasonable expectation that a funding node can cover a transaction before allowing it).

Debit Card Cashback Considerations

Consider how to handle transactions with a debit card cashback component (e.g. from a user who took a cash disbursement along with their purchase at a retail register). This information should be provided in the webhook's transaction request notification (e.g. {"type": "PURCHASE_WITH_CASH_DISBURSEMENT"} with sub_amounts array containing {"amount_type": "AMOUNT_CASH"}). You can either approve the full transaction amount, provide Partial Approval only for the purchased goods, or deny the transaction.

KYC Considerations

We will perform basic account and user KYC checks (e.g. making sure the user has SEND-AND-RECEIVE permissions and that the user watchlists flag is not a MATCH) prior to ever issuing a request from the TRANS|POST|JIT webhook. This means that you should not receive requests from users who are unable to transact.

Incoming card credits

When we receive an incoming card based credit transaction (Examples: CashApp or Venmo transfers), we will generate a webhook for these transactions. This allows you to control if and when you allow users to do things like transfer funds from a CashApp account to their Synapse account. Any transaction that involves a CASH deposit will not generate a webhook. Refund transactions and any others that might be force posts also will not generate a webhook as those can not be refused. Not responding to these webhooks at all, or not responding within the expected time frame, will allow the transaction to be deposited like normal.

Last updated