@bondi-labs/integration-sdk - v0.0.3
    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 actions individually so framework adapters (`actionHandler`) can
    // infer their `body` and `response` types from the Zod schemas.
    export const createContact = 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() }),
    });

    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: [createContact],
    },
    ],
    });
    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. The framework adapters do everything for you: HMAC signature verification, raw-body capture, Zod body validation, response validation, and JSON serialization.

    actionHandler infers the body and response types from the action's Zod schemas — no as any, no JSON.parse, no verifyRequest boilerplate.

    // src/server.ts
    import express from "express";
    import { actionHandler } from "@bondi-labs/integration-sdk/express";
    import { createContact } from "./bondi.integration";

    const app = express();

    app.post("/contacts", actionHandler(createContact, async ({ body }) => {
    // body: { email: string; fullName: string } — typed via Zod
    // signature verified, action header validated, body parsed
    return { id: "new-id" };
    }));

    app.listen(3000);

    The token is read from BONDI_INTEGRATION_TOKEN automatically. Pass it explicitly if you prefer: actionHandler(createContact, fn, { token }).

    import Fastify from "fastify";
    import { actionHandler } from "@bondi-labs/integration-sdk/fastify";
    import { createContact } from "./bondi.integration";

    const app = Fastify();

    // One-time setup so Fastify hands us raw bytes (HMAC needs the original body):
    app.addContentTypeParser("application/json", { parseAs: "buffer" },
    (_req, body, done) => done(null, body));

    app.post("/contacts", actionHandler(createContact, async ({ body }) => {
    return { id: "new-id" };
    }));

    Anywhere with the standard Web Request/Response:

    // app/api/contacts/route.ts (Next.js App Router)
    import { withAction } from "@bondi-labs/integration-sdk/web";
    import { createContact } from "@/bondi.integration";

    export const POST = withAction(createContact, async ({ body }) => {
    return { id: "new-id" };
    });

    If you don't have an action definition handy (or want full control over the response shape), use the verification primitives:

    // Express — middleware that just verifies the signature
    import { bondiExpressMiddleware } from "@bondi-labs/integration-sdk/express";
    const verify = bondiExpressMiddleware();
    app.post("/custom", verify, (req, res) => { /* req.body parsed, sig verified */ });

    // Web standard — bare verifier
    import { verifyWebRequest } from "@bondi-labs/integration-sdk/core";
    const result = await verifyWebRequest(request);
    if (!result.valid) return new Response(null, { status: 401 });
    const data = JSON.parse(result.body!);

    // Anywhere — pure function, no framework dependency
    import { verifyRequest } from "@bondi-labs/integration-sdk/core";
    const result = verifyRequest({
    token: process.env.BONDI_INTEGRATION_TOKEN!,
    signature: headers["x-bondi-signature"],
    timestamp: headers["x-bondi-timestamp"],
    action: headers["x-bondi-action"],
    rawBody, // string of the original bytes — never re-serialized JSON
    });