Vendo SDKs
Cookbook

Multi-tenant SaaS

Scope every SDK call to the logged-in user with one line of code.

In a multi-tenant SaaS, each end-user may have connected their own integrations. The Vendo SDK provides a single method — forRequest / forUser — to scope all SDK calls to a specific user's context.

Architecture

Client → [X-Vendo-User-JWT header] → Your API → forRequest(headers) → Vendo credentials service

                                                               User's OAuth tokens

The JWT is issued by Vendo when the user logs in. Pass it on every API request and the SDK extracts it automatically.

This pattern requires Vendo mode (VENDO_API_KEY must be set).

Request handler

from fastapi import FastAPI, Request
from vendo import Vendo

app = FastAPI()
_vendo = Vendo()  # created once at startup

@app.post("/api/summarize")
async def summarize(request: Request, body: dict):
    # Scope the client to the authenticated user
    client = _vendo.for_request(request.headers)

    # All subsequent calls use this user's connections
    openai_key = client.token("openai")
    google_token = client.token("google")

    return {"openai_key": openai_key[:8] + "..."}
import express from "express";
import { Vendo } from "@vendodev/sdk";

const app = express();
const vendo = new Vendo(); // created once at startup

app.post("/api/summarize", express.json(), async (req, res) => {
  try {
    // Scope the client to the authenticated user
    const client = vendo.forRequest(req.headers);

    // All subsequent calls use this user's connections
    const openaiKey = await client.token("openai");
    const googleToken = await client.token("google");

    res.json({ status: "ok" });
  } catch (e) {
    res.status(400).json({ error: String(e) });
  }
});
import Vapor
import Vendo

let vendo = try Vendo()  // created once at startup

func summarizeHandler(_ req: Request) async throws -> Response {
    let headers = Dictionary(
        uniqueKeysWithValues: req.headers.map { ($0.name.description, $0.value) }
    )

    // Scope the client to the authenticated user
    let client = try vendo.forRequest(headers: headers)

    // All subsequent calls use this user's connections
    let openaiKey  = try await client.token("openai")
    let googleToken = try await client.token("google")

    return Response(status: .ok)
}

Background jobs

When the JWT comes from a queue message or job payload rather than live request headers:

from vendo import Vendo

_vendo = Vendo()

async def process_export_job(user_jwt: str, export_id: str):
    client = _vendo.for_user(user_jwt)
    google_token = client.token("google")
    # Export data using user's Google token ...
import { Vendo } from "@vendodev/sdk";

const vendo = new Vendo();

async function processExportJob(userJwt: string, exportId: string) {
  const client = vendo.forUser(userJwt);
  const googleToken = await client.token("google");
  // Export data using user's Google token ...
}
import Vendo

func processExportJob(userJwt: String, exportId: String) async throws {
    let client = vendo.forUser(jwt: userJwt)
    let googleToken = try await client.token("google")
    // Export data using user's Google token ...
}

Handling missing JWT

from vendo.errors import IdentityNotPresent

try:
    client = _vendo.for_request(request.headers)
except IdentityNotPresent:
    # Missing X-Vendo-User-JWT header — user is not authenticated
    return {"error": "authentication required"}, 401
import { IdentityNotPresent } from "@vendodev/sdk";

try {
  const client = vendo.forRequest(req.headers);
} catch (e) {
  if (e instanceof IdentityNotPresent) {
    return res.status(401).json({ error: "authentication required" });
  }
  throw e;
}
do {
    let client = try vendo.forRequest(headers: headers)
} catch VendoError.identityNotPresent {
    throw Abort(.unauthorized, reason: "authentication required")
}

On this page