Quote to Swap Integration Guide

Build a backend-safe Carbium swap flow from quote request to signed submission by separating API auth, signing boundaries, RPC relay, and confirmation checks.

Quote to Swap Integration Guide

Most Carbium swap integrations do not fail because the quote endpoint is hard to call. They fail because teams blur four different jobs into one fragile loop:

  • quote construction
  • signing authority
  • transaction relay
  • confirmation and replay control

This page owns the build workflow for the current Carbium quote-to-swap path. It assumes you are using the published GET https://api.carbium.io/api/v2/quote flow and want a production-safe route from quote request to landed transaction.

Part of the Carbium Solana infrastructure stack.


What this page owns

Use this page for one question only:

How should a real app wire Carbium's current quote flow into a safe sign -> submit -> confirm path?

Keep the neighboring pages narrow:

If your question is...Use this page
What parameters does the current quote endpoint expect?Q1
Why did the quote request fail before signing?Swap API Errors Reference
How do I relay a signed transaction through Carbium RPC?Sending Transactions through Carbium RPC
How do I build the whole quote-to-confirm workflow?This page

The clean integration shape

For most apps, the stable path is:

  1. your backend requests a quote from https://api.carbium.io/api/v2/quote
  2. the request includes user_account when you need an executable transaction
  3. the quote response returns route data and a base64 txn
  4. the signer approves the transaction on the correct boundary
  5. your backend submits through https://rpc.carbium.io/?apiKey=...
  6. your backend confirms the signature before any replay decision
flowchart TD
    A["Backend quote request<br/>api.carbium.io/api/v2/quote"] --> B["Quote response<br/>routePlan + txn"]
    B --> C{"Who signs?"}
    C -->|Wallet app| D["Client wallet signs txn"]
    C -->|Backend executor| E["Server-side signer signs txn"]
    D --> F["Backend relay"]
    E --> F
    F --> G["RPC submit<br/>rpc.carbium.io"]
    G --> H["Confirmation check"]
    H --> I["Success, wait, or investigate"]

The important split is not stylistic. It prevents three common mistakes:

  • exposing X-API-KEY or RPC credentials to clients
  • retrying the same signed payload without checking confirmation state
  • mixing quote failures with RPC failures in one undebuggable error bucket

Decide the signing boundary first

Before you write code, decide where signatures are allowed to happen.

App shapeRecommended signerWhy
Consumer wallet or embedded wallet UXUser wallet clientThe user controls approval, while Carbium keys stay backend-side
Custodial backend or controlled executorBackend signerUseful when your service owns execution and can protect signing material
Mixed app with frontend orchestration and backend relayClient signs, backend relaysKeeps consent in the wallet and submission logic on infrastructure you control

If this decision is still fuzzy, stop there first. A blurry signing boundary usually turns into a blurry security boundary.

📘

Carbium's current published flow returns txn from the quote step when user_account is present. That makes the signing boundary the main application decision, not a second Carbium swap-build request.


Minimal workflow, without mixing concerns

1. Quote on the backend

Keep the Swap API key server-side and request the current Q1 quote shape.

const quoteUrl = new URL("https://api.carbium.io/api/v2/quote");
quoteUrl.searchParams.set("src_mint", inputMint);
quoteUrl.searchParams.set("dst_mint", outputMint);
quoteUrl.searchParams.set("amount_in", amountIn);
quoteUrl.searchParams.set("slippage_bps", "50");
quoteUrl.searchParams.set("user_account", userPublicKey);

const quoteRes = await fetch(quoteUrl, {
  headers: {
    "X-API-KEY": process.env.CARBIUM_API_KEY!,
    "Accept": "application/json",
  },
});

if (!quoteRes.ok) {
  throw new Error(`Quote failed with ${quoteRes.status}`);
}

const quote = await quoteRes.json();

if (!quote.txn) {
  throw new Error("Missing txn in quote response");
}

This is where you validate:

  • the request used src_mint, dst_mint, amount_in, and slippage_bps
  • user_account was present when your flow expects txn
  • the response contains a route and executable payload before you ask any signer to act

2. Sign where your app is designed to sign

Deserialize the returned transaction and sign it only on the approved boundary.

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

const unsignedTx = VersionedTransaction.deserialize(
  Buffer.from(quote.txn, "base64")
);

const signedTx = await wallet.signTransaction(unsignedTx);
const signedBase64 = Buffer.from(signedTx.serialize()).toString("base64");

Do not pass Carbium keys into the wallet client just because the wallet is signing. Signing and API authentication are separate responsibilities.

3. Relay and confirm on the backend

Submission belongs on infrastructure you control, using the RPC key that matches the documented Carbium RPC auth pattern.

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

const connection = new Connection(
  `https://rpc.carbium.io/?apiKey=${process.env.CARBIUM_RPC_KEY}`,
  "confirmed"
);

async function submitSignedTransaction(signedBase64: string) {
  const signedTx = VersionedTransaction.deserialize(
    Buffer.from(signedBase64, "base64")
  );

  const signature = await connection.sendRawTransaction(
    signedTx.serialize(),
    { skipPreflight: false, maxRetries: 3 }
  );

  const confirmation = await connection.confirmTransaction(
    signature,
    "confirmed"
  );

  return { signature, confirmation };
}

The app-level rule is simple:

  • quote first
  • sign once
  • submit once
  • confirm before replay

What to log so support does not become guesswork

Treat one swap attempt as one traceable execution record.

Store at least:

FieldWhy it matters
app request IDCorrelates UI, backend, and RPC events
quote parametersHelps you reproduce route or shape mistakes
returned route metadataDistinguishes no-route issues from later failures
submitted signaturePrevents blind duplicate sends
final confirmation stateTells you whether the transaction landed, stalled, or failed later

Without that trail, teams often mistake a quote issue, a signing issue, and an RPC issue for the same production bug.


Failure map by stage

Use the stage to choose the right next doc instead of debugging the whole stack at once.

Failure stageTypical signalBest next move
Quote request401, 403, 400, empty txn, no routeGo to Swap API Errors Reference
SigningWallet rejects, signer unavailable, serialization issuesCheck your signing boundary and local app logic
RelaySend path fails before signature is acceptedCheck backend RPC auth and submission logic
ConfirmationSignature exists but outcome is unclearUse your confirmation flow before replaying

This page intentionally stops short of inventing undocumented on-chain failure semantics. Its job is to keep the workflow clean enough that failures stay local to the right stage.


Production checklist

  • keep CARBIUM_API_KEY and CARBIUM_RPC_KEY on backend infrastructure only
  • require user_account whenever the workflow expects an executable quote response
  • persist the submitted signature before any retry logic runs
  • separate quote-stage logs from relay-stage logs
  • keep the signer boundary explicit in code and architecture docs
  • confirm the transaction state before rebuilding or replaying intent
🔶

Use this page as the workflow owner, then drop into Q1 for request shape or Sending Transactions through Carbium RPC for the relay boundary. For setup and product access, start at carbium.io.