Quickstart Guide (Sandbox)

A quick introduction to building with HIFI API in sandbox environment

Introduction

Welcome to the HIFI Quickstart Guide!

In this guide, we will walk you through the process of creating a user, adding onramp/offramp bank accounts for the user, and then doing transfers. By following along, you'll gain a clear understanding of how our endpoints work and how to integrate them into your application.

To get started, you'll need an API key, which you can get by signing up in the Dashboard .

You'll have two different API keys for two different HIFI environments. Today we'll start in the Sandbox environment with the sandbox API key.

Let's first create a user!

User

A user object represents either an individual or a business. All transactions and accounts are associated with a user object.

To create a user, you must first provide the user's personal information for KYC compliance purposes. The user must also review and accept HIFI's Terms and Service to obtain a valid signed agreement ID, which signifies a binding agreement to use our service.

Get a Valid Signed Agreement ID

To get HIFI's Terms and Service page, you can call the Generate Terms of Service Link endpoint. You will need to pass in an idempotencyKey, which can be any UUID. This key will be used as your signed agreement ID.

Request:

curl --request POST \
     --url https://sandbox.hifibridge.com/tos-link \
     --header 'accept: application/json' \
     --header 'authorization: Bearer zpka_XXXXX' \
     --header 'content-type: application/json' \
     --data '
{
  "idempotencyKey": "a4ca0421-f962-45d1-aea2-bf1da9836c73"
}
'

Response:

{
  "url": "https://dashboard.hifibridge.com/accept-terms-of-service?sessionToken=536bb03a-8ac9-4ba7-b928-461007ecf6eb&templateId=2fb2da24-472a-4e5b-b160-038d9dc82a40&sandbox=true",
  "signedAgreementId": "a4ca0421-f962-45d1-aea2-bf1da9836c73"
}

You will get a response object back containing the url and the signedAgreementId. The url directs you to HIFI's Terms of Service page. The signedAgreementId, which is the same as the idempotencyKey you passed, will only be valid after you accept the Terms of Service.

HIFI's Terms of Service page:

HIFI's default terms of service template. This page can be whitelabled with your organizations logo and brand colors.

Create User

Now that we have the user's valid signedAgreementId and personal information, we can create a user by calling the Create User endpoint. You can learn more about the required personal information here.

Request:

curl --request POST \
     --url https://sandbox.hifibridge.com/user/create \
     --header 'accept: application/json' \
     --header 'authorization: Bearer zpka_XXXXX' \
     --header 'content-type: application/json' \
     --data '
{
  "userType": "individual",
  "legalFirstName": "Ronnie",
  "legalLastName": "Coleman",
  "complianceEmail": "[email protected]",
  "compliancePhone": "+11234567890",
  "dateOfBirth": "1997-02-17",
  "taxIdentificationNumber": "123456789",
  "country": "USA",
  "govIdFront": "https://picsum.photos/200/300",
  "ipAddress": "108.28.159.21",
  "city": "Hoboken",
  "addressLine1": "123 Example St",
  "postalCode": "07030",
  "stateProvinceRegion": "NJ",
  "govIdCountry": "USA",
  "signedAgreementId": "a4ca0421-f962-45d1-aea2-bf1da9836c73"
}
'

Response:

