Integrate into your own server
Add a token-minting endpoint to your backend so the browser SDK gets a short-lived session JWT — no secret ever reaches the client.
This is the pattern from Choose your auth mode made concrete: a token-minting endpoint on your server, and a browser page that calls it before calling the SDK.
1. Add a token endpoint to your backend
import jwt from "jsonwebtoken";
const SECRET = process.env.QURAVIN_SIGNING_SECRET; // from Get your API key
const APP_ID = "your-app-id"; // your app's registered id
app.get("/ai-token", requireUserLogin, (req, res) => {
const token = jwt.sign(
{
iss: APP_ID,
sub: req.user.id, // your app's user id
aud: "quravin",
pipelines: ["translate-string"], // which pipelines this user may call
rate_limit: { rpm: 30 },
},
SECRET,
{ algorithm: "HS256", expiresIn: "15m" },
);
res.json({ token, expires_in: 15 * 60 });
});
requireUserLogin is your own auth middleware — the token only ever goes to a user your server
has already authenticated.
2. Fetch the token and call the SDK from the browser
<input id="text" />
<button id="go">Translate</button>
<span id="result"></span>
<script src="https://js.quravin.com/v1.js"></script>
<script>
const fetchToken = async () => {
const r = await fetch("/ai-token", { credentials: "include" });
if (!r.ok) throw new Error("Token fetch failed");
return (await r.json()).token;
};
const ai = new Quravin.Quravin({
endpoint: "https://api.example.com/ai-pipeline",
sessionToken: await fetchToken(),
onTokenExpired: fetchToken, // auto-refreshes when the API returns 401
});
document.getElementById("go").onclick = async () => {
const out = await ai.run({
pipeline: "translate-string",
inputs: { text: document.getElementById("text").value, target_language: "German" },
});
document.getElementById("result").textContent = out.translation;
};
</script>
3. Handle errors
A quota or rate-limit hit returns a typed error — show the user a clear message and a link to pricing:
try {
const out = await ai.run({ pipeline: "translate-string", inputs: { /* ... */ } });
} catch (err) {
showToast("Translation failed: " + err.message);
}
That’s the whole loop: your server mints tokens, the browser never sees a secret, and every call
is attributed to the user who made it. The SDK also has a more complete reference — batching with
ai.runMany, the ai.button DOM binding, raw-prompt mode, and a full troubleshooting table — ask
your platform contact for it if you need to go deeper.