Crypto On and Offramp Integration Guide

Seamless conversion between fiat and crypto - end to end technical integration guide

Introduction

Zero Hash's Crypto On and Offramp product allows any business to quickly launch an experience that allows their end customers to convert between fiat and crypto assets, and vice-versa.

Definitions

TermDefinition
PlatformThe company that's in contract with Zero Hash and directly interacts with Zero Hash's API's or SDK's.
End CustomerThe end retail customer that has a direct relationship with the Platform and Zero Hash.

High Level Flow

  1. Submit End Customer
  2. Query End Customer
  3. Fund Float Account
  4. Get Assets
  5. Get Quote
  6. Execute Quote
  7. Query Withdrawal
  8. Network Fee Procedures
  9. Email Receipt Requirements
  10. End of Day Settlement

Initial Setup

The Platform will need to onboard its business to the Zero Hash platform. They then need to add additional users, create API keys, and whitelist IP addresses. See instructions here.

1. Submit End Customer

Once API keys are created and approved, the Platform can begin to integrate to the API. For the following examples, we’ll assume the Platform's platform code is PLAT01.

The Platform submits the End Customer via POST /participants/customers/new:

{
    "first_name": "John",
    "last_name": "Smith",
    "email": "[email protected]",
    "phone_number": "+12345834789",
    "date_of_birth": "1985-09-02",
    "ip_address": "999.168.252.199",
    "address_one": "1 Main St.",
    "address_two": "Suite 1000",
    "city": "Chicago",
    "zip": "12345",
    "jurisdiction_code": "US-IL",
    "partial": true,
    "sanction_screening": "pass",
    "sanction_screening_timestamp": 1603378501282,
    "signed_agreements": [{
        "type": "user_agreement",
        "region": "us",
        "signed_timestamp": 1603378501286
    }],
}

Note: The Sender will be agreeing to the Zero Hash terms and conditions. The platform indicates to Zero Hash that they accepted the terms by sending a value in the signed_agreement object as shown above.

  • type: the type of user agreement being signed. For Remittances, the Sender is signing the standard user agreement, so a value of user_agreement is expected.
  • signed_timestamp: is the timestamp that the Sender accepted the terms and conditions.
  • region: represents the Zero Hash entity that the Sender is entering into a contract with. The only acceptable value right now is US.

POST /participants/customers/new response:

 {
     "first_name": "John",
     "last_name": "Smith",
     "email": "[email protected]",
     "address_one": "1 Main St.",
     "address_two": "Suite 1000",
     "country": "United States",
     "state": "IL",
     "jurisdiction_code": "US-IL",
     "city": "Chicago",
     "zip": "12345",
     "date_of_birth": "1985-09-02",
     "id_number_type": "unknown",
     "id_number": "",
     "non_us_other_type": "",
     "id_issuing_authority": "",
     "signed_timestamp": -62135596800000,
     "risk_rating": "",
     "metadata": {},
     "platform_code": "PLAT01",
     "participant_code": "CUST01",
     "tax_id": "",
     "citizenship": "",
     "citizenship_code": "",
     "kyc": "unknown",
     "kyc_timestamp": null,
     "onboarded_location": "US-IL",
     "sanction_screening": "pass",
     "sanction_screening_timestamp": null,
     "liveness_check": "unknown",
     "phone_number": "+12345834789",
     "employment_status": "unknown",
     "industry": "unknown",
     "source_of_funds": "unknown",
     "signed_agreements": [{
         "region": "us",
         "signed_timestamp": 1603378501,
         "type": "user_agreement"
     }]
 }

Note: The response contains a participant_code for the End Customer. This uniquely identifies the End Customer indefinitely and should be use in subsequent API calls where applicable. We’ll use CUST01 as the participant_code throughout the examples.

2. Query End Customer

The Platform can query the End Customer via the GET /participants/{email} endpoint or the Platform can query all End Customers via the GET /participants endpoint

3. Fund Fiat Accounts

There are 3 types of fiat accounts needed for this flow:

  • Float Account: The account that will fund real-time onramp transactions. Onramp transactions will fail if the balance in this account is insufficient.
  • Settlement Reserve Account: The account that will be used to top-up the settlement account, once a day
  • Settlement Account: The account that will be credited 1x a day in order to complete daily settlement. The deposit into this account will be initiated by the Payment Service Provider

For the following examples, we’ll assume Platform's platform code is PLAT01.

The Platform will fund each of the above accounts by sending fiat to the proper bank account, tagging each wire with a memo equal to the Platform's participant_code. Including the wire memo means that we can auto-credit the correct account when we receive the wire/ACH.

Here are the account details:

Float AccountSettlement Reserve AccountSettlement Account
Participant code00SCXM[Platform's participant code][Platform's participant code]
Account group[Platform's participant code][Platform's participant code][Platform's participant code]
Account labelgeneralsettlement_reservegeneral
Account typeAvailableAvailableAvailable
AssetUSDUSDUSD
Wire MemoPLAT01 - FLOATPLAT01 - RESERVEPLAT01 - SETTLEMENT

For the example, let's say the Float Account is funded with $50,000 and the Settlement Reserve Account is funded with $25,000:

AssetCurrent BalanceChange
Float AccountUSD$50,000(+$50,000)
Settlement Reserve AccountUSD$25,000(+$25,000)

Note: For the Wire Memo, some payment providers don't support a fixed memo for the wire or ACH. In these cases please reach out to your ZH contact so we can investigate alternatives.

ℹ️

In Cert, your platform will be pre-funded with Float account funds.

4. Get Assets

In order to know which assets are support, the Platform can query the GET /assets. If "rfq_liquidity_enabled": true, then it is supported

5. Get Quote

The Platform will now get the initial quote via the POST /convert_withdraw/rfq endpoint:

{
	"participant_code" : "CUST01",
	"side" : "buy",
	"underlying" : "BTC",
	"quoted_currency" : "USD",
	"total" : 20,
	"withdrawal_address" : "2N8PYGKSQRpHa5VDNZ4iLwxi5crpRWb3TR1"
}

Response:

{
   "message": {
       "request_id": "64f34a8d-f5f5-4e37-b01d-7c6814aed4bc",
       "participant_code": "CUST01",
       "quoted_currency": "USD",
       "side": "buy",
       "quantity": "0.00087097",
       "price": "22962.90",
       "quote_id": "5aaaa8af-5d0c-4d55-868b-b56ea19a5443",
       "expire_ts": 1661449335103,
       "account_group": "00SCXM",
       "withdrawal_address" : "2N8PYGKSQRpHa5VDNZ4iLwxi5crpRWb3TR1"
       "account_label": "general",
       "obo_participant": {
           "participant_code": "CUST01",
           "account_group": "PLAT01",
           "account_label": "general"
       },
       "network_fee_notional": "1.50",
       "network_fee_quantity": "0.00001",
       "total_notional": "21.5",
       "underlying": "BTC",
       "asset_cost_notional": "20"
   }
}

Note:

  • fee_inclusive = true can be enacted to ensure that the End Customer spends a maximum of total and the fees will be deducted from there
  • Typically for onramps the quote length is 30 seconds, although this is configurable

6. Execute Quote

The platform will then execute a POST /convert_withdraw/execute call using the quote_id from the response above. Here is the is the response of this endpoint:

{
  "message": {
    "request_id": "945c41ba-c06e-4e69-aaf6-ea27cb7c75b2",
    "quote": {
      "request_id": "7a8a8526-3614-4373-9ab4-0069dc643109",
      "participant_code": "CUST01",
      "quoted_currency": "USD",
      "side": "buy",
      "quantity": "0.00087097",
      "price": "22962.90",
      "quote_id": "dc179192-5bdf-41f2-9ffa-badb1396a0a7",
      "expire_ts": 1568311649602,
      "account_group": "GRP001",
      "account_label": "general",
      "obo_participant": {
        "participant_code": "20XRLH",
        "account_group": "WRD1K0",
        "account_label": "general"
      },
      "network_fee_notional": "1.50",
      "network_fee_quantity": "0.00001",
      "total_notional": "21.5",
      "underlying": "BTC",
      "asset_cost_notional": "20",
      "withdrawal_address": "2N8PYGKSQRpHa5VDNZ4iLwxi5crpRWb3TR1",
      "payment_processor": {
        "name": "payment processor name",
        "id": "payment processor id"
      },
      "spread_notional": ".22",
      "spread_bps": "100",
      "withdrawal_request_id": "Dea97133e-ab15-4c86-86c1-86671b8420nr"
    },
    "trade_id": "ba97133e-ab15-4c86-86c1-86671b8420bc",
    "status": "Completed"
  }
}

Note: After this, fiat is being taken from the float account, a trade is being created and settled, and an automatic and immediate on-chain transfer is being sent to the withdrawal_address

7. Query Withdrawal

When monitoring a withdrawal, the Platform can query the GET /withdrawals/requests/Dea97133e-ab15-4c86-86c1-86671b8420nr endpoint.

Response:

{
    "id":"Dea97133e-ab15-4c86-86c1-86671b8420nr",
    "withdrawal_account_id": 152089,
    "participant_code": "CUST01",
    "account_group": "PLAT01",
    "account_label": null,
    "requestor_participant_code": "PLAT01",
    "asset": "BTC",
    "requested_amount": "20",
    "settled_amount": "20",
    "gas_price": null,
    "status": "SETTLED",
    "on_chain_status": "PENDING",
    "client_withdrawal_request_id": "zh_withdrawal_17845eeffd5f71abaa85e9722e76c931a",
    "requested_timestamp": 1659542191772,
    "transaction_id": "3899526c5f986297d3b2ba29f2c4c8b0fb486bef2fc76f7c11aae2b1faf3fe76",
    "input_data": null,
    "fee_amount": 0.00001,
    "quoted_fee_amount": 0.00001 
} 

ParmeterDescription
statusThe Zero Hash internal settlement status.

- PENDING: The request has been created and is pending approval from users.
- APPROVED: The request is approved but not settled.
- REJECTED: The request is rejected and in a terminal state.
- SETTLED: The funds have been deducted from the participan
on_chain_statusThe blockchain status.

- PENDING: The withdrawal has been broadcasted on-chain but is not yet confirmed.
- CONFIRMED: The withdrawal has been sufficiently confirmed on-chain.
transaction_idThe on-chain transaction hash, useful to display out to customers so that they can follow the transaction if they wish.

8. Network Fee Procedures

The Platform will be paying for the network fee associated with any Onramp transactions. There are 2 options:

  1. Fund the network fee account. Account details:
  • Participant_code: PLAT01 (the Platform's participant_code)
  • Account_group: PLAT01
  • Account_label: network_fee
  • Asset: USD

All network fees will paid out of this account in real-time

  1. Don’t fund the network fee account. This account will build up a payable balance

Zero Hash will add the amount of the receivable balance on this account to the end of month invoice, coupling it with the Onramp usage activity amount due.

9. Email Receipt Requirements

Zero Hash requires that the End Customer receives an email receipt for each transaction. We also insist that the email contain

  1. Certain fields and values, which can be obtained using our API
  2. Adequate support contact information

Fields

Here are the required field names and their associated API fields:

Email Receipt Field NameDescriptionExampleAPI Location
Order NumberUnique order identifier9a738372-0855-4b25-8c65-5de0aa858b8btrade_id from GET /trades
Order TypeThe type of orderOnramp TransactionN/a, can hard-code to "Onramp Transaction Transmission"
Transmission AmountThe transmission amount for the transaction, in quoted currency terms$10total POST /convert_withdraw/rfq or POST /convert_withdraw/execute response
Amount Received by End CustomerThe amount of the underlying crypto received by the End Customer10 USDCquantity POST /convert_withdraw/rfq or POST /convert_withdraw/execute response
FeesAny added fee values included in the transaction. If there are no fees, then this needs to be expressly stated.

This includes processing fees (ie, a fee assessed by the Platform to the End Customer) or blockchain network fees.
"$0", "1.99", "$0 Fees", or "No Fees"- any fees from POST /convert_withdraw/rfq or POST /convert_withdraw/execute response

- If the End Customer is incurring the network fee, take network_fee_notional from POST /convert_withdraw/rfq or POST /convert_withdraw/execute response
Date/TimeDate and timestamp of the transaction transmission (meaning the time that the fiat was converted and settled into crypto within the Zero Hash system)2024-10-11 18:38:00settled_timestamp from GET /trades
Account IDZero Hash’s unique account identifier for the End Customer ( the “participant code” within Zero Hash).CUST01participant_code from POST /convert_withdraw/rfq or POST /convert_withdraw/execute

15. End of Day Settlement:

Platform's will typically use a Payment Service Provider (PSP) to process the USD leg of the transaction. PSP's will be required to settle with Zero Hash on a daily basis (excluding weekends and holidays). PSP's will typically withhold both 1) chargebacks and 2) PSP fees. As a result, Zero Hash will receive a settlement amount that is less than the Net Delivery Obligation (NDO) - in this example the NDO is $500. To compensate for this, Zero Hash will sweep funds from the Settlement Reserve Account to the Settlement Account, completing end of day settlement.

Here are the account changes after the PSP's wire lands at Zero Hash:

AssetCurrent BalanceChange
Float AccountUSD$49,500
Settlement Reserve AccountUSD$25,000
End Customer AccountUSDC0
Platform USDC AccountUSDC0
Settlement AccountUSD$497.5+$497.5

In order to get the Settlement Account to the correct amount (500), Zero Hash will initiate a transfer from the Settlement Reserve Account to the Settlement Account:

AssetCurrent BalanceChange
Float AccountUSD$49,500
Settlement Reserve AccountUSD$24,997.5(-$2.5)
End Customer AccountUSDC0
Platform USDC AccountUSDC0
Settlement AccountUSD$500+$2.5

Once the NDO has been met, Zero Hash will automatically settle the $500 to the Float Account:

AssetCurrent BalanceChange
Float AccountUSD$50,000+$500
Settlement Reserve AccountUSD$24,997.5
End Customer AccountUSDC0
Platform USDC AccountUSDC0
Settlement AccountUSD$0-$500