{
  "wallet": {
    "walletStatus": "ACTIVE",
    "walletMessage": "",
    "actionNeeded": {
      "actions": [],
      "fieldsToResubmit": []
    },
    "walletAddress": {
      "POLYGON_AMOY": {
        "address": "0x9781A62Ad95f2A0D409c86304bF869E88d58972F"
      }
    }
  },
  "user_kyc": {
    "status": "PENDING",
    "actionNeeded": {
      "actions": [],
      "fieldsToResubmit": []
    },
    "message": "kyc aplication still under review"
  },
  "ramps": {
    "usdAch": {
      "onRamp": {
        "status": "PENDING",
        "actionNeeded": {
          "actions": [],
          "fieldsToResubmit": []
        },
        "message": "kyc aplication still under review",
        "achPull": {
          "achPullStatus": "PENDING",
          "actionNeeded": {
            "actions": [],
            "fieldsToResubmit": []
          },
          "message": ""
        }
      },
      "offRamp": {
        "status": "PENDING",
        "actionNeeded": {
          "actions": [],
          "fieldsToResubmit": []
        }
      }
    },
    "euroSepa": {
      "onRamp": {
        "status": "INACTIVE",
        "actionNeeded": {
          "actions": [],
          "fieldsToResubmit": []
        },
        "message": "SEPA onRamp will be available in near future"
      },
      "offRamp": {
        "status": "PENDING",
        "actionNeeded": {
          "actions": [],
          "fieldsToResubmit": []
        },
        "message": ""
      }
    }
  },
  "user": {
    "id": "4020689b-46ab-46d3-90b6-152ebc4d9ad2"
  }
}

Let's take a moment to understand the response:

  • The wallet object contains the status of the wallet that was provisioned for the user, including all the wallet addresses associated with the user.
  • The user_kyc object contains the user's KYC status, which is initially "PENDING" as the compliance screening process is done asynchronously (typically takes a few seconds).
  • The ramps object contains the status of the user's onramp and offramp capabilities for various payment rails. Onramp refers to the conversion of fiat money to digital currency. Offramp refers to the conversion of digital currency to fiat money. The key components are:
    • usdAch: Represents the user's status to onramp and offramp using USD via ACH.
    • euroSepa: Represents the user's ability to onramp and offramp using EURO via SEPA.
  • The user object contains the user's ID, which should be saved for future API calls for this particular user.

You'll notice that the status for the onramp and offramp fields within the ramps object are "PENDING" . This is because those features are only available to the user after they pass KYC.

Get User

After creating a user, you'll want to either register a webhook or poll the Get User endpoint to get the most up-to-date user KYC status.

Let's call the Get User endpoint with the user id we just created:

Request:

curl --request GET \
     --url 'https://sandbox.hifibridge.com/user?userId=4020689b-46ab-46d3-90b6-152ebc4d9ad2' \
     --header 'accept: application/json' \
     --header 'authorization: Bearer zpka_XXXXX'

Response:

{
  "wallet": {
    "walletStatus": "ACTIVE",
    "walletMessage": "",
    "actionNeeded": {
      "actions": [],
      "fieldsToResubmit": []
    },
    "walletAddress": {
      "POLYGON_AMOY": {
        "address": "0x9781A62Ad95f2A0D409c86304bF869E88d58972F"
      }
    }
  },
  "user_kyc": {
    "status": "ACTIVE",
    "actionNeeded": {
      "actions": [],
      "fieldsToResubmit": []
    },
    "message": ""
  },
  "ramps": {
    "usdAch": {
      "onRamp": {
        "status": "ACTIVE",
        "actionNeeded": {
          "actions": [],
          "fieldsToResubmit": []
        },
        "message": "",
        "achPull": {
          "achPullStatus": "ACTIVE",
          "actionNeeded": {
            "actions": [],
            "fieldsToResubmit": []
          }
        }
      },
      "offRamp": {
        "status": "ACTIVE",
        "actionNeeded": {
          "actions": [],
          "fieldsToResubmit": []
        }
      }
    },
    "euroSepa": {
      "onRamp": {
        "status": "INACTIVE",
        "actionNeeded": {
          "actions": [],
          "fieldsToResubmit": []
        },
        "message": "SEPA onRamp will be available in near future"
      },
      "offRamp": {
        "status": "ACTIVE",
        "actionNeeded": {
          "actions": [],
          "fieldsToResubmit": []
        },
        "message": ""
      }
    }
  },
  "user": {
    "id": "4020689b-46ab-46d3-90b6-152ebc4d9ad2"
  }
}

After polling the Get User endpoint for a few seconds, we see that the user has successfully passed KYC, which means the status for the onramp and offramp fields within the ramps object are now "ACTIVE" as well.

