ZeroCopyZeroCopy
API reference

sendBundle

Submit one to four signed transactions as one bundle for delivery through block engines.

Request
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "sendBundle",
  "params": [["SIGNED_TRANSACTION_1", "SIGNED_TRANSACTION_2"], { "encoding": "base64" }]
}

Params

PositionTypeContent
0array of stringsOne to four fully signed wire transactions, in execution order, encoded as specified by options.encoding.
1object, optionalOptions, below.

Options

FieldTypeDefaultMeaning
encodingstring"base58""base58" or "base64", for every transaction of the bundle.
tipinteger, lamportsnoneOne declared tip for the whole bundle. Used only when the bundle's own tips are below your minimum and your key may declare tips.

Other fields, skipPreflight among them, are ignored: bundles are never simulated. Each transaction has the size limits of sendTransaction.

Result

The bundle id: 64 lowercase hex characters, with HTTP 200.

Result shape
{"jsonrpc":"2.0","result":"BUNDLE_ID_HEX","id":1}

The id is SHA-256 of the members' first signatures, concatenated in order as raw 64-byte values. It is not a block engine's status id. SWQoS has no bundle status method; confirm each signature through your Solana RPC provider.

A result acknowledges submission to at least one block engine, not landing.

Submission requirements

Each member must meet the transaction size limits. The bundle must satisfy the tip, control-account and rate rules. Bundles have no preflight simulation.

Errors

ReasonHTTPCodeWhen
unauthorized401-32000The key is missing, unknown or revoked.
invalid_request200see codesThe request is not a valid sendBundle call.
bundle_size200-32602Fewer than 1 or more than 4 transactions. count and limit.
malformed, too_large, unsigned, signature_count200-32602A transaction failed to decode or check. index is its position.
bundle_control200-32602A control-account rule was broken. index and rule.
payment_required402-32003The key is suspended until its overdue invoice is paid.
missing_tip200-32602The tips are below your minimum, or below the engine minimum. minimum is the one that applies.
credit_exhausted200-32602Credit is unavailable for this declared-tip submission.
not_ready503-32603waiting_for is block_engine, blockhash, tip_vaults or declared_tips.
rate_limited429-32001limit: bundles for bundle admission, or limit: transactions for your key's send rate. Wait for retry_after_ms when supplied.
overloaded503-32603The region is temporarily busy.

For not_ready with waiting_for: block_engine, wait and retry while the signed transactions remain valid. Do not submit members separately if they must execute together.

Examples

This example sends signed transaction files, in order, as one bundle with the client helper. Together the transactions must carry the tip.

send-bundle.ts
import { readFile } from 'node:fs/promises'
import { call, SwqosError } from './swqos.ts'

const files = process.argv.slice(2)
const transactions = await Promise.all(
  files.map(async (file) => (await readFile(file)).toString('base64')),
)

try {
  const bundleId = await call<string>('sendBundle', [transactions, { encoding: 'base64' }])
  console.log('accepted', bundleId)
} catch (error) {
  if (!(error instanceof SwqosError) || !error.rejection) throw error
  console.error(error.rejection.reason, error.rejection.message)
}

The same id, computed locally from the wire bytes with @solana/kit:

bundle-id.ts
import { createHash } from 'node:crypto'
import {
  getBase58Encoder,
  getSignatureFromTransaction,
  getTransactionDecoder,
} from '@solana/kit'

export function bundleId(wireTransactions: Uint8Array[]): string {
  const hash = createHash('sha256')
  for (const wire of wireTransactions) {
    const signature = getSignatureFromTransaction(getTransactionDecoder().decode(wire))
    hash.update(Uint8Array.from(getBase58Encoder().encode(signature)))
  }
  return hash.digest('hex')
}

On this page