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:
- your backend requests a quote from
https://api.carbium.io/api/v2/quote - the request includes
user_accountwhen you need an executable transaction - the quote response returns route data and a base64
txn - the signer approves the transaction on the correct boundary
- your backend submits through
https://rpc.carbium.io/?apiKey=... - 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-KEYor 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 shape | Recommended signer | Why |
|---|---|---|
| Consumer wallet or embedded wallet UX | User wallet client | The user controls approval, while Carbium keys stay backend-side |
| Custodial backend or controlled executor | Backend signer | Useful when your service owns execution and can protect signing material |
| Mixed app with frontend orchestration and backend relay | Client signs, backend relays | Keeps 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
txnfrom the quote step whenuser_accountis 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, andslippage_bps user_accountwas present when your flow expectstxn- 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:
| Field | Why it matters |
|---|---|
| app request ID | Correlates UI, backend, and RPC events |
| quote parameters | Helps you reproduce route or shape mistakes |
| returned route metadata | Distinguishes no-route issues from later failures |
| submitted signature | Prevents blind duplicate sends |
| final confirmation state | Tells 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 stage | Typical signal | Best next move |
|---|---|---|
| Quote request | 401, 403, 400, empty txn, no route | Go to Swap API Errors Reference |
| Signing | Wallet rejects, signer unavailable, serialization issues | Check your signing boundary and local app logic |
| Relay | Send path fails before signature is accepted | Check backend RPC auth and submission logic |
| Confirmation | Signature exists but outcome is unclear | Use 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_KEYandCARBIUM_RPC_KEYon backend infrastructure only - require
user_accountwhenever 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.
Updated 11 days ago