In the sandbox environment, user KYC will automatically pass. However, in production, KYC can fail for various reasons. For example, KYC might fail if the user's government ID is too blurry. To pass KYC, refer to the actionNeeded object to identify the required actions and the fields you need to resubmit through the Update User endpoint.


Account

After creating a user who has passed KYC, you can add two types of accounts to the user:

  • Onramp account: A bank account used as the source for the onramping process. For example, during onramping, fiat currency from the onramp account is converted into stablecoin.
  • Offramp account: A bank account used as the destination for the offramping process. For example, during offramping, stablecoin is converted into fiat currency and sent to the offramp account.

We will now add both onramp and offramp accounts for the user we just created.

Add Onramp Account

A Virtual Account is an onramp account created by our system to facilitate onramping. Users can deposit fiat money into the virtual account, and the deposited funds are automatically converted into stablecoin. To create a virtual account for the user, you can call the Create Onramp Virtual Account endpoint with the user id.

The rail parameter you pass in will determine the fiat currency and payment rails you want this onramp virtual account to support. For example, passing the rail parameter as US_ACH_WIRE will allow the user to deposit USD into the virtual account with ACH push or WIRE transfer, which will be automatically converted to USDC.

Let's make a Create Onramp Virtual Account call using the user id we created earlier, with the rail parameter set to US_ACH_WIRE:

Request

curl --request POST \
     --url 'https://sandbox.hifibridge.com/account/activateOnRampRail?userId=4020689b-46ab-46d3-90b6-152ebc4d9ad2' \
     --header 'accept: application/json' \
     --header 'authorization: Bearer zpka_XXXXX' \
     --header 'content-type: application/json' \
     --data '
{
  "rail": "US_ACH_WIRE"
}
'

Response

{
  "message": "Virtual account for US_ACH_WIRE created successfully",
  "account": {
    "virtualAccountId": "8b82b91e-8ef6-47fc-b2d5-f058a92e7f7e",
    "userId": "4020689b-46ab-46d3-90b6-152ebc4d9ad2",
    "paymentRails": [
      "ach_push",
      "wire"
    ],
    "sourceCurrency": "usd",
    "destinationChain": "POLYGON_AMOY",
    "destinationCurrency": "usdc",
    "destinationWalletAddress": "0x9781A62Ad95f2A0D409c86304bF869E88d58972F",
    "railStatus": "activated",
    "depositInstructions": {
      "bankName": "Bank of Nowhere",
      "routingNumber": "101019644",
      "accountNumber": "900868101397",
      "bankAddress": "1800 North Pole St., Orlando, FL 32801"
    }
  }
}

Let's take a look at the response, focusing on the virtual account object:

  • The virtualAccountId is the unique identifier for the newly created virtual account. This ID should be saved for future retrieval of account information, including deposit instructions and micro-deposit details required by the institution.
  • The paymentRails indicates the payment methods supported by this virtual account.
  • The sourceCurrency, destinationChain, and destinationCurrency together represents the complete onramp rail. In our case, any usd deposited into the virtual account will be converted to usdc and sent to destinationWalletAddress on the POLYGON_AMOY blockchain.
  • The railStatus reflects whether this virtual account is active for onramping.
  • IMPORTANT: The depositInstructions object contains the bank account details that the user needs to deposit fiat into for onramping.

Link Plaid Account for USD Onramp

You can also link a Plaid account to the virtual account for ACH pull by making a Link Plaid Account for USD Onramp call with a Plaid processor token. For this guide, we've provided you with a Plaid processor token in the request fields. If you'd like to learn how to generate a Plaid processor token, you can follow the Plaid guide.

Please note that linking a Plaid account alone will not allow you to onramp, as onramping requires a virtual account. If you've been following this guide and have already made a Create Onramp Virtual Account call earlier, then a virtual account has already been created for you. If you don't have a virtual account yet, you can set the createVirtualAccount request parameter to true to have one automatically created.

In essence, linking a plaid account for USD onramp allows you to ACH pull funds from the user's Plaid bank account whenever they want to onramp, eliminating the need for manual deposits into the virtual account.

