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
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.
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.
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.
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.
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.