Build transaction steps
curl --request POST \
--url https://transfer.layerzero-api.com/v1/build-user-steps \
--header 'Content-Type: application/json' \
--header 'x-api-key: <api-key>' \
--data '
{
"quoteId": "quote_abc123"
}
'import requests
url = "https://transfer.layerzero-api.com/v1/build-user-steps"
payload = { "quoteId": "quote_abc123" }
headers = {
"x-api-key": "<api-key>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {'x-api-key': '<api-key>', 'Content-Type': 'application/json'},
body: JSON.stringify({quoteId: 'quote_abc123'})
};
fetch('https://transfer.layerzero-api.com/v1/build-user-steps', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://transfer.layerzero-api.com/v1/build-user-steps",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'quoteId' => 'quote_abc123'
]),
CURLOPT_HTTPHEADER => [
"Content-Type: application/json",
"x-api-key: <api-key>"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://transfer.layerzero-api.com/v1/build-user-steps"
payload := strings.NewReader("{\n \"quoteId\": \"quote_abc123\"\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("x-api-key", "<api-key>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://transfer.layerzero-api.com/v1/build-user-steps")
.header("x-api-key", "<api-key>")
.header("Content-Type", "application/json")
.body("{\n \"quoteId\": \"quote_abc123\"\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://transfer.layerzero-api.com/v1/build-user-steps")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["x-api-key"] = '<api-key>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"quoteId\": \"quote_abc123\"\n}"
response = http.request(request)
puts response.read_body{
"userSteps": [
{
"type": "TRANSACTION",
"description": "bridge",
"chainKey": "solana",
"chainType": "SOLANA",
"signerAddress": "Dz93pUVjXuaMnSsPSn7V99V4cUzhKoQdx9ECwZJZiafG"
}
]
}API Reference
Build User Steps
Generate fresh transaction data for Solana transfers with up-to-date blockhash.
POST
/
build-user-steps
Build transaction steps
curl --request POST \
--url https://transfer.layerzero-api.com/v1/build-user-steps \
--header 'Content-Type: application/json' \
--header 'x-api-key: <api-key>' \
--data '
{
"quoteId": "quote_abc123"
}
'import requests
url = "https://transfer.layerzero-api.com/v1/build-user-steps"
payload = { "quoteId": "quote_abc123" }
headers = {
"x-api-key": "<api-key>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {'x-api-key': '<api-key>', 'Content-Type': 'application/json'},
body: JSON.stringify({quoteId: 'quote_abc123'})
};
fetch('https://transfer.layerzero-api.com/v1/build-user-steps', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://transfer.layerzero-api.com/v1/build-user-steps",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'quoteId' => 'quote_abc123'
]),
CURLOPT_HTTPHEADER => [
"Content-Type: application/json",
"x-api-key: <api-key>"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://transfer.layerzero-api.com/v1/build-user-steps"
payload := strings.NewReader("{\n \"quoteId\": \"quote_abc123\"\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("x-api-key", "<api-key>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://transfer.layerzero-api.com/v1/build-user-steps")
.header("x-api-key", "<api-key>")
.header("Content-Type", "application/json")
.body("{\n \"quoteId\": \"quote_abc123\"\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://transfer.layerzero-api.com/v1/build-user-steps")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["x-api-key"] = '<api-key>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"quoteId\": \"quote_abc123\"\n}"
response = http.request(request)
puts response.read_body{
"userSteps": [
{
"type": "TRANSACTION",
"description": "bridge",
"chainKey": "solana",
"chainType": "SOLANA",
"signerAddress": "Dz93pUVjXuaMnSsPSn7V99V4cUzhKoQdx9ECwZJZiafG"
}
]
}Generates fresh transaction data for a quote. Required for Solana transfers due to short blockhash validity.
For Solana, the transaction data is base64-encoded:
Reference
When to use
| Chain type | When to call |
|---|---|
| EVM | Not required. Use userSteps directly from quote response. |
| Solana | Always required. Solana transactions have short blockhash validity (~60 seconds). Call this endpoint immediately before execution. |
Request body
| Parameter | Type | Required | Description |
|---|---|---|---|
quoteId | string | Yes | Quote ID from the /quotes response |
Response
Returns auserSteps array with fresh transaction data.
User step attributes
User steps can beTRANSACTION or SIGNATURE (same structure as quote response):
TRANSACTION step:
| Attribute | Type | Description |
|---|---|---|
type | string | TRANSACTION |
description | string | Human-readable action description |
chainKey | string | Chain to execute on |
chainType | enum | EVM, SOLANA, STARKNET |
signerAddress | string | Wallet that must sign |
transaction | object | Transaction details |
transaction.encoded | object | Encoded transaction data |
{
type: "TRANSACTION",
chainType: "SOLANA",
transaction: {
encoded: {
encoding: "base64",
data: "AQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQABAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA..."
}
}
}
Code examples
- cURL
- TypeScript
- Python
curl -X POST "https://transfer.layerzero-api.com/v1/build-user-steps" \
-H "Content-Type: application/json" \
-H "x-api-key: YOUR_API_KEY" \
-d '{"quoteId": "QUOTE_ID_FROM_SOLANA_QUOTE"}'
const response = await fetch('https://transfer.layerzero-api.com/v1/build-user-steps', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'x-api-key': 'YOUR_API_KEY',
},
body: JSON.stringify({quoteId: quote.id}),
});
const {userSteps} = await response.json();
console.log('User steps:', userSteps);
import requests
response = requests.post(
"https://transfer.layerzero-api.com/v1/build-user-steps",
headers={"x-api-key": "YOUR_API_KEY"},
json={"quoteId": quote["id"]},
)
user_steps = response.json()["userSteps"]
print("User steps:", user_steps)
Response
{
"userSteps": [
{
"type": "TRANSACTION",
"description": "bridge",
"chainKey": "solana",
"chainType": "SOLANA",
"signerAddress": "Dz93pUVjXuaMnSsPSn7V99V4cUzhKoQdx9ECwZJZiafG",
"transaction": {
"encoded": {
"encoding": "base64",
"data": "AQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAkT..."
}
}
}
]
}
Solana execution
After building user steps, deserialize and sign the transaction:import {VersionedTransaction, Connection} from '@solana/web3.js';
// Get fresh user steps
const {userSteps} = await buildUserSteps(quoteId);
const step = userSteps[0];
// Decode base64 transaction
const txBuffer = Buffer.from(step.transaction.encoded.data, 'base64');
const tx = VersionedTransaction.deserialize(txBuffer);
// Sign and send
const signedTx = await wallet.signTransaction(tx);
const signature = await connection.sendRawTransaction(signedTx.serialize());
await connection.confirmTransaction(signature, 'confirmed');
Why Solana needs fresh transactions
Solana transactions include a recent blockhash that expires after ~60 seconds. The/build-user-steps endpoint generates transactions with the latest blockhash, ensuring they remain valid during execution.
Workflow:
- Request quote → Receive quote with
quoteId - Call
/build-user-stepswithquoteId→ Get fresh transaction - Sign and submit immediately (within 60 seconds)
Related endpoints
- Quotes — Request transfer quotes (includes
userStepsfor EVM) - Status — Track transfer progress after execution
Examples
- Solana Example — Complete Solana transfer with transaction building
Authorizations
API key for authenticating requests. Required for /quotes, /build-user-steps, /submit-signature, and /status endpoints.
Body
application/json
The quote ID to build steps for
Example:
"quote_abc123"
Response
200 - application/json
Successfully built user steps
Show child attributes
Show child attributes
Was this page helpful?