Now, let's call the Link Plaid Account for USD Onramp endpoint using the user ID we created earlier to link a Plaid account:

Request

curl --request POST \
     --url 'https://sandbox.hifibridge.com/account/usd/onramp/plaid?userId=4020689b-46ab-46d3-90b6-152ebc4d9ad2' \
     --header 'accept: application/json' \
     --header 'authorization: Bearer zpka_XXXXX' \
     --header 'content-type: application/json' \
     --data '
{
  "accountType": "CHECKING",
  "createVirtualAccount": false,
  "plaidProcessorToken": "processor-sandbox-57edb9cd-003b-4048-81da-451838a82d27",
  "bankName": "Bank of America"
}
'

Response

{
  "status": "ACTIVE",
  "invalidFields": [],
  "message": "Bank account added successfully",
  "id": "625d0f77-7445-4d25-be7b-933678ca0ab8"
}

The id returned in the response object is the unique identifier for the linked Plaid account for USD onramp. This ID should be saved for future use whenever you want to initiate an onramp through an ACH pull from the Plaid account.

Add Offramp Account

To offramp, you can add a USD offramp bank account by making an Add USD Offramp Bank Account (ACH) call or an Add USD Offramp Bank Account (Wire) call, depending on whether you want to offramp via ACH or WIRE transfer.

Let's add a USD offramp bank account for ACH. To do this, you'll need to provide your bank account details. However, for the purpose of this guide, we've pre-configured the bank account details for you, so all you need to do is call the Add USD Offramp Bank Account (ACH) endpoint:

Request:

curl --request POST \
     --url 'https://sandbox.hifibridge.com/account/usd/offramp?userId=4020689b-46ab-46d3-90b6-152ebc4d9ad2' \
     --header 'accept: application/json' \
     --header 'authorization: Bearer zpka_XXXXX' \
     --header 'content-type: application/json' \
     --data '
{
  "currency": "usd",
  "accountOwnerType": "individual",
  "bankName": "Chase",
  "accountOwnerName": "Henry Wu",
  "accountNumber": "123456789012",
  "routingNumber": "021000021",
  "streetLine1": "123 Main St.",
  "city": "New York",
  "state": "NY",
  "postalCode": "10001",
  "country": "USA"
}
'

Response:

{
  "status": "ACTIVE",
  "invalidFields": [],
  "message": "Account created successfully",
  "id": "a3500c23-13b6-4626-aaf1-cc8596363e29"
}

The id returned in the response object is the unique identifier for the USD offramp bank account (ACH). This ID should be saved for future use whenever you want to initiate an offramp through an ACH push.


Transfer

After creating both onramp and offramp accounts, the user can now perform three types of transfers/conversions:

  • Onramp Fiat to Stablecoin: Convert fiat currency from an onramp bank account to stablecoin.
  • Transfer Stablecoin to Stablecoin: Transfer stablecoin between users or wallet addresses.
  • Offramp Stablecoin to Fiat: Convert stablecoin to fiat currency and send it to an offramp bank account.

In this section of the quick start guide, we will walk through the entire transfer flow from onramp to offramp between two users. The first user (User A) will be the user we just created, and the second user (User B) will be an existing user we've provided for the purpose of this guide.

Here's how the entire transfer flow will look like in three steps:

  1. Onramp $1 USD from User A's Plaid bank account to User A's wallet as 1 USDC.
  2. Transfer the 1 USDC from User A's wallet to User B's wallet.
  3. Offramp User B's 1 USDC to User B's bank account as $1 USD.

Please note that in the sandbox environment, no real money movement occurs, so the onramping and offramping won't actually process real funds. However, all the request and response examples will provide a clear overview of how the transfer occurs.

Onramp $1 USD to 1 USDC

To onramp fiat to stable coin, you will make a Convert Fiat to Stablecoin call.

