@bondi-labs/integration-sdk - v0.0.1
    Preparing search index...

    Getting started — plain Node / Express / Fastify

    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" });
    });