Sending Transactions from the API
Introduction
Although sending a transaction from the Custody Infrastructure API may seem unintuitive at first, it only takes a few calls to obtain all the information you need and ensure the transaction is sent successfully. This article will walk you through that process one call at a time. By the end of the article, you will be able to create transactions and send them to the blockchain via the API.
This is an API process. Be sure you are comfortable making API calls and interpreting the responses. See Getting Started with the API for details.
This is a transfer process. Make sure Custody Infrastructure system policies permit your API user to perform transfers. See Policy Engine for details.
Summary
This article covers:
- What is a "Transaction"
- How it Works
- Detailed Process Diagram
- Prerequisite Information
- Call-by-Call Walkthrough
- Transaction Parameters
- Endpoint Deep Dive*
- Further Reading
*Endpoint Deep Dive - Expanded
- getCoins
- availableBalance
- getLimits
- getAllContacts
- getSuggestedFee
- getGasLimitSuggestedForTransaction
- getCurrentToken
- send_____Transaction (We use Ethereum as an example)
- getTransactionByToken

What is a "Transaction"
All "transactions" discussed in this article are, technically, transaction requests. Once sent by the API user, transaction requests are subject to approval according to specific account and general platform policies. Your transactions will not be submitted to the blockchain until appropriate approval. If a transaction request is not approved before its timeout period expires, the request will be canceled, and the transaction will need to be recreated. See MPC Approval Workflows to see how the approval process is configured.
The default timeout for a transaction request is 300 minutes or 5 hours.
How it Works
This series of API calls concludes with the creation of a transaction request. Once approved, the request is sent to the blockchain. Like any transaction, it must be picked up by miners or validators and confirmed on-chain before it can be considered final.
Properly configuring the fees to reflect the current state of the blockchain greatly increases the transaction's chance of being quickly added to the blockchain.
%%{
init: {
'theme': 'base',
'themeVariables': {
'primaryColor': '#9B69FF',
'primaryTextColor': '#EFEFEF',
'secondaryColor': '#CCCCCC',
'secondaryTextColor': '#CCCCCC',
'lineColor': '#9B69FF',
'arrowColor': '#9B69FF',
'fontFamily': 'Arial, sans-serif, bold',
'fontSize': '16px',
}
}
}%%
flowchart TD
A([Transaction Request]) --> B{Internal Approval Process}
B -->|<span style="color: black;">Approved</span>| C[Request Becomes Transaction]
B -->|<span style="color: black;">Denied</span>| G([RequestCancelled])
C --> D[Mempool]
D --> E[Picked up by Miner/Validator]
E --> F([Transaction On-Chain])
style A fill:#9B69FF,color:#fff
style B fill:#9B69FF,color:#fff,rounded
style C fill:#9B69FF,color:#fff,rounded
style D fill:#9B69FF,color:#fff,rounded
style E fill:#9B69FF,color:#fff,rounded
style F fill:#007BFF,color:#fff
style G fill:#FF0000,color:#fff
The Transaction Procedure
First, you assess the state of your account and the current cost of blockchain activity:
- How much currency do you have?
- How much can you move?
Next, you retrieve the transaction token information. This will let you track the transaction you are about to send.
Finally, you can send and track a transaction, confident that you have the funds and are knowledgeable about your policy limits.
Detailed Process Diagram
%%{
init: {
'theme': 'base',
'themeVariables': {
'primaryColor': '#9B69FF',
'primaryTextColor': '#FFFFFF',
'primaryBorderColor': '#9B69FF',
'secondaryColor': '#CCCCCC',
'secondaryTextColor': '#000000',
'secondaryBorderColor': '#9B69FF',
'lineColor': '#9B69FF',
'arrowColor': '#9B69FF',
'fontFamily': 'Arial, sans-serif, bold',
'fontSize': '16px',
'textColor': '#000000'
}
}
}%%
sequenceDiagram
API User ->> API: getCoins
API ->> Internal System: Retrieve Coins for Accounts
Internal System -->> API: Return Account Info with Coins
API -->> API User: Return Account Info
API User ->> API: availableBalance
API ->> Internal System: Check Available Balance
Internal System -->> API: Return Available Balance
API -->> API User: Return Available Balance
API User ->> API: getLimits
API ->> Internal System: Check User Limits
Internal System -->> API: Return Transfer Limits
API -->> API User: Return Transfer Limits
API User ->> API: getAllContacts
API ->> Internal System: Retrieve Configured Contacts
Internal System -->> API: Return Contact List
API -->> API User: Return Contact List
API User ->> API: getSuggestedFee
API ->> Blockchain: Query Suggested Fee & Gas Limits
Blockchain -->> API: Return Suggested Fee
API -->> API User: Return Suggested Fee
API User ->> API: getGasLimitSuggestedForTransaction
API ->> Blockchain: Query Suggested Gas Limit for Transaction
Blockchain -->> API: Return Suggested Gas Limit
API -->> API User: Return Suggested Gas Limit
API User ->> API: getCurrentToken
API ->> Internal System: Retrieve Current Transaction Token
Internal System -->> API: Return Transaction Token
API -->> API User: Return Transaction Token
API User ->> API: send_____Transaction
API ->> Blockchain: Transmit Transaction to Blockchain
API -->> API User: Transaction Transmitted Successfully (No Errors)
API User ->> API: getTransactionByToken
API ->> Blockchain: Query Transaction Status by Token
Blockchain -->> API: Return Transaction Status
API -->> API User: Return Transaction Status
The request approval process is not included in the above diagram as it varies from user to user and organization to organization and depends on how you have configured your policy. If the API user's transactions are subject to approval, the approval process occurs after the user sucessfully calls send____Transaction.
Prerequisite Information
Before starting the process, make sure you have access to the following information. You will need it for creating the transaction request and you won't be able to glean it from the API:
- account_id: int: You can get a list of all the acounts on the system, but it won't tell you which account_id is for which account name. Know the ID number of the account you want to work with before begining this procedure.
- coin_types: list: A number of endpoints will need you to specify what coin(s) you want to work with. Use the official coin symbol (such as ETH or BTC).
- cold_data_id or mpc_data_id: Optional[str]: Lets the system know what the most recent state of the contacts list was when you last checked. If you provide a xxx_data_id string, the API call will return only what has changed since you last checked the contact list.
- use_cache: Check the cache rather than the chain to return faster results, but the cached return can't reflect any changes since the last time the system checked the chain.
- outputs: list: You will need to list every specific output by destination and amount. It often makes sense to consolidate multiple transactions into one to save on fees. Specifying multiple outputs allows you to do exactly that.
- specificFromAddress: Optional[str]: If you are sending from a segregated account with multiple wallets, you can specify which wallet you want to draw the funds from with this parameter.
Call-by-Call Walkthrough
The process below highlights the necessary fields to note for each step.
Call getCoins. This will show you what coins you have to work with as well as their current fiat values. Running this step is only recommended after upgrading the client or adding a new currency, or when you need to know the current fiat value of your active currencies.
Call availableBalance. This lets you know how much of a given asset you have in the account. Combine this with the information from getCoins to determine the fiat value of your assets.
Call getLimits. The AmountLeft and FeeLeft show you how close you are to your periodic spending and fee limits; this is the upper bounds of what you can transact. See Limits for details.
Call getAllContacts. Filter it with your account_id to check and see whether your intended destination is already configured as a contact for the account. If the destination is not configured, see Addresses and Address Groups. If your user is governed by a policy that lets you send to "any address," you can skip this step. You can check policy settings in the desktop GUI.
Call getSuggestedFee. There are two parameters here.
- use_cache lets the system know if you want to view the same answer you retrieved last time you checked the fees, or if want to query the chain for current fees.
- coin_type sets what currency you are checking fees for.
Whether to check the chain or the cache depends on the validity of the asset you are transacting and the last time you ran getSuggestedFee without the cache, forcing a node check. If you feel the information in your cache is current enough, there is no need to check a node.
NOTE: getSuggestedFee returns the parameters 'suggestedFee' and 'payment_amount'. These can be used to calculate the gas price for the transaction. If you do not have a gas price, it can be calculated during the send______transaction call.
Call getGasLimitSuggestedForTransaction if you are sending an asset that requires gas. Note the suggested parameters. You will need them when creating the transaction request.
Call getCurrentToken. You need to print the result or it won't be displayed in the log. Make a note of the return (we recommend assigning it to a variable you can recall later). This token can be used to track your next transaction on-chain without leaving the API.
Call send______Transaction. Each currency has its own "send____Transaction" endpoint and set of parameters. Some parameters are used for all transactions, such as accountId. Others are unique to specific assets. See Transaction Parameters for a detailed list and guidance on the parameters.
When sending a Casper (CSPR) transaction, sendCasperTransaction requires a gas_price value. The gas_price parameter is calculated as the payment_amount divided by the suggestedFee value received from the getSuggestedFee endpoint call: gas_price = payment_amount / suggestedFee.
The transaction request is created, and a notification is automatically sent to eligible approvers.
Endpoint Deep Dive
{
"FunctionData": {},
"Meta": {
"AccessToken": "3c4e...5a11",
"Destination": "6f8f...cb28",
"FunctionName": "getCoins",
"RandomData": "342f...4fac",
"Source": "045b...45c0",
"Time": "2024-09-29 19:26:15.136466"
}
}For more information about this endpoint, see getCoins.
apiUser.availableBalance(1,["ETH"])For more information about this endpoint, see availableBalance.
apiUser.getLimits()For more information about this endpoint, see getLimits.
apiUser.getAllContacts()For more information about this endpoint, see getAllContacts.
apiUser.getSuggestedFee(False, ["ETH"])For more information about this endpoint, see getSuggestedFee.
apiUser.getGasLimitSuggestedForTransaction(1, "ETH", [("0x12Cf2442d3BDa42d461b18FE7185dc284E5f1b8A", "6000")])For more information about this endpoint, see getGasLimitSuggestedForTransaction.
current_token = apiClientExamples.apiUser.getCurrentToken()
print("your token is: " + current_token)For more information about this endpoint, see getCurrentToken.
apiUser.sendEthereumTransaction(1, [("0x12Cf2442d3BDa42d461b18FE7185dc284E5f1b8A", "600000")],3000000000, 21000, 1000000000)For more information about this endpoint, see sendEthereumTransaction.
apiUser.getTransactionByToken("f192762c5733e1771320ab2f169788816765d0f9776c53651f9cdc7b54958aec","ETH")For more information about this endpoint, see getTransactionByToken.
Further Reading
- To understand API responses, see API Responses
- To interpret the output of TransactionFormatVersion.V2., see Webhook v2 Output