Let's take a look at the request fields:

  • The requestId is a unique identifier for the transfer request, ensuring that a request is processed only once to prevent duplicates.
  • The sourceUserId represents the user from whom we are onramping, and the sourceAccountId is the onramp account owned by sourceUserId. In our case, sourceUserId will be User A, and the sourceAccountId is the linked Plaid Account for USD onramp that we obtained earlier.
  • The destinationUserId represents the user to whom we want to onramp the funds. In our case, this will be User B's user id.
  • The sourceCurrency, destinationCurrency, chain, and amount specify that we are onramping $1 usd to usdc on the POLYGON_AMOY blockchain.
  • The isInstant is a future feature, so you can ignore it for now.

Request:

curl --request POST \
     --url https://sandbox.hifibridge.com/transfer/fiat-to-crypto \
     --header 'accept: application/json' \
     --header 'authorization: Bearer zpka_XXXXX' \
     --header 'content-type: application/json' \
     --data '
{
  "requestId": "39eea9ba-9edc-4afa-b250-48921e9b05ef",
  "sourceUserId": "4020689b-46ab-46d3-90b6-152ebc4d9ad2",
  "sourceAccountId": "625d0f77-7445-4d25-be7b-933678ca0ab8",
  "destinationUserId": "4020689b-46ab-46d3-90b6-152ebc4d9ad2",
  "sourceCurrency": "usd",
  "destinationCurrency": "usdc",
  "chain": "POLYGON_AMOY",
  "isInstant": false,
  "amount": 1
}
'

Response:

{
  "transferType": "FIAT_TO_CRYPTO",
  "transferDetails": {
    "id": "e24c0c83-cedb-4491-8675-66f26b8e91a4",
    "requestId": "39eea9ba-9edc-4afa-b250-48921e9b05ef",
    "sourceUserId": "4020689b-46ab-46d3-90b6-152ebc4d9ad2",
    "destinationUserId": "4020689b-46ab-46d3-90b6-152ebc4d9ad2",
    "chain": "POLYGON_AMOY",
    "sourceCurrency": "usd",
    "amount": 1,
    "destinationCurrency": "usdc",
    "sourceAccountId": "625d0f77-7445-4d25-be7b-933678ca0ab8",
    "createdAt": "2024-08-16T17:02:38.029127+00:00",
    "status": "FIAT_SUBMITTED",
    "isInstant": false,
    "fee": null
  }
}

Let's take a look at the response object, focusing on the transferDetails object:

  • The id is a unique identifier for this transfer, which you can save to get the most up-to-date transfer status through the Get a Fiat-to-Stablecoin Transfer Record endpoint.
  • The status represents the transfer status, which initially appears as "FIAT_SUBMITTED". You will want to either register a webhook or poll the Get a Fiat-to-Stablecoin Transfer Record endpoint to monitor the status until the transfer is "CONFIRMED".
  • The rest of the fields are the same as the request fields you provided when calling the endpoint.

Transfer 1 USDC Between Wallets

After the onramp transfer is confirmed, User A will have 1 USDC in their wallet. Now we can transfer the 1 USDC from User A's wallet to User B's wallet. To do this, we can call the Transfer Stablecoin to Stablecoin endpoint.

Let's take a look at the request fields:

  • The requestId is a unique identifier for the transfer request, ensuring that a request is processed only once to prevent duplicates.
  • The senderUserId represents the user who wants to send the stablecoin. In our case, this is User A's user id.
  • The recipientUserId represents the user who will receive the stablecoin. In our case, this is User B's user id.
  • The chain, currency, and amount fields indicate that we are sending 1 usdc on the POLYGON_AMOY blockchain.

Request:

curl --request POST \
     --url https://sandbox.hifibridge.com/transfer/crypto-to-crypto \
     --header 'accept: application/json' \
     --header 'authorization: Bearer zpka_XXXXX' \
     --header 'content-type: application/json' \
     --data '
{
  "requestId": "4a67126c-18f7-4053-8b67-935f5bbff2e5",
  "senderUserId": "4020689b-46ab-46d3-90b6-152ebc4d9ad2",
  "recipientUserId": "bfdd6277-933b-4d8f-968f-0e660988a1ad",
  "chain": "POLYGON_AMOY",
  "currency": "usdc",
  "amount": 1
}
'

