Framework guide
RFQ for Next.js App Router
Render the widget from a Client Component. Mint authorToken in a Route Handler. Ignore authorId, permissions, and supplierId if the browser sends them. Copy those fields from the signed-in user. Secret stays in BSW_RFQ_SECRET, never NEXT_PUBLIC_*. The same route ships in the package AGENTS.md.
Install
pnpm add @bootstrapware/rfq
Example app: pnpm --filter @bootstrapware/rfq-example dev on port 3015. Buyer and supplier routes live under /buyer and /supplier.
Client island
"use client";
import { RfqRequester, createHostedAdapter } from "@bootstrapware/rfq";
import "@bootstrapware/rfq/styles.css";
export function BuyerRfq({
actor,
authorToken,
renewAuthorToken,
}: {
actor: { id: string; permissions: string[] };
authorToken: string;
renewAuthorToken: () => Promise<string>;
}) {
const adapter = createHostedAdapter({
appId: process.env.NEXT_PUBLIC_RFQ_APP_ID!,
publishableKey: process.env.NEXT_PUBLIC_BSW_RFQ_PUBLISHABLE_KEY!,
scope: { tenantKey: "acme" },
authorToken,
});
return (
<RfqRequester
scope={{ appId: process.env.NEXT_PUBLIC_RFQ_APP_ID!, tenantKey: "acme" }}
actor={actor}
adapter={adapter}
authorToken={authorToken}
renewAuthorToken={renewAuthorToken}
/>
);
}Server-only token route
// app/api/rfq/author-token/route.ts
import { NextResponse } from "next/server";
import { getSessionUser, rfqAccess } from "@/lib/session";
export async function POST(request: Request) {
const user = await getSessionUser();
if (!user) return NextResponse.json({ error: "Sign in required." }, { status: 401 });
const body = await request.json();
const access = await rfqAccess(user.id, String(body.tenantKey ?? ""));
if (!access) return NextResponse.json({ error: "The requested resource was not found." }, { status: 404 });
const response = await fetch("https://rfq.bootstrapware.co/api/v1/author-tokens", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.BSW_RFQ_SECRET}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
authorId: user.id,
appId: process.env.RFQ_APP_ID,
tenantKey: access.tenantKey,
permissions: access.permissions,
supplierId: access.supplierId,
expiresInSec: 300,
}),
});
return NextResponse.json(await response.json(), { status: response.status });
}The example route at examples/rfq-nextjs/app/api/rfq/author-token/route.ts always returns 501 until you wire the signed-in session, rfqAccess, and BSW_RFQ_SECRET. Setting the secret alone is not enough. Do not mint tokens in the browser.
Related: Identity · Persisted BYO · Files