All integrations

Voice agent · Custom guide

Northsun + LiveKit

LiveKit Agents has no vendor-initiated webhook: a LiveKit Agent is a worker process that connects outward to the LiveKit server, and your own code is what calls Northsun. This guide maps that worker-initiated pattern to Northsun's frozen caller-memory API with plain fetch; it is a custom guide, not a native or certified adapter.


What Northsun adds to LiveKit

  • A caller-memory lookup you control.

    Your worker code decides when to call Northsun after ctx.waitForParticipant() — there is no webhook to configure on LiveKit's side.

  • Direction-safe identity as a documented pattern.

    LiveKit's sip.phoneNumber attribute names the customer the same way on inbound and outbound calls; sip.trunkPhoneNumber never does.

  • A proofable boundary.

    A successful recording proves the exact worker configuration shown; it does not establish a native LiveKit feature.


How to connect LiveKit to Northsun

  1. Resolve the caller's phone number

    Read sip.phoneNumber off the connected participant's attributes after ctx.waitForParticipant(); treat a missing or malformed value as unresolved, not as an unknown caller.

  2. Recall before the agent's instructions are set

    Call POST /v1/recall_and_enrich and use the response to shape the instructions you pass to voice.Agent.

  3. Write back from the shutdown callback

    Register ctx.addShutdownCallback() and call POST /v1/calls/end with the session's transcript and outcome after the room disconnects.

  4. Prove the write-back

    The interaction is committed durably before calls/end responds; retain the Northsun trace ID and confirm it with a later recall.


Custom HTTP pattern

Adapt this boundary to the current vendor payload and verify it with controlled events before production.

tslivekit · illustrative
async function callNorthsun(path: string, body: unknown) {
  const response = await fetch(`https://mcp.northsun.ai${path}`, {
    method: "POST",
    headers: {
      Authorization: `Bearer ${process.env.NORTHSUN_API_KEY}`,
      "Content-Type": "application/json",
    },
    body: JSON.stringify(body),
  });

  if (!response.ok) {
    throw new Error(`Northsun request failed: ${response.status}`);
  }

  return response.status === 204 ? undefined : response.json();
}

// Inside your agent's entry function, after ctx.connect() + ctx.waitForParticipant():
// LiveKit's SIP participant attributes name the customer's number the same way
// in both call directions — see the LiveKit documentation for the direction-safety detail.
const phoneNumber = participant.attributes["sip.phoneNumber"];
const packet = phoneNumber
  ? await callNorthsun("/v1/recall_and_enrich", {
      phone_number: phoneNumber,
      trigger: "answered",
      session_id: ctx.room.name,
    })
  : null;

const instructions = packet
  ? buildYourOwnInstructions(packet)
  : "Greet the caller and ask how you can help.";

// From ctx.addShutdownCallback(), after the session ends:
await callNorthsun("/v1/calls/end", {
  phone_number: phoneNumber,
  session_id: ctx.room.name,
  duration_s: durationSeconds,
  outcome: mappedOutcome,
});

FAQ

Is this a native LiveKit integration?
No. It is a custom guide for calling Northsun's frozen REST endpoints from your own LiveKit Agents worker code.
Why is there no webhook step?
LiveKit Agents has no vendor-initiated webhook for this lifecycle — a LiveKit Agent worker connects outward to LiveKit, so your own code is always what calls Northsun.
Does sip.phoneNumber need a direction check?
No — unlike some platforms' From/To fields, sip.phoneNumber names the customer's number in both inbound and outbound calls. sip.trunkPhoneNumber is always your own number, in either direction.

Choose Northsun as your agent memory layer.