Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.hubra.app/llms.txt

Use this file to discover all available pages before exploring further.

POST https://hubra.app/api/v1/broadcast
Submit a fully signed transaction. The default route is plain Solana RPC, which works for any signed transaction (Sanctum-built, Voltr-built, native). The chain does not care which builder produced the bytes. Optional route: "sanctum" (with sanctumKind and sanctum_order) forwards to Sanctum’s execute endpoint, which adds MEV protection and smarter retries. Use it when broadcasting transactions that came back from /stake or /unstake with route: "sanctum".
hubra_token is required on every call. Without a matching token, the endpoint rejects the request with 403 forbidden.

Request

curl -X POST https://hubra.app/api/v1/broadcast \
  -H 'Content-Type: application/json' \
  -d '{
    "signed_tx":   "<base64 signed tx>",
    "hubra_token": "<token from /stake or /unstake response>"
  }'
FieldTypeRequiredDescription
signed_txstringyesBase64-encoded fully signed transaction.
hubra_tokenstringyesToken from the matching /stake or /unstake response. HMAC over the unsigned message bytes. Tokens expire ~2 minutes after issue.
route"rpc" | "sanctum"optionalDefaults to "rpc". Use "sanctum" for Sanctum-routed swap flows.
sanctumKind"token" | "depositStake" | "depositSol" | "withdrawStake"conditionalRequired when route="sanctum". Pass through the value from the /stake or /unstake response.
sanctum_orderobjectconditionalRequired when route="sanctum". Pass through the sanctum_order object from the /stake or /unstake response. Sanctum’s execute endpoint validates the signed transaction against this.

Response

{
  "signature": "5z6Z...",
  "explorer":  "https://solscan.io/tx/5z6Z..."
}
FieldTypeDescription
signaturestringBase58 on-chain transaction signature.
explorerstringPre-built Solscan link for the signature.

When to use Sanctum routing

You came fromUse
/stake sol-liquid-stakeroute: "sanctum"
/stake sol-native-stakeroute: "rpc"
/stake usdc-earnroute: "rpc"
/unstake sol-native-stake instantroute: "sanctum"
/unstake sol-native-stake deactivateroute: "rpc"
/unstake sol-liquid-stake instantroute: "sanctum"
/unstake sol-liquid-stake slowroute: "sanctum"
/unstake usdc-earn instantroute: "rpc"
/withdrawroute: "rpc"
The simple rule: if the build response had route: "sanctum", broadcast with route: "sanctum" and forward the Sanctum-specific fields. Sanctum’s broadcaster adds MEV protection and smarter retries. Plain RPC works for any signed transaction; the chain itself does not distinguish.

Signing example (Node + @solana/web3.js)

import { VersionedTransaction, Keypair } from "@solana/web3.js";

// 1. Build the unsigned tx
const buildResp = await fetch("https://hubra.app/api/v1/stake", {
  method: "POST",
  headers: { "Content-Type": "application/json" },
  body: JSON.stringify({
    strategy: "sol-liquid-stake",
    wallet:   walletPubkey,
    amount:   "1.0",
  }),
}).then((r) => r.json());

const { transaction, hubra_token, route, sanctumKind, sanctum_order } = buildResp;

// 2. Sign locally
const tx = VersionedTransaction.deserialize(Buffer.from(transaction, "base64"));
tx.sign([wallet]); // wallet is a Keypair you hold
const signed = Buffer.from(tx.serialize()).toString("base64");

// 3. Broadcast — forward Sanctum-specific fields when applicable
const broadcastBody =
  route === "sanctum"
    ? { signed_tx: signed, hubra_token, route, sanctumKind, sanctum_order }
    : { signed_tx: signed, hubra_token };

const { signature } = await fetch("https://hubra.app/api/v1/broadcast", {
  method: "POST",
  headers: { "Content-Type": "application/json" },
  body: JSON.stringify(broadcastBody),
}).then((r) => r.json());

console.log(`Confirmed: https://solscan.io/tx/${signature}`);
For sol-native-stake, tx.sign([wallet]) only fills the wallet’s slot. The stake-account slot is pre-signed by Hubra’s server.

Errors

StatusSlugWhen
400invalid_requestMissing signed_tx, missing hubra_token, missing sanctum_order (when route="sanctum"), or invalid route / sanctumKind.
403forbiddenhubra_token does not match this transaction, is malformed, or has expired. Rebuild via /stake or /unstake to get a fresh token.
502upstream_errorRPC or Sanctum rejected the broadcast (invalid blockhash, signature failure, simulation failure, insufficient funds).

See also

Hubra token

The HMAC mechanism in detail.

POST /stake

Build a stake transaction.

POST /unstake

Build an unstake transaction.

Errors

Problem-details and slugs.