This guide covers using the Bondi SDK without the NestJS adapter — for Express apps, Fastify apps, simple scripts, or any Node.js runtime.
npm install @bondi-labs/integration-sdk zod
npx bondi init
Same flow as NestJS — writes BONDI_API_URL, BONDI_WORKSPACE_ID, and BONDI_INTEGRATION_TOKEN to .env.
Plain TypeScript module — no decorators needed.
// src/bondi.integration.ts
import { defineIntegration, defineAction, defineTrigger } from "@bondi-labs/integration-sdk/core";
import { z } from "zod";
export const contactCreatedPayload = z.object({
id: z.string(),
email: z.string().email(),
});
export default defineIntegration({
name: "My CRM",
slug: "my-crm",
category: "crm",
baseUrl: "https://api.mycrm.com/v1",
services: [
{
name: "contacts",
label: "Contacts",
triggers: [
defineTrigger({ name: "contact.created", label: "Contact Created", payload: contactCreatedPayload }),
],
actions: [
defineAction({
name: "createContact",
label: "Create Contact",
method: "POST",
endpoint: "/contacts",
body: z.object({ email: z.string(), fullName: z.string() }),
response: z.object({ id: z.string() }),
}),
],
},
],
});
npx bondi sync
// src/emit-example.ts
import { createBondiClient } from "@bondi-labs/integration-sdk/core";
import myCRM from "./bondi.integration";
const client = createBondiClient({
workspaceId: process.env.BONDI_WORKSPACE_ID,
token: process.env.BONDI_INTEGRATION_TOKEN,
apiUrl: process.env.BONDI_API_URL,
// optional:
onEmitError: (err, event) => console.error(`emit ${event} failed:`, err),
});
const bondi = client.bind(myCRM);
bondi.triggers["contact.created"].emit({ id: "1", email: "alice@example.com" });
Bondi calls your action endpoints with HMAC-signed requests. verifyRequest does the work — but you must capture the raw body before any JSON parser runs.
import express from "express";
import { verifyRequest } from "@bondi-labs/integration-sdk/core";
const app = express();
// CRITICAL: capture raw body for HMAC verification
app.use(express.json({
verify: (req, _res, buf) => {
(req as any).rawBody = buf.toString("utf8");
},
}));
app.post("/contacts", (req, res) => {
const result = verifyRequest(
process.env.BONDI_INTEGRATION_TOKEN!,
req.headers["x-bondi-signature"] as string,
req.headers["x-bondi-timestamp"] as string,
req.headers["x-bondi-action"] as string,
(req as any).rawBody,
);
if (!result.valid) {
return res.status(401).json({ error: result.reason });
}
// Signature verified — handle the action
const { email, fullName } = req.body;
// ... save to DB, return response
res.json({ id: "new-id" });
});
app.listen(3000);
import Fastify from "fastify";
import { verifyRequest } from "@bondi-labs/integration-sdk/core";
const app = Fastify();
app.addContentTypeParser("application/json", { parseAs: "buffer" }, (_req, body, done) => {
done(null, body);
});
app.post("/contacts", (req, reply) => {
const rawBody = (req.body as Buffer).toString("utf8");
const result = verifyRequest(
process.env.BONDI_INTEGRATION_TOKEN!,
req.headers["x-bondi-signature"] as string,
req.headers["x-bondi-timestamp"] as string,
req.headers["x-bondi-action"] as string,
rawBody,
);
if (!result.valid) return reply.code(401).send({ error: result.reason });
const parsed = JSON.parse(rawBody);
// ... handle the action
reply.send({ id: "new-id" });
});