Integrate with Garden API
If you get stuck during implementation, reach out in our Townhall—our dev team is ready to help!
This cookbook guides you through integrating Garden into your wallet backend, bridge aggregator, or infrastructure service using our API. You'll learn how to authenticate users, create and initiate cross-chain swaps, and track and redeem them on the destination chain. If you're unsure whether to use the SDK or APIs, see this comparison.
We'll demonstrate a complete swap flow from Bitcoin Testnet4 (tBTC) to Arbitrum Sepolia (WBTC), showing how to coordinate with Garden APIs at each step.
For a full reference implementation, see this page—a working terminal UI in Rust that demonstrates these steps in a real application.
Authenticate
Before placing or interacting with orders, your system must authenticate the user. Garden supports two authentication methods:
Option 1: Direct SIWE
Garden uses Sign-In with Ethereum (SIWE) to verify wallet ownership.
- Request a nonce
Generate a unique, single-use nonce as a challenge.
Endpoint:
POST /auth/siwe/challenges
Expected response:
{
"status": "Ok",
"result": "a34f6521d29cb6f0febbef3c0799f1b8213f85162fa206a535e5e11424c87b43"
}
- Sign the nonce
The user signs the nonce with their EVM wallet, generating a structured SIWE message. This is done by creating a local signer instance (e.g., using 'PrivateKeySigner' from the Alloy crate in Rust), which uses the user's private key to sign the message. The signed message ensures:
- The nonce is unique and prevents replay attacks.
- The user's private key remains secure.
SIWE message format:
<domain> wants you to sign in with your Ethereum account:
<account_address>
Garden.fi
URI: <url>
Version: 1
Chain ID: <chain_id>
Nonce: <the unique nonce from the previous step>
Issued At: <current_time>
Example for testnet:
localhost:4361 wants you to sign in with your Ethereum account:
0x7E5F4552091A69125d5DfCb7b8C2659029395Bdf
Garden.fi
URI: http://localhost:4361
Version: 1
Chain ID: 11155111
Nonce: a34f6521d29cb6f0febbef3c0799f1b8213f85162fa206a535e5e11424c87b43
Issued At: 2025-04-09T07:29:20.203Z
- Verify the signed message
Send the signed message to obtain a JSON web token (JWT).
Endpoint:
POST /auth/siwe/tokens
Request:
{
"message": "<message_string>",
"signature": "<hex_encoded_signature>",
"nonce": "<unique_nonce_message>"
}
Expected response:
{
"status": "Ok",
"result": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoiMHhlOTg5MmE4QTNENTk4MTREYjE0OTkxOEFEOWZGNDI4MmMyRDk5MUU4IiwiZXhwIjoxNzQ0MzYyMDIwfQ.iHiPUkeaNBkoUPhDQLqYkP6-6F3NtdmqrfozpafX8oM"
}
Option 2: API key
API keys offer a convenient way to authenticate if you prefer to manage authentication in-house or need persistent access without requiring users to sign messages repeatedly.
If you need an API key, please reach out to us on Townhall and ping us, or raise a support ticket.
Include the API key in your request headers as "api-key" with the generated key as its value. This replaces JWT bearer tokens for authentication.
api-key: <your_api_key>
Order lifecycle
This section outlines the swap lifecycle and how each step maps to specific API calls.
When a user requests to swap assets, start by retrieving a price quote for the order pair (source chain, source asset, destination chain, destination asset, and amount). After confirming the quote, the order is attested and validated by Garden's API, then created via the /relayer/create-order
endpoint.
For a detailed breakdown of the order lifecycle, including status transitions, see the order lifecycle. For a deeper understanding of the Garden protocol, read the intent flow.
Get supported pairs
The first step is to display all available options. Use the /quote/strategies
endpoint to get all supported pairs from Garden. Each pair has a unique ‘id’.
Endpoint:
GET /quote/strategies
Example response:
{
"status": "Ok",
"result": {
"btyrasac": {
"id": "btyrasac",
"source_chain_address": "1db36714896afaee20c2cc817d170689870858b5204d3b5a94d217654e94b2fb",
"dest_chain_address": "0x29f72597ca8a21F9D925AE9527ec5639bAFD5075",
"source_chain": "bitcoin_testnet",
"dest_chain": "arbitrum_sepolia",
"source_asset": {
"asset": "primary",
"token_id": "bitcoin",
"decimals": 8
},
"dest_asset": {
"asset": "0x795Dcb58d1cd4789169D5F938Ea05E17ecEB68cA",
"token_id": "bitcoin",
"decimals": 8
},
"makers": [],
"min_amount": "10000",
"max_amount": "100000",
"min_source_timelock": 12,
"min_source_confirmations": 1,
"min_price": 1.0001,
"fee": 30
},
... other supported pairs ...
}
}
Define the order pair
based on user selection in this format:
source_chain:source_asset::destination_chain:destination_asset
For our tBTC to WBTC (Arbitrum) example:
bitcoin_testnet:primary::arbitrum_sepolia:0x795Dcb58d1cd4789169D5F938Ea05E17ecEB68cA
Get quote
Request solver quotes for the expected output (or required input) amount using the selected order pair
and specifying the amount
in sats.
Endpoint:
GET /quote/?order_pair=<source_chain:source_asset::dest_chain::dest_asset>&amount=<desired_in_amount>&exact_out=boolean
exact_out
lets you choose whether to specify what you want to spend or what you want to receive.
- exact_out=false: Specify input amount (e.g., send 0.01 BTC → get X WBTC)
- exact_out=true: Specify desired output (e.g., get 10 WBTC → send X BTC)
For our tBTC to WBTC (Arbitrum) example:
price?order_pair=bitcoin_testnet:primary::arbitrum_sepolia:0x795Dcb58d1cd4789169D5F938Ea05E17ecEB68cA&amount=10000&exact_out=false
Example response:
{
"status": "Ok",
"result": {
"quotes": {
"btyrasac": "9970"
},
"input_token_price": 77328.3666062084,
"output_token_price": 77328.3666062084
}
}
The response provides asset prices (in USD) from market oracles and solver quotes for the specified amount. If exact_out
is false
, the quote reflects the output (receive) amount for the user.
In atomic swaps, funds are locked on-chain using a secret hash, which is the SHA256 hash of a randomly generated secret.
Garden offers two approaches for managing the secret and its hash:
Garden-managed secret: Garden generates and manages the secret and secret hash internally.
- Redemptions are done automatically.
Developer-managed secret: You can generate and manage the secret and hash yourself.
- The developer must manually perform redemption calls.
- setup instant refund for bitcoin swaps
After receiving a quote, you can proceed with either a Garden-managed secret or a Developer-managed secret flow, depending on what best suits your integration needs.
Garden-managed secret
Create order
When ready, create an order by sending a request to /relayer/create-order
. This creates the order in Garden's orderbook. The response includes the unique order ID
for tracking and managing the order.
Endpoint:
POST /relayer/create-order
Example request for tBTC to WBTC:
{
"source_chain": "bitcoin_testnet",
"destination_chain": "arbitrum_sepolia",
"source_asset": "primary",
"destination_asset": "0x795Dcb58d1cd4789169D5F938Ea05E17ecEB68cA",
"initiator_destination_address": "0x753Cf575A0224c590ACF3587031E88b238261f7a",
"source_amount": "10000",
"destination_amount": "9970",
"nonce": "1743337081630",
"additional_data": {
"strategy_id": "btyrasac",
"bitcoin_optional_recipient": "tb1qz0pnh98kfynptg9dtkj06f7sqnlxl3dxnmnjw4",
}
}
- Since the source is Bitcoin, initiator_source_address is not required.
- If the destination is Bitcoin, initiator_destination_address is not required.
Expected response:
{
"result": <order-id>,
"status": "Ok",
}
Get order data
To retrieve order data for an order ID:
Endpoint:
GET /orders/id/:id/
Expected response:
{
"status": "Ok",
"result": {
"created_at": "<timestamp>",
"updated_at": "<timestamp>",
"deleted_at": null,
"source_swap": {
"created_at": "<timestamp>",
"updated_at": "<timestamp>",
"deleted_at": null,
"swap_id": "<swap_id_hash>", /* the htlc script address */
"chain": "<chain_name>",
"asset": "<asset_type>",
"initiator": "<initiator_address>",
"redeemer": "<redeemer_address>",
"timelock": "<timelock_value>",
"filled_amount": "<amount>",
"amount": "<amount>",
"secret_hash": "<secret_hash>",
"secret": "",
"initiate_tx_hash": "<transaction_hash>",
"redeem_tx_hash": "",
"refund_tx_hash": "<transaction_hash>",
"initiate_block_number": "<block_number>",
"redeem_block_number": "0",
"refund_block_number": "<block_number>",
"required_confirmations": "<number>",
"current_confirmations": "<number>"
},
"destination_swap": {
"created_at": "<timestamp>",
"updated_at": "<timestamp>",
"deleted_at": null,
"swap_id": "<swap_id_hash>", /* the htlc script address */
"chain": "<chain_name>",
"asset": "<asset_type>",
"initiator": "<initiator_address>",
"redeemer": "<redeemer_address>",
"timelock": "<timelock_value>",
"filled_amount": "<amount>",
"amount": "<amount>",
"secret_hash": "<secret_hash>",
"secret": "",
"initiate_tx_hash": "<transaction_hash>",
"redeem_tx_hash": "",
"refund_tx_hash": "<transaction_hash>",
"initiate_block_number": "<block_number>",
"redeem_block_number": "0",
"refund_block_number": "<block_number>",
"required_confirmations": "<number>",
"current_confirmations": "<number>"
},
"create_order": {
// order metadata
}
}
}
If the order is unmatched, this endpoint returns null.
Initiate order
The order initiation process begins when the user initiates the swap on the source chain. For EVM-based chains, use the /relayer/initiate
endpoint. The process differs for Bitcoin, which uses scripts.
Initiating on EVM:
To initiate, the user must sign the HTLC initiation message using their wallet provider's EIP-712 typed data signing method. This signature authorizes the HTLC contract to lock tokens in escrow with the specified parameters. The signature is used for contract initialization.
- The user must call the
eip712domain()
function on the HTLC contract that returns the eip712 domain data. Sample EIP712domain data:
[ eip712Domain method Response ]
name string : HTLC
version string : 1
chainId uint256 : 42161
verifyingContract address : 0x6b6303fAb8eC7232b4f2a7b9fa58E5216F608fcb
- The initiation message must be in the following format:
HTLC initiation message format:
{
address redeemer;
uint256 timelock;
uint256 amount;
bytes32 secretHash;
}
- The message, along with the domain data, must be signed with the wallet, and the signature is sent with the initiate request to the relayer.
Endpoint:
POST /relayer/initiate
Example request:
{
"order_id" : "<order_id>",
"signature": "<signature_hex_string>",
"perform_on": "Source"
}
On success, the response contains the transaction hash of the initiate event.
Expected response:
{
"result": "<tx_id>",
"status": "Ok"
}
Initiating on bitcoin:
On Bitcoin, initiation can be done in two ways:
- Fund the HTLC script address directly using a Bitcoin wallet.
- Construct a transaction paying the required amount to the HTLC script address and broadcast it.
The HTLC script address is the swap ID of the source chain swap, found in the source_swap
field in the /orders/id/:id
response.
Redeem asset
Redemption is handled automatically for the user. No manual action is required to redeem tokens.
If the secret is managed by the developer, redemptions must also be handled by the developer.
Developer managed secret
Generate secret and secret Hash
The secret hash is a SHA-256 hash of a randomly generated secret.
The secret must be hashed using the SHA-256 algorithm. This hash is used for initiating and redeeming swaps.
Example:
secret = "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" //randomly generated hex string
| SHA256 hashed
v
secret_hash = "5df6e0e2761359d30a8275058e299fcc0381534545f55cf43e41983f5d4c9456"
Create order
When ready, create an order by sending a request to /relayer/create-order
. This creates the order in Garden's orderbook. The response includes the unique order ID
for tracking and managing the order.
Endpoint:
POST /relayer/create-order
Example request for tBTC to WBTC:
{
"source_chain": "bitcoin_testnet",
"destination_chain": "arbitrum_sepolia",
"source_asset": "primary",
"destination_asset": "0x795Dcb58d1cd4789169D5F938Ea05E17ecEB68cA",
"initiator_source_address": "609c8b4b2026902fa15ad850de19d56ecee31ce9b61f0f69cc732da3a58b6ad6", //x-only pubkey of the initiator bitcoin address
"initiator_destination_address": "0x753Cf575A0224c590ACF3587031E88b238261f7a",
"source_amount": "10000",
"destination_amount": "9970",
"nonce": "1743337081630",
"secret_hash": "5b876744fc3b8812da6b5317e3dcbb833c68c13c59cb67e9dfac992d388a1b74", // the secret generated by the user
"additional_data": {
"strategy_id": "btyrasac",
"bitcoin_optional_recipient": "tb1qz0pnh98kfynptg9dtkj06f7sqnlxl3dxnmnjw4",
}
}
Expected response:
{
"result": <order-id>,
"status": "Ok",
}
Get order data
To retrieve order data for an order ID:
Endpoint:
GET /orders/id/:id/
Expected response:
{
"status": "Ok",
"result": {
"created_at": "<timestamp>",
"updated_at": "<timestamp>",
"deleted_at": null,
"source_swap": {
"created_at": "<timestamp>",
"updated_at": "<timestamp>",
"deleted_at": null,
"swap_id": "<swap_id_hash>", /* the htlc script address */
"chain": "<chain_name>",
"asset": "<asset_type>",
"initiator": "<initiator_address>",
"redeemer": "<redeemer_address>",
"timelock": "<timelock_value>",
"filled_amount": "<amount>",
"amount": "<amount>",
"secret_hash": "<secret_hash>",
"secret": "",
"initiate_tx_hash": "<transaction_hash>",
"redeem_tx_hash": "",
"refund_tx_hash": "<transaction_hash>",
"initiate_block_number": "<block_number>",
"redeem_block_number": "0",
"refund_block_number": "<block_number>",
"required_confirmations": "<number>",
"current_confirmations": "<number>"
},
"destination_swap": {
"created_at": "<timestamp>",
"updated_at": "<timestamp>",
"deleted_at": null,
"swap_id": "<swap_id_hash>", /* the htlc script address */
"chain": "<chain_name>",
"asset": "<asset_type>",
"initiator": "<initiator_address>",
"redeemer": "<redeemer_address>",
"timelock": "<timelock_value>",
"filled_amount": "<amount>",
"amount": "<amount>",
"secret_hash": "<secret_hash>",
"secret": "",
"initiate_tx_hash": "<transaction_hash>",
"redeem_tx_hash": "",
"refund_tx_hash": "<transaction_hash>",
"initiate_block_number": "<block_number>",
"redeem_block_number": "0",
"refund_block_number": "<block_number>",
"required_confirmations": "<number>",
"current_confirmations": "<number>"
},
"create_order": {
// order metadata
}
}
}
}
If the order is unmatched, this endpoint returns null.
Initiate order
The order initiation process begins when the user initiates the swap on the source chain. For EVM-based chains, use the /relayer/initiate
endpoint. The process differs for Bitcoin, which uses scripts.
Initiating on EVM:
To initiate, the user must sign the HTLC initiation message using their wallet provider's EIP-712 typed data signing method. This signature authorizes the HTLC contract to lock tokens in escrow with the specified parameters. The signature is used for contract initialization.
- The user must call the
eip712domain()
function on the HTLC contract that returns the eip712 domain data.
Sample EIP712domain data:
[ eip712Domain method Response ]
name string : HTLC
version string : 1
chainId uint256 : 42161
verifyingContract address : 0x6b6303fAb8eC7232b4f2a7b9fa58E5216F608fcb
- The initiation message must be in the following format:
HTLC initiation message format:
{
address redeemer;
uint256 timelock;
uint256 amount;
bytes32 secretHash;
}
- The message, along with the domain data, must be signed with the wallet, and the signature is sent with the initiate request to the relayer.
Endpoint:
POST /relayer/initiate
Example request:
{
"order_id" : "<order_id>",
"signature": "<signature_hex_string>",
"perform_on": "Source"
}
On success, the response contains the transaction hash of the initiate event.
Expected response:
{
"result": "<tx_id>",
"status": "Ok"
}
Initiating on bitcoin:
On Bitcoin, initiation can be done in two ways:
- Fund the HTLC script address directly using a Bitcoin wallet.
- Construct a transaction paying the required amount to the HTLC script address and broadcast it.
The HTLC script address is the swap ID of the source chain swap, found in the source_swap
field in the /orders/id/:id
response.
Setup Bitcoin Instant-Refund
Instant refund is triggered automatically in these scenarios:
- Order Expiry: User initiates swap → Filler fills → User fails to redeem before expiry → Instant refund triggered
- Partial Fill: User initiates swap → Filler confirms partial fill → Remaining unfilled portion triggers instant refund
To enable instant refund when the source swap is bitcoin, make the following API calls.
a. Generate signature hashes
Endpoint: POST /relayer/bitcoin/instant-refund-hash
Generates the transaction digest(s) that must be signed to enable instant refund.
Request Body:
{
"order_id": "<order_id>"
}
Response:
{
"sighashes": [
"<hex_encoded_sighash_to_sign>"
]
}
Requirements:
- Sign each sighash using Schnorr signature with SIGHASH_SINGLE | ANYONECANPAY
b. Submit instant refund transaction
Endpoint: POST relayer/bitcoin/instant-refund
Accepts user signatures and stores the prepared SACP (Single + AnyoneCanPay) transaction for future use.
Request Body:
{
"order_id": "<order_id>",
"signatures": [
"<schnorr_signature_in_hex>"
]
}
Response:
{
"status": "ok",
"result": "<transaction_hex>"
}
Validation:
- Signatures are verified against generated sighashes
- Public key must match swap initiator
- Schnorr signature format with correct SIGHASH type enforced
With this, Bitcoin swaps can be refunded instantly to the user if the order expires or is partially filled.
Redeem asset
The redemption step finalizes the swap, allowing the user to claim assets on the destination chain after the order has been successfully initiated and confirmed.
Prerequisites:
- The swap must be complete on the
source chain
, and assets must be ready on thedestination chain
. - The destination chain must show an
initiate_tx_hash
underdestination_swap
. - The
secret
generated during order creation must be revealed.
Retrieving order details:
Poll the /orders/id/:id
endpoint to fetch the latest order status. Once destination_swap.initiate_tx_hash
is present, the destination HTLC is funded and ready for redemption.
Redemption on EVM chain:
Use the /relayer/redeem
endpoint to submit the redemption request, including the secret that proves the user can claim the funds.
Endpoint:
POST /relayer/redeem
Example request:
{
"order_id" : "<order_id>",
"secret": "<secret>",
"perform_on": "Destination"
}
Expected response:
{
status: "Ok",
result: "<redeem_transaction_hash>"
}
Redeeming on Bitcoin:
For Bitcoin-based swaps, redemption involves constructing and signing a transaction with the appropriate witness data.
The HTLC on Bitcoin is constructed using the destination_swap
details from /orders/id/:id
.
To redeem:
- Use the secret from order creation.
- Generate the required witness data:
- Secret
- Redeem leaf bytes
- Control block bytes
- Construct and sign the Bitcoin transaction.
- Use Garden’s relayer service for gasless redemption by sending the transaction hex bytes in the specified format.
An Example in RUST on how to construct and sign HTLC transactions
Endpoint:
POST /bitcoin/redeem
Example request:
{
"order_id": "<order_id>",
"redeem_tx_bytes": "<transaction_hex_of_the_constructed_redeem_txn>"
}
Expected response:
{
status: "Ok",
result: "<redeem_transaction_hash>"
}
You now have everything you need to integrate Garden swaps into your application using our APIs...