How to Use AI Sessions for Multi-Turn Context
Create an AI session, persist its ID, and reuse it across follow-up prompts to maintain server-managed conversation context.
An AI session groups multiple sendAI turns under one ID so the platform can maintain context across follow-up questions. Pass sessionId on subsequent calls to continue the same conversation.
How Sessions Work
Turn 1 Turn 2
────── ──────
sendAI({ message }) sendAI({
(no sessionId) sessionId: <from turn 1>,
}) message: { text: follow-up }
│ │
▼ ▼
new session ID created same session continued- On the first call, omit sessionId or pass null. The SDK creates a session and returns its ID in the response metadata.
- On follow-up calls, pass that ID as sessionId.
- Sessions are tied to senderId. Use a stable user ID for each end user, support ticket, or conversation owner.
Extracting sessionId (First Turn)
const first = await notifier.sendAI({
senderId: "user-123",
message: { text: "Start a new support session." },
});
const sessionId =
first?.metadata?.packet?.id ??
first?.metadata?.id;Store sessionId in your database keyed by user, ticket, or chat thread.
Continuing a Session
await notifier.sendAI({
senderId: "user-123",
sessionId,
message: {
text: "Can you elaborate on the second point?",
},
});Sessions vs. messages[] History
| Approach | When to use |
|---|---|
| sessionId | Server-managed continuity and simpler client code |
| Full messages[] on each call | Exact client-controlled context window and stateless servers |
| fetchAIHistory | Audit or rebuild a UI from stored turns |
Transport
Use transport: "http" for session continuation.
JavaScript / TypeScript
Continue a multi-turn AI conversation by passing sessionId from the first response into follow-up sendAI calls.
Complete Multi-Turn Example
import { DNotifier } from "@dnotifier-realtime/dnotifier";
const USER_ID = "user-123";
const notifier = new DNotifier({
appId: process.env.DNOTIFIER_APP_ID,
secret: process.env.DNOTIFIER_SECRET,
transport: "http",
userId: USER_ID,
onConnected: () => {},
onMessage: () => {},
onDisconnected: () => {},
});
await notifier.connect();
// Turn 1 — new session
const first = await notifier.sendAI({
senderId: USER_ID,
message: {
text: "I need help with billing for order #4821.",
},
});
const sessionId =
first?.metadata?.packet?.id ??
first?.metadata?.id;
if (!sessionId) {
throw new Error("The AI response did not include a session id");
}
console.log("Session:", sessionId);
console.log("Reply:", first?.data?.content ?? first);
// Turn 2 — same session
const second = await notifier.sendAI({
senderId: USER_ID,
sessionId,
message: {
text: "What documents do I need to submit?",
},
});
console.log("Follow-up:", second?.data?.content ?? second);Persist Session Per Ticket
// After the first turn, save the id to your database.
await db.tickets.update(ticketId, {
dnotifierSessionId: sessionId,
});
// On a later HTTP request, load and reuse it.
const ticket = await db.tickets.get(ticketId);
const response = await notifier.sendAI({
senderId: USER_ID,
sessionId: ticket.dnotifierSessionId,
message: { text: userMessage },
});Chat-Style Messages in a Session
const first = await notifier.sendAI({
senderId: USER_ID,
message: {
messages: [
{
role: "system",
content: "You are a billing specialist.",
},
{
role: "user",
content: "I was charged twice.",
},
],
},
});
const sessionId =
first?.metadata?.packet?.id ??
first?.metadata?.id;
await notifier.sendAI({
senderId: USER_ID,
sessionId,
message: {
messages: [
{
role: "user",
content: "Here is my receipt number: R-9912.",
},
],
},
});TypeScript Helper
function sessionIdFromResponse(
response: unknown,
): string | undefined {
const value = response as {
metadata?: {
packet?: { id?: string };
id?: string;
};
};
return value?.metadata?.packet?.id ?? value?.metadata?.id;
}Troubleshooting
| Issue | Check |
|---|---|
| Follow-up lacks context | Verify sessionId is passed and not regenerated |
| New session each time | Store and reuse the ID from the first response |
| Wrong user context | senderId must match across turns |
Dart / Flutter
Continue a multi-turn AI conversation by passing sessionId from the first response into follow-up sendAI calls.
Complete Multi-Turn Example
import 'package:dnotifier/dnotifier.dart';
const userId = 'user-123';
Future<void> main() async {
final notifier = DNotifier(
appId: 'your-app-id',
secret: 'your-app-secret',
transport: 'http',
userId: userId,
onConnected: () {},
onMessage: (_) {},
);
await notifier.connect();
// Turn 1 — new session
final first = await notifier.sendAI(
senderId: userId,
message: {
'text': 'I need help with billing for order #4821.',
},
);
final sessionId = sessionIdFromResponse(first);
if (sessionId == null) {
throw StateError('The AI response did not include a session id');
}
print('Session: $sessionId');
// Turn 2 — same session
final second = await notifier.sendAI(
senderId: userId,
sessionId: sessionId,
message: {
'text': 'What documents do I need to submit?',
},
);
print('Follow-up: $second');
}
String? sessionIdFromResponse(dynamic response) {
if (response is! Map) return null;
final metadata = response['metadata'];
if (metadata is Map) {
final packet = metadata['packet'];
if (packet is Map && packet['id'] != null) {
return packet['id'].toString();
}
if (metadata['id'] != null) {
return metadata['id'].toString();
}
}
return null;
}Persist Session Per Ticket
class TicketStore {
String? sessionIdForTicket(String ticketId) => _sessions[ticketId];
void saveSession(String ticketId, String sessionId) {
_sessions[ticketId] = sessionId;
}
final Map<String, String> _sessions = {};
}
Future<void> handleUserMessage(
DNotifier notifier,
TicketStore store,
String ticketId,
String text,
) async {
var sessionId = store.sessionIdForTicket(ticketId);
final response = await notifier.sendAI(
senderId: userId,
sessionId: sessionId,
message: {'text': text},
);
sessionId ??= sessionIdFromResponse(response);
if (sessionId != null) {
store.saveSession(ticketId, sessionId);
}
}Chat-Style in a Session
final first = await notifier.sendAI(
senderId: userId,
message: {
'messages': [
{
'role': 'system',
'content': 'You are a billing specialist.',
},
{
'role': 'user',
'content': 'I was charged twice.',
},
],
},
);
final sessionId = sessionIdFromResponse(first);
await notifier.sendAI(
senderId: userId,
sessionId: sessionId,
message: {
'messages': [
{
'role': 'user',
'content': 'Here is my receipt number: R-9912.',
},
],
},
);Python
Continue a multi-turn AI conversation by passing session_id from the first response into follow-up send_ai calls.
Complete Multi-Turn Example
import asyncio
from dnotifier import DNotifier
USER_ID = "user-123"
def session_id_from_response(response) -> str | None:
if not isinstance(response, dict):
return None
metadata = response.get("metadata")
if isinstance(metadata, dict):
packet = metadata.get("packet")
if isinstance(packet, dict) and packet.get("id") is not None:
return str(packet["id"])
if metadata.get("id") is not None:
return str(metadata["id"])
return None
async def main():
notifier = DNotifier(
app_id="your-app-id",
secret="your-app-secret",
transport="http",
user_id=USER_ID,
)
await notifier.connect()
# Turn 1 — new session
first = await notifier.send_ai(
sender_id=USER_ID,
message={
"text": "I need help with billing for order #4821."
},
)
session_id = session_id_from_response(first)
if session_id is None:
raise RuntimeError("The AI response did not include a session id")
print(f"Session: {session_id}")
# Turn 2 — same session
second = await notifier.send_ai(
sender_id=USER_ID,
session_id=session_id,
message={
"text": "What documents do I need to submit?"
},
)
print(f"Follow-up: {second}")
asyncio.run(main())Persist Session Per Ticket
class TicketStore:
def __init__(self):
self._sessions: dict[str, str] = {}
def session_id_for_ticket(self, ticket_id: str) -> str | None:
return self._sessions.get(ticket_id)
def save_session(self, ticket_id: str, session_id: str):
self._sessions[ticket_id] = session_id
async def handle_user_message(
notifier,
store: TicketStore,
ticket_id: str,
text: str,
):
session_id = store.session_id_for_ticket(ticket_id)
response = await notifier.send_ai(
sender_id=USER_ID,
session_id=session_id,
message={"text": text},
)
if session_id is None:
new_session = session_id_from_response(response)
if new_session:
store.save_session(ticket_id, new_session)Chat-Style in a Session
first = await notifier.send_ai(
sender_id=USER_ID,
message={
"messages": [
{
"role": "system",
"content": "You are a billing specialist.",
},
{
"role": "user",
"content": "I was charged twice.",
},
],
},
)
session_id = session_id_from_response(first)
await notifier.send_ai(
sender_id=USER_ID,
session_id=session_id,
message={
"messages": [
{
"role": "user",
"content": "Here is my receipt number: R-9912.",
},
],
},
)