@mnemix-ai/bland-kit
v0.1.0
Published
Mnemix integration kit for Bland.ai voice pathways — pre-call enrichment + post-call memory write-back.
Downloads
35
Maintainers
Readme
@mnemix-ai/bland-kit
Mnemix integration kit for Bland.ai voice pathways. Pre-call enrichment + post-call memory write-back.
Why this exists
Bland builds production voice pathways. Mnemix is the memory and identity layer behind those pathways: every inbound call can resolve the caller through Twilio Lookup, Trestle, and Baylio before the first audio packet, then return variables your pathway can template into the first turn.
After the call, the kit writes a structured summary back to Mnemix so the next call starts with useful context. The kit is intentionally thin: it verifies Bland webhooks when configured, calls the three supported Mnemix v1 endpoints, builds pathway variables, and degrades to safe defaults when external data is unavailable.
Install
npm install @mnemix-ai/bland-kit @mnemix-ai/clientQuickstart: Cloudflare Worker
import { Mnemix } from "@mnemix-ai/client";
import {
handleBlandPostCall,
handleBlandPreCall,
type BlandPostCallPayload,
type BlandPreCallPayload,
} from "@mnemix-ai/bland-kit";
interface Env {
MNEMIX_KEY: string;
BLAND_WEBHOOK_SECRET?: string;
MNEMIX_TENANT?: string;
}
export default {
async fetch(req: Request, env: Env): Promise<Response> {
if (req.method !== "POST") {
return new Response("Method not allowed", { status: 405 });
}
const url = new URL(req.url);
const rawBody = await req.text();
const signature = req.headers.get("x-bland-signature") ?? undefined;
const mnemix = new Mnemix({ apiKey: env.MNEMIX_KEY });
const opts = {
mnemix,
blandWebhookSecret: env.BLAND_WEBHOOK_SECRET,
tenantId: env.MNEMIX_TENANT,
};
if (url.pathname === "/bland/pre-call") {
const payload = JSON.parse(rawBody) as BlandPreCallPayload;
const ctx = await handleBlandPreCall(opts, payload, {
signature,
rawBody,
tone: "warm",
agentName: "the assistant",
});
return Response.json({ variables: ctx.variables, system_prompt: ctx.system_prompt });
}
if (url.pathname === "/bland/post-call") {
const payload = JSON.parse(rawBody) as BlandPostCallPayload;
return Response.json(await handleBlandPostCall(opts, payload));
}
return new Response("Not found", { status: 404 });
},
};Quickstart: Node Express
import express from "express";
import { Mnemix } from "@mnemix-ai/client";
import { handleBlandPostCall, handleBlandPreCall } from "@mnemix-ai/bland-kit";
const app = express();
app.use(express.text({ type: "application/json" }));
const mnemix = new Mnemix({ apiKey: process.env.MNEMIX_KEY! });
app.post("/bland/pre-call", async (req, res) => {
const rawBody = req.body as string;
const ctx = await handleBlandPreCall(
{
mnemix,
blandWebhookSecret: process.env.BLAND_WEBHOOK_SECRET,
tenantId: process.env.MNEMIX_TENANT,
},
JSON.parse(rawBody),
{
signature: req.header("x-bland-signature") ?? undefined,
rawBody,
tone: "warm",
},
);
res.json({ variables: ctx.variables, system_prompt: ctx.system_prompt });
});
app.post("/bland/post-call", async (req, res) => {
const result = await handleBlandPostCall({ mnemix }, JSON.parse(req.body as string));
res.json(result);
});
app.listen(process.env.PORT ?? 8787);Quickstart: Next.js App Router
// app/api/bland/[hook]/route.ts
import { Mnemix } from "@mnemix-ai/client";
import { handleBlandPostCall, handleBlandPreCall } from "@mnemix-ai/bland-kit";
const mnemix = new Mnemix({ apiKey: process.env.MNEMIX_KEY! });
export async function POST(
req: Request,
{ params }: { params: { hook: string } },
) {
const rawBody = await req.text();
const opts = {
mnemix,
blandWebhookSecret: process.env.BLAND_WEBHOOK_SECRET,
tenantId: process.env.MNEMIX_TENANT,
};
if (params.hook === "pre-call") {
const ctx = await handleBlandPreCall(opts, JSON.parse(rawBody), {
signature: req.headers.get("x-bland-signature") ?? undefined,
rawBody,
tone: "professional",
});
return Response.json({ variables: ctx.variables, system_prompt: ctx.system_prompt });
}
if (params.hook === "post-call") {
return Response.json(await handleBlandPostCall(opts, JSON.parse(rawBody)));
}
return Response.json({ error: "unknown hook" }, { status: 404 });
}What You Get Back
handleBlandPreCall() returns a BlandPreCallContext:
interface BlandPreCallContext {
variables: {
caller_name: string;
is_returning: boolean;
last_intent: string | null;
last_call_summary: string | null;
carrier: string | null;
line_type: string | null;
company: string | null;
role: string | null;
industry: string | null;
suggested_intent: string | null;
};
system_prompt: string;
mnemix: {
trace_id: string;
known: boolean;
caller_id: string;
memory_age_ms: number;
timing_ms: { total: number; memory_ms: number; enrichment_ms: number };
};
}variables is the safe subset Bland should template into prompts and branch conditions. system_prompt is a ready-to-use prompt assembled from those variables. mnemix carries trace and timing data for logs, support, and debugging.
System Prompt Customization
import { buildSystemPrompt } from "@mnemix-ai/bland-kit";
const systemPrompt = buildSystemPrompt(ctx.variables, {
tone: "warm", // "warm" | "professional" | "casual"
agentName: "Avery",
brandName: "Northstar Support",
fallbackGreeting: "Hi there",
});The builder does not invent missing history. If Mnemix returns no memory, the prompt tells the agent to ask rather than assume.
Webhook Signature Verification
Set BLAND_WEBHOOK_SECRET in your deployment and pass blandWebhookSecret to the kit. For pre-call webhooks, also pass the raw request body and the X-Bland-Signature header to handleBlandPreCall().
The kit verifies HMAC-SHA256 signatures through verifyBlandSignature(). If signature verification fails, the pre-call path returns a safe default context instead of exposing enriched caller data.
Graceful Degradation
Voice pathways should not block on enrichment. If Mnemix times out, returns a 5xx, receives a malformed phone number, or returns partial enrichment, the kit returns defaults such as caller_name: "there" and is_returning: false. Bland can continue the call while Mnemix logs the failure path.
Partial enrichment is still useful. For example, Twilio Lookup can supply carrier data while Trestle is unavailable, or Baylio can suggest a likely intent without a full profile.
Errors
BlandKitError is the base typed error for kit-level failures.
BlandKitSignatureError represents invalid Bland webhook signatures. The pre-call handler is designed to degrade safely; direct users of verifyBlandSignature() can throw or map this error according to their own policy.
Mnemix client errors are not wrapped unless the kit needs to add Bland-specific context.
Configuration
| Option | Required | Description |
| --- | --- | --- |
| mnemix | Yes | A Mnemix client constructed with your API key. |
| blandWebhookSecret | No, recommended | Shared secret used to verify Bland webhook signatures. |
| tenantId | No | Optional tenant override when one API key serves multiple tenants. |
| recallTimeoutMs | No | Maximum time to wait before falling back to default pre-call context. |
| logger | No | Logger with info, warn, and error; defaults to console. |
The 3 v1 Endpoints This Kit Calls
POST /v1/recall_and_enrichfor pre-call memory and enrichment.POST /v1/calls/endfor post-call memory write-back.GET /v1/caller/{phone_number}for optional out-of-band caller lookup.
The default API base URL is https://mnemix-api.sayeed965.workers.dev.
Privacy & Compliance
Phone numbers are normalized to E.164 before use. Audit logging stores a phone hash produced with HMAC-SHA256 over the tenant secret and normalized phone number. The kit sends caller context to Mnemix only; it does not send PII to OpenAI or Anthropic. If your agent sends transcripts to a model provider, redact or filter those transcripts in your application before write-back.
Mnemix supports GDPR-compliant access, deletion, and audit workflows. Keep your Bland retention settings, transcript storage, and recording URLs aligned with your own data processing agreements.
License
MIT
