npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@mnemix-ai/vapi-kit

v0.3.0

Published

Mnemix integration kit for Vapi voice assistants — assistant-request enrichment, per-call cache, post-call memory write-back.

Readme

@mnemix-ai/vapi-kit

Mnemix integration kit for Vapi voice agents. Assistant-request enrichment, per-turn cache reuse, and end-of-call memory write-back.

Why this exists

Vapi can ask your server.url for an assistant at call start and can call the same server during later LLM turns. This kit maps supported events to Mnemix's frozen caller-memory API, returning stored caller memory with nullable Trestle and Twilio Lookup enrichment and submitting completed-call context after a verified end-of-call report. This is a custom integration kit, not a native or certified Vapi adapter.

The kit is intentionally thin. It verifies Vapi webhook signatures when configured, calls the supported Mnemix v1 endpoints, builds Vapi-ready assistant context, caches recall by call.id, and degrades to safe defaults when external data is unavailable.

Install

npm install @mnemix-ai/vapi-kit @mnemix-ai/client

Quickstart: Cloudflare Worker

import { Mnemix } from "@mnemix-ai/client";
import {
  handleVapiEndOfCallReport,
  handleVapiServerUrl,
  type VapiEndOfCallReportPayload,
  type VapiServerUrlEvent,
} from "@mnemix-ai/vapi-kit";

interface Env {
  MNEMIX_KEY: string;
  MNEMIX_BASE_URL?: string;
  VAPI_WEBHOOK_SECRET?: string;
}

export default {
  async fetch(req: Request, env: Env): Promise<Response> {
    if (req.method !== "POST") {
      return new Response("Method not allowed", { status: 405 });
    }

    const rawBody = await req.text();
    const signature = req.headers.get("x-vapi-signature") ?? undefined;
    const payload = JSON.parse(rawBody) as VapiServerUrlEvent | VapiEndOfCallReportPayload;
    const mnemix = new Mnemix({
      apiKey: env.MNEMIX_KEY,
      baseUrl: env.MNEMIX_BASE_URL || undefined,
    });
    const opts = {
      mnemix,
      vapiWebhookSecret: env.VAPI_WEBHOOK_SECRET,
    };

    if (payload.message.type === "end-of-call-report") {
      return Response.json(
        await handleVapiEndOfCallReport(opts, payload as VapiEndOfCallReportPayload, {
          signature,
          rawBody,
        }),
      );
    }

    const response = await handleVapiServerUrl(opts, payload as VapiServerUrlEvent, {
      signature,
      rawBody,
      tone: "warm",
      agentName: "the assistant",
    });

    return Response.json(response);
  },
};

Point Vapi's assistant server.url at the Worker route that contains this handler, then use examples/vapi-assistant.json as a starting config.

Quickstart: Node Express

import express from "express";
import { Mnemix } from "@mnemix-ai/client";
import {
  handleVapiEndOfCallReport,
  handleVapiServerUrl,
  type VapiEndOfCallReportPayload,
  type VapiServerUrlEvent,
} from "@mnemix-ai/vapi-kit";

const app = express();
app.use(express.text({ type: "application/json" }));

const mnemix = new Mnemix({
  apiKey: process.env.MNEMIX_KEY!,
  baseUrl: process.env.MNEMIX_BASE_URL || undefined,
});

app.post("/vapi/server", async (req, res) => {
  const rawBody = req.body as string;
  const signature = req.header("x-vapi-signature") ?? undefined;
  const payload = JSON.parse(rawBody) as VapiServerUrlEvent | VapiEndOfCallReportPayload;
  const opts = {
    mnemix,
    vapiWebhookSecret: process.env.VAPI_WEBHOOK_SECRET,
  };

  if (payload.message.type === "end-of-call-report") {
    const result = await handleVapiEndOfCallReport(opts, payload as VapiEndOfCallReportPayload, {
      signature,
      rawBody,
    });
    res.json(result);
    return;
  }

  const response = await handleVapiServerUrl(opts, payload as VapiServerUrlEvent, {
    signature,
    rawBody,
    tone: "professional",
  });
  res.json(response);
});

app.listen(process.env.PORT ? Number(process.env.PORT) : 8787);

Quickstart: Next.js App Router

// app/api/vapi/server/route.ts
import { Mnemix } from "@mnemix-ai/client";
import {
  handleVapiEndOfCallReport,
  handleVapiServerUrl,
  type VapiEndOfCallReportPayload,
  type VapiServerUrlEvent,
} from "@mnemix-ai/vapi-kit";

const mnemix = new Mnemix({
  apiKey: process.env.MNEMIX_KEY!,
  baseUrl: process.env.MNEMIX_BASE_URL || undefined,
});