Response:

{
  "transferType": "CRYPTO_TO_CRYPTO",
  "transferDetails": {
    "id": "85a86d1a-c707-4cb5-9e9e-5985cf8c908c",
    "requestId": "4a67126c-18f7-4053-8b67-935f5bbff2e5",
    "senderUserId": "4020689b-46ab-46d3-90b6-152ebc4d9ad2",
    "recipientUserId": "bfdd6277-933b-4d8f-968f-0e660988a1ad",
    "recipientAddress": "0xDe96360DC190708eC645b9D308f33c3886F45e02",
    "chain": "POLYGON_AMOY",
    "currency": "usdc",
    "transactionHash": "0x68cb7038c7285d9fb7d49951d910ff766be33d1f9476ce38c1e1d6f29e23be10",
    "createdAt": "2024-08-16T17:07:21.298959+00:00",
    "updatedAt": "2024-08-16T17:07:21.298959+00:00",
    "status": "CREATED",
    "contractAddress": "0x41e94eb019c0762f9bfcf9fb1e58725bfb0e7582",
    "failedReason": null,
    "recipient": {
      "business_name": null,
      "legal_last_name": "Man",
      "compliance_email": "[email protected]",
      "legal_first_name": "Post"
    },
    "sender": {
      "business_name": null,
      "legal_last_name": "Coleman",
      "compliance_email": "[email protected]",
      "legal_first_name": "Ronnie"
    },
    "fee": null
  }
}

Let's take a look at the response object, focusing on the transferDetails object:

  • The id is a unique identifier for this transfer, which you can save to get the most up-to-date transfer status using the Get a Stablecoin-to-Stablecoin Transfer Record endpoint.
  • The status represents the transfer status, which initially appears as "CREATED". You will want to either register a webhook or poll the Get a Stablecoin-to-Stablecoin Transfer Record endpoint to monitor the latest transfer status until the transfer is "CONFIRMED".
  • The transactionHash is the transaction hash on the blockchain, which you can use to check the transaction status online with a blockchain explorer.
  • The contractAddress represents the smart contract address that facilitates the stablecoin transfer between wallets.
  • The failedReason will show the reason if the transfer fails.
  • The sender and recipient objects provide detailed information about the sender and recipient users.
  • The rest of the fields are the same as the request fields you provided when calling the endpoint.

Offramp 1 USDC to $ 1 USD

After transferring 1 USDC from User A's wallet to User B's wallet, we can offramp the USDC to User B's offramp bank account. To do this, we can call the Convert Stablecoin to Fiat endpoint.

Let's take a look at the request fields:

  • The requestId is a unique identifier for the transfer request, ensuring that a request is processed only once to prevent duplicates.
  • The sourceUserId represents the user from whom we want to offramp. In our case, this is User B's user id.
  • The destinationUserId represents the user receiving the offramp funds, and the destinationAccountId is the receiving offramp account owned by destinationUserId. In our case, the destinationUserId will be User B's user id, and the destinationAccountId will be User B's USD offramp account id.
  • The chain, sourceCurrency, destinationCurrency, paymentRail, and amount fields indicate that we are converting 1 usdc on the POLYGON_AMOY blockchain to usd and sending that usd to destinationAccountId through ach push.

Request:

curl --request POST \
     --url https://sandbox.hifibridge.com/transfer/crypto-to-fiat \
     --header 'accept: application/json' \
     --header 'authorization: Bearer zpka_XXXXX' \
     --header 'content-type: application/json' \
     --data '
{
  "requestId": "2cd8684a-b806-4f9a-9156-19156123c07d",
  "sourceUserId": "bfdd6277-933b-4d8f-968f-0e660988a1ad",
  "destinationUserId": "bfdd6277-933b-4d8f-968f-0e660988a1ad",
  "destinationAccountId": "5d62b9e3-e922-43a8-984b-be69517fe275",
  "chain": "POLYGON_AMOY",
  "sourceCurrency": "usdc",
  "destinationCurrency": "usd",
  "paymentRail": "ach",
  "amount": 1
}
'

