ACH Powered-Trading RFQ Integration Guide
A standard flow for a Platform is using Zero Hash Liquidity Services to source liquidity and enable digital asset buys and sells through their application. To make this experience simple for Platforms looking to integrate we have bundled ACH funding and disbursements as part of our trade execution using ZHLS.
Why use this?
We are hearing from Platforms that they need more flexibility when trying to manage settlement and collection of fiat related to buying and selling of crypto. With this feature Platforms can easily facilitate the fiat money movement required to fund trades while leveraging Zero Hash's licensing and compliance.
Platforms can easily track the status of the ACH transaction using two new endpoints that we have enabled - POST /payments/status
and POST /payments/history
. Using the Payments Status endpoint, Platforms can easily get the most recent status about the ACH transaction. If a Platform would like additional detail about the payment, the Payments History endpoint will show additional detail as well as a record of all state changes of the transaction.
Flow and API Sequencing
Here's an example:
- Zero Hash integrated platform, "Platform A" with participant code PLAT01, onboards a new customer, "Customer B" with participant code CUST01.
- Platform A will need to link Customer B's bank account.
- Customer B requests to purchase $10 of BTC on Platform A and would like to fund the trade using the linked bank account.
- Platform A will generate a quote and execute the trade creating the ACH transaction.
- Platform A will then be able to check the status of the ACH transaction.
Step 1 - link a Customer's external bank account
The Platform needs to link a Customer's external bank account using POST /payments/external_accounts endpoint. This will return an external_account_id
that will be used in future steps to help generate an ACH transaction.
Example Request
{
"participant_code": "CUST01",
"account_number": "123456789",
"account_type": "checking",
"routing_number": "211374020",
"account_nickname": "Primary Checking"
}
Example Response
{
"request_id":"f4f607d0-e5bf-45e3-b6f6-d6271e834fce",
"external_account_id":"b76c3095-3e9e-4de3-85a4-adc0697f46ce",
"participant_code":"CUST01",
"platform_code":"PLAT01",
"account_nickname":"Primary Checking",
"account_type":"checking",
"created_at":"2022-05-23T16:20:22.918Z"
}
Step 2 - Generate a quote using GET /liquidity/rfq
Example Request
GET /liquidity/rfq?underlying=BTC"ed_currency=USD&side=buy&total=10.00&participant_code=CUST01
Example Response
{
"account_group": "00SCXM",
"account_label": "general",
"expire_ts": 1655299072148,
"obo_participant": {
"account_group": "PLAT01",
"account_label": "general",
"participant_code": "CUST01"
},
"participant_code": "PLAT01",
"price": "21464.2941466869861985",
"quantity": "0.00046589",
"quote_id": "9c7b95ce-a973-4147-b832-f2985f9929b8",
"quoted_currency": "USD",
"request_id": "e7d03b95-3df7-4776-a049-07c8543ce66c",
"side": "buy",
"underlying": "BTC",
"asset_cost_notional": "10.00"
}
Step 3 - Execute the the quote using POST /liquidity/execute making sure to include the funding_details sub-object - doing this will tell Zero Hash to generate the corresponding ACH transaction.
Example Request
{
"quote_id": "9c7b95ce-a973-4147-b832-f2985f9929b8",
"funding_details": {
"external_account_id": "b76c3095-3e9e-4de3-85a4-adc0697f46ce",
"description": "CRYPTOTRD",
"bank_fee": "1.25"
}
}
external_account_id
is used to indicate what bank account we shall direct the ACH transaction.description
is a 10 character field that should be used to identify the reason for the ACH transaction.bank_fee
allows the Platform to collect a fee along with the ACH. For example if BUY trade is for $10.00 and thebank_fee
is $1.25, we will generate an ACH debit entry for $11.25 collecting the value of the trade plus thebank_fee
.
Example Response
{
"ach_details": {
"bank_fee": "1.25",
"external_account_id": "b76c3095-3e9e-4de3-85a4-adc0697f46ce",
"payment_amount": "11.25",
"transaction_id": "75e55408-25c0-4020-a45b-89153caf571b"
},
"quote": {
"account_group": "00SCXM",
"account_label": "general",
"expire_ts": 1655307279582,
"obo_participant": {
"account_group": "PLAT01",
"account_label": "general",
"participant_code": "CUST01"
},
"participant_code": "CUST01",
"price": "21304.245936215087667",
"quantity": "0.00046939",
"quote_id": "9c7b95ce-a973-4147-b832-f2985f9929b8",
"quoted_currency": "USD",
"request_id": "62da6480-3f43-4ccd-9363-f35cc39a51ac",
"side": "buy",
"transaction_timestamp": 1655307275215,
"underlying": "BTC",
"asset_cost_notional": "10.00"
},
"request_id": "a619ee6b-5c83-4241-a8ab-fb081fdcd966",
"status": "Completed",
"trade_id": "a31f69ca-5998-44b7-9a1d-869b8130fa40",
"trade_ids_list": ["a31f69ca-5998-44b7-9a1d-869b8130fa4"]
}
Step 4 - Platforms can then check the status of the ACH transaction
Platforms can then check the status of the ACH transaction using GET /payments/status
to make sure the ACH funding transaction processed successfully. We recommend checking on the status of the ACH transaction to ensure it has processed successfully. This can become important in the event a Platform wishes to places restrictions on movements of the crypto asset based on funding.
Example Request
GET /payments/status?trade_id=a31f69ca-5998-44b7-9a1d-869b8130fa40
Example Response
[
{
"amount": "10.00",
"bank_transfer_id": "e927c5fc-39bc-e3d5-f932-9176e1737cd7",
"created_at": "2022-06-15T15:34:36.147Z",
"participant_code": "CUST01",
"status": "posted",
"transaction_id": "75e55408-25c0-4020-a45b-89153caf571b",
"transfer_type": "debit",
"updated_at": "2022-06-16T15:34:36.155Z"
}
]
ACH transactions will take one business day to move to a posted status. Returns can take longer depending on the type of ACH return.
Updated about 8 hours ago