Send from Next.js
Fabric is server-only, so it belongs in a Route Handler or Server Action — never a Client Component,
getServerSideProps shipped to the browser, or a NEXT_PUBLIC_ variable. The SDK throws if it is
constructed in a browser.
-
Install and configure
Terminal window npm install @fabric-messaging/sdk.env.local FABRIC_API_KEY=sk_test_replace_with_your_keyFABRIC_WEBHOOK_SECRET=whsec_replace_with_your_secretDo not prefix either with
NEXT_PUBLIC_. -
Send from a Route Handler
app/api/notify/route.ts import { Fabric } from "@fabric-messaging/sdk";const fabric = new Fabric({ apiKey: process.env.FABRIC_API_KEY! });export async function POST(request: Request) {const { orderRef, phone } = await request.json();const result = await fabric.messages.send("order.shipped", {to: phone,data: { ref: orderRef },reference: `order:${orderRef}`,idempotencyKey: `order-shipped:${orderRef}`,});return Response.json({ id: result.data.id, status: result.data.status });} -
Verify the webhook against the raw body
app/api/fabric/webhook/route.ts import { Fabric } from "@fabric-messaging/sdk";const fabric = new Fabric({ apiKey: process.env.FABRIC_API_KEY! });export async function POST(request: Request) {const payload = await request.text(); // raw — do not JSON.parse firsttry {const event = fabric.webhooks.verify({payload,signature: request.headers.get("fabric-signature") ?? undefined,secret: process.env.FABRIC_WEBHOOK_SECRET!,});await processOnce(event.id, event);return new Response("ok", { status: 200 });} catch {return new Response("bad signature", { status: 400 });}}
The App Router hands you the raw body from request.text(), which is exactly what signature
verification needs. Reading request.json() first would reserialise the bytes and break the HMAC.