Skip to main content
POST
/
Transaction
/
sendbatch
Send Batch Transactions
curl --request POST \
  --url https://api.altude.so/api/Transaction/sendbatch \
  --header 'Content-Type: application/json' \
  --header 'X-API-Key: <x-api-key>' \
  --data '
{
  "signedTransaction": "<string>"
}
'
{
  "status": "Queued",
  "message": "Transaction sent successfully",
  "signature": "5eykt4UsFv8P8NJdTREpY1vzqKqZKvdpKuc147dw2N9d" 
}
Submit multiple signed transactions to be processed together on the Solana network.

headers

X-API-Key
string
required
The API key created from Altude

Request Body

signedTransaction
string
required
The base64-encoded signed batch transaction containing multiple operations
For a step-by-step example that builds and partially signs a transaction to send SPL tokens—including creating the ATA if missing, setting any required authorities, partial local signing, and submission to Altude’s signing endpoint—see the Send example: Send example. This Send example is provided as an example only.

Response

success
boolean
Indicates if the batch transaction was sent successfully
signatures
array
Array of transaction signatures for each transaction in the batch
{
  "status": "Queued",
  "message": "Transaction sent successfully",
  "signature": "5eykt4UsFv8P8NJdTREpY1vzqKqZKvdpKuc147dw2N9d" 
}

Example

This example demonstrates how to build a Solana transaction that transfers SPL tokens for a given mint and sets the associated token account (ATA) owner (the authority) to a specified public key. The fee payer covers account creation and transaction fees. The sample walks through building the transaction, partially signing it locally, and submitting the partially-signed transaction to the Altude signing endpoint for final submission.

Install library

  npm install @solana/web3.js @solana/spl-token

Copy and paste this to your project

import { Connection, Keypair, PublicKey, Transaction } from '@solana/web3.js';
import { getAssociatedTokenAddress, createAssociatedTokenAccountInstruction, createSetAuthorityInstruction, AuthorityType, TOKEN_PROGRAM_ID, getAccount, createTransferInstruction } from '@solana/spl-token';


 const sourceSignerSecretKey: any[] = [/* your fee payer secret key array here */];
 const API_KEY = 'your-api-key-here';
 // --- Helper functions (inline for docs-only usage) ---
 // Compact helpers used only for this example: connection factory,
 // idempotent ATA creator, serialization helpers, and a signing POST helper.
 function getConnection(rpcUrl: string, commitment: any = 'confirmed') {
   return new Connection(rpcUrl, commitment);
 }

 async function ensureCreateAtaInstruction(connection: Connection, feePayer: PublicKey, mint: PublicKey, owner: PublicKey) {
   // Compute the associated token account (ATA) for the owner + mint.
   const ata = await getAssociatedTokenAddress(mint, owner);

   // If the ATA does not exist on-chain, return an instruction to create it.
   // Caller should add the returned `ix` to the transaction to make creation idempotent.
   const info = await connection.getAccountInfo(ata);
   if (!info) {
     const ix = createAssociatedTokenAccountInstruction(feePayer, ata, owner, mint);
     return { ata, ix };
   }
   return { ata, ix: null };
 }

 function uint8ArrayToBase64(bytes: Uint8Array) {
   // Convert a Uint8Array into a base64 string for JSON transport.
   let binary = '';
   const chunkSize = 0x8000;
   for (let i = 0; i < bytes.length; i += chunkSize) {
     const chunk = bytes.subarray(i, i + chunkSize);
     binary += String.fromCharCode(...chunk);
   }
   return btoa(binary);
 }

 function serializePartialSigned(tx: Transaction, feePayerSecret: number[]) {
   // Partially sign the transaction with the local fee payer key.
   // Use `requireAllSignatures: false` so a remote signer can complete signatures.
   const keypair = Keypair.fromSecretKey(Uint8Array.from(feePayerSecret));
   tx.partialSign(keypair);
   const raw = tx.serialize({ requireAllSignatures: false });
   return uint8ArrayToBase64(raw);
 }

 async function postToSigningServer(url: string, signedBase64: string, apiKey?: string) {
   // POST the partially-signed transaction to Altude. The server is expected
   // to append remaining signatures (if any) and broadcast the transaction.
   const headers: Record<string, string> = { 'Content-Type': 'application/json' };
   if (apiKey) headers['X-API-Key'] = apiKey;
   const res = await fetch(url, { method: 'POST', headers, body: JSON.stringify({ SignedTransaction: signedBase64 }) });
   return res.json();
 }

 // STEP 1 — Build transaction Transfer (idempotent create + set close authority)
 export async function buildTransferTx(mints: string[], source: string, destination: string, amount: number , configUrl = 'https://api.altude.so/api/transaction/config') {
   // Load network config (RPC URL and FeePayer public key) from Altude.
   const cfg = await (await fetch(configUrl)).json();
   const connection = getConnection(cfg.RpcUrl);
   const feePayerPub = new PublicKey(cfg.FeePayer);
   const sourcePub = new PublicKey(source);
   const destinationPub = new PublicKey(destination);
   const tx = new Transaction();

   for (const mint in mints) {
   
     const mintPub = new PublicKey(mint);

     // Prepare an idempotent create-if-missing instruction for the ATA.
     // `ix` will be non-null when the ATA must be created.
     const { ata, ix } = await ensureCreateAtaInstruction(connection, feePayerPub, mintPub, sourcePub);

   
     tx.feePayer = feePayerPub;

     // Try to fetch ATA account info; absence indicates we should add the create instruction.
     const ataAccount = await getAccount(connection, ata);

     if (ataAccount == null) {
       // Add the ATA creation instruction when needed so the transaction is safe to re-run.
       if (ix) tx.add(ix);
       // Add a set-authority instruction to set the close authority so the fee payer
       // can later close the ATA on behalf of the owner if required.
       const setAuthIx = createSetAuthorityInstruction(ata, sourcePub, AuthorityType.CloseAccount, feePayerPub, [], TOKEN_PROGRAM_ID);
       tx.add(setAuthIx);
     }

     // Add the SPL token transfer instruction to move tokens from source to destination.
     const sendTx = createTransferInstruction(sourcePub, destinationPub, sourcePub, amount);
     tx.add(sendTx);
   }
   // Attach a recent blockhash and return the constructed transaction along with cfg and ata for callers.
   tx.recentBlockhash = (await connection.getLatestBlockhash('finalized')).blockhash;
   return  tx;
 }

 // STEP 2 — Sign (partial) and submit to signing server
 // Partially sign locally so the signing server can complete signatures and broadcast.
 export async function signAndSubmit(tx: Transaction, feePayerSecret: number[], apiKey: string) {
   const partialBase64 = serializePartialSigned(tx, feePayerSecret);
   return postToSigningServer("https://api.altude.so/api/transaction/send", partialBase64, apiKey);
 }

Implementation

async function createTransferTransaction(mints: string[], source: string, destination: string, amount: number) {
  const tx = await buildTransferTx(mints, source, destination, amount);
  return signAndSubmit(tx, sourceSignerSecretKey, API_KEY);
}

createTransferTransaction(["mintAddress"], "sourceAddress", "destinationAddress", 1000).then(res => {
  console.log('Transaction successful:', res);
})