Response:

{
  "transferType": "CRYPTO_TO_FIAT",
  "transferDetails": {
    "id": "cf7fd329-4d64-46b6-827d-3bd1aa767af6",
    "requestId": "2cd8684a-b806-4f9a-9156-19156123c07d",
    "sourceUserId": "bfdd6277-933b-4d8f-968f-0e660988a1ad",
    "destinationUserId": "bfdd6277-933b-4d8f-968f-0e660988a1ad",
    "chain": "POLYGON_AMOY",
    "sourceCurrency": "usdc",
    "amount": 1,
    "destinationCurrency": "usd",
    "liquidationAddress": "0xdeadbeef2usdcpolygon889d0beb-81ef-4698-ba5c-a8b80720fcd4",
    "destinationAccountId": "5d62b9e3-e922-43a8-984b-be69517fe275",
    "transactionHash": "0xa1f68897d8fe99a7009312f40dea258401a1749d28e308d3937397d6463b2084",
    "createdAt": "2024-08-16T17:11:59.772231+00:00",
    "updatedAt": "2024-08-16T17:11:59.772231+00:00",
    "status": "CREATED",
    "contractAddress": "0x41e94eb019c0762f9bfcf9fb1e58725bfb0e7582",
    "sourceUser": {
      "business_name": null,
      "legal_last_name": "Man",
      "compliance_email": "[email protected]",
      "legal_first_name": "Post"
    },
    "destinationUser": {
      "business_name": null,
      "legal_last_name": "Man",
      "compliance_email": "[email protected]",
      "legal_first_name": "Post"
    },
    "destinationAccount": {
      "id": "5d62b9e3-e922-43a8-984b-be69517fe275",
      "account_owner_name": "Post Man",
      "bank_name": "Chase",
      "account_number": "123456789012",
      "routing_number": "021000021",
      "account_type": "us",
      "business_identifier_code": null,
      "bank_country": null,
      "iban": null,
      "beneficiary_first_name": null,
      "beneficiary_last_name": null
    },
    "failedReason": null,
    "fee": null,
    "conversionRate": {
      "vaildFrom": "2024-08-16T17:11:59.760Z",
      "toCurrency": "usd",
      "vaildUntil": "2024-08-16T17:12:29.760Z",
      "fromCurrency": "usdc",
      "conversionRate": 1
    }
  }
}

Let's take a look at the response object, focusing on the transferDetails object:

  • The id is a unique identifier for this transfer, which you can save to get the most up-to-date transfer status using the Get a Stablecoin-to-Fiat Transfer Record endpoint.
  • The status represents the transfer status, which initially appears as "CREATED". You will want to either register a webhook or poll the Get a Stablecoin-to-Fiat Transfer Record endpoint to get the latest transfer status until the transfer is "CONFIRMED".
  • The liquidationAddress is a wallet address created by our system to facilitate offramping and is tied to destinationAccountId. Stablecoins that are sent to this liquidation address will be converted to fiat currency and sent to the corresponding offramp bank account.
  • The transactionHash is the transaction hash on the blockchain, which you can use to check the transaction status online with a blockchain explorer.
  • The contractAddress represent the smart contract address that facilitates the stablecoin transfer between wallets.
  • The failedReason will show the reason if the transfer fails.
  • The sourceUser and destinationUser objects provide detail information about the sender and recipient users.
  • The destinationAccount object provides detailed information about the offramp bank account.
  • The conversionRate object provides the conversion rate information between the source and destination currencies.
  • The rest of the fields are the same as the request fields you provided when calling the endpoint.

Congratulations! You've successfully navigated several key processes, including creating a user, adding onramp/offramp accounts, and transferring funds between users. We hope this guide has provided you with a solid understanding of how to utilize HIFI's API endpoints to manage digital currency transfers seamlessly. If you have any further questions or need additional support, please refer to our documentation or contact our support team. Happy coding!