export async function POST(req: Request) {
  const rawBody = await req.text();
  const signature = req.headers.get("x-vapi-signature") ?? undefined;
  const payload = JSON.parse(rawBody) as VapiServerUrlEvent | VapiEndOfCallReportPayload;
  const opts = {
    mnemix,
    vapiWebhookSecret: process.env.VAPI_WEBHOOK_SECRET,
  };

  if (payload.message.type === "end-of-call-report") {
    return Response.json(
      await handleVapiEndOfCallReport(opts, payload as VapiEndOfCallReportPayload, {
        signature,
        rawBody,
      }),
    );
  }

  return Response.json(
    await handleVapiServerUrl(opts, payload as VapiServerUrlEvent, {
      signature,
      rawBody,
      tone: "warm",
    }),
  );
}

Server URL Events

Vapi sends all events to the configured server.url as JSON with a message.type.

assistant-request is the cold-start path. The kit reads message.call.id as the cache key and message.call.customer.number as the canonical E.164 caller phone number, calls recallAndEnrich() once, builds variables, and returns a Vapi assistant response.

function-call is the per-turn path. Vapi's server.url fires per LLM turn. The kit caches the Mnemix context by call.id (default 15 min TTL) so per-turn invocations don't re-hit Mnemix. Function handlers can read the cached caller context and return small tool results without repeating enrichment.

end-of-call-report is the submission path. The kit maps Vapi's artifact.transcript, artifact.messages, artifact.recordingUrl, timestamps, and endedReason into POST /v1/calls/end, then invalidates the cache entry for the completed call.id. A successful acknowledgement is not persistence proof; verify the update through a later read before depending on it.

Cache Layer

The cache exists because Vapi may invoke your server URL on every LLM turn. The first assistant-request stores Mnemix context and prompt variables under call.id; later function-call messages with the same call.id reuse that context.

Cache is process/isolate-local and therefore best-effort across a scaled deployment. Correctness does not depend on a cache hit: a miss re-fetches from Mnemix before falling back to the last known context or safe defaults.

Default TTL is 15 minutes. If a call outlives the TTL, the kit re-fetches from Mnemix on the next per-turn event and falls back to the last known context if the re-fetch fails.

What You Get Back

handleVapiServerUrl() returns a Vapi-ready JSON response. For assistant requests, that response can include variable values and system prompt context derived from:

interface VapiCallVariables {
  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;
}

The variables are the prompt-safe subset. Keep full Mnemix responses in logs only when your retention policy allows it.

System Prompt Customization

import { buildSystemPrompt } from "@mnemix-ai/vapi-kit";

const systemPrompt = buildSystemPrompt(ctx.variables, {
  tone: "warm",
  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 VAPI_WEBHOOK_SECRET in your deployment and configure the same secret in Vapi. Vapi sends x-vapi-signature as sha256=<hex>. Pass the raw request body and signature header to the kit so verifyVapiSignature() can check the exact bytes Vapi signed.

If signature verification fails, do not return enriched context. The kit rejects the request or returns safe defaults depending on the handler path you call.

Graceful Degradation

Voice agents 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. Vapi can continue the call while Mnemix logs the failure path.

Partial enrichment is still useful. Twilio Lookup can supply carrier data while Trestle is unavailable.

Configuration

| Option | Required | Description | | --- | --- | --- | | mnemix | Yes | A Mnemix client constructed with your API key. | | vapiWebhookSecret | No, recommended | Shared secret used to verify Vapi webhook signatures. | | recallTimeoutMs | No | Maximum time to wait before falling back to default assistant context. | | cacheTtlMs | No | Per-call cache TTL. Defaults to 15 minutes. | | logger | No | Logger with info, warn, and error; defaults to console. |

Public API Surface

  • POST /v1/recall_and_enrich for assistant-request voice memory and enrichment.
  • POST /v1/calls/end for end-of-call memory write-back.

The only other frozen public v1 endpoint is GET /v1/caller/{phone_number}; this kit does not call it.

Construct the client with baseUrl: process.env.MNEMIX_BASE_URL || undefined when you need an explicit origin. Empty or omitted values let @mnemix-ai/client use its default, https://mcp.mnemix.ai. The Vapi path is designed for sub-300ms voice recall.

Privacy

The kit requires E.164 caller numbers and redacts phone numbers in its own logs. If your agent sends transcripts or caller context to a model provider, redact or filter that data in your application before write-back.

Keep Vapi recordings, transcripts, and webhook payloads aligned with your own consent, retention, and data processing terms.

License

MIT