How it works
Each user message is one synchronous POST /v1/ai request. No WebSockets, no webhooks.
- The first request has no
session_id. ModelRiver starts a conversation and returns its id inmeta.session_id. - Every later request sends that
session_idwith only the new user message. ModelRiver adds the earlier conversation itself (recent turns plus a rolling summary), so you never keep or resend the history.
Before you start
- A project API key (
mr_live_…). See API keys. - A chat workflow with Session memory turned on. It's in the workflow's Advanced settings and is off by default. This page uses a workflow named
my_chatbot. - Leave Save full request and response text on in Project settings → Request logs. Session memory is built from the stored text.
First message
Send the workflow and the user's message:
curl https://api.modelriver.com/v1/ai \ -H "Authorization: Bearer $MODELRIVER_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "workflow": "my_chatbot", "messages": [{"role": "user", "content": "Hi, my name is Sam."}] }'The response (trimmed) has the reply and the new session id:
1{2 "status": "success",3 "data": {4 "choices": [5 { "message": { "role": "assistant", "content": "Nice to meet you, Sam! How can I help?" } }6 ]7 },8 "meta": {9 "workflow": "my_chatbot",10 "session_id": "9f8c2c1e-5b8a-4d0e-9a51-2f6f0f1d7a10"11 }12}- The reply text is at
data.choices[0].message.content. That holds for any chat workflow without a structured output; with one,datais your parsed JSON object instead. See Response formats. - Check
statusfirst. If the provider fails, ModelRiver still answers200, with"status": "error"and anerrorobject. See Error handling.
Next messages
Send the same workflow, the session_id, and only the new message:
curl https://api.modelriver.com/v1/ai \ -H "Authorization: Bearer $MODELRIVER_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "workflow": "my_chatbot", "session_id": "9f8c2c1e-5b8a-4d0e-9a51-2f6f0f1d7a10", "messages": [{"role": "user", "content": "Do you remember my name?"}] }'The model sees the first turn, so it can answer "Sam". The session_id stays the same for the whole conversation.
Don't add earlier messages to messages. The server merges memory for you, and sending the history again would repeat it.
A terminal chat in Node.js
Save this as chat.mjs and run it with Node.js 18 or later. Type a message and press Enter; an empty line quits.
1import readline from "node:readline/promises";2 3const rl = readline.createInterface({ input: process.stdin, output: process.stdout });4let sessionId = null;5 6while (true) {7 const text = await rl.question("you> ");8 if (!text.trim()) break;9 10 const res = await fetch("https://api.modelriver.com/v1/ai", {11 method: "POST",12 headers: {13 Authorization: `Bearer ${process.env.MODELRIVER_API_KEY}`,14 "Content-Type": "application/json",15 },16 body: JSON.stringify({17 workflow: "my_chatbot",18 ...(sessionId && { session_id: sessionId }),19 messages: [{ role: "user", content: text }],20 }),21 });22 const body = await res.json();23 24 if (body.status !== "success") {25 console.error(body.error ?? body);26 continue;27 }28 sessionId = body.meta.session_id;29 console.log("bot>", body.data.choices[0].message.content);30}31rl.close();MODELRIVER_API_KEY=mr_live_your_key node chat.mjsIn a web app, run the same request on your server so the API key never reaches the browser, and store one session_id per conversation.
Start over
To reset the memory, delete the session and send the next message without session_id:
curl -X DELETE https://api.modelriver.com/v1/sessions/9f8c2c1e-5b8a-4d0e-9a51-2f6f0f1d7a10 \ -H "Authorization: Bearer $MODELRIVER_API_KEY"You can also delete sessions in the console under Logs → Sessions. Sending a deleted or unknown session_id returns 422 session_not_found.
Read the history
ModelRiver stores every turn, so you don't need your own copy. GET /v1/sessions/:id returns the conversation, oldest message first, and GET /v1/sessions lists a project's conversations. See the Sessions API.
Going further
The chatbot tutorial builds a full React and Node.js app on the same session memory, with async requests, real-time WebSocket delivery, backend callbacks and structured replies. For token-by-token replies, see Streaming.