Skip to content

Guides

Interactive support chat

Stream answers into your own chat interface while keeping conversation state, reusable files and recovery under your control.

How the pieces connect

  1. 01Chat turn
  2. 02Your backend
  3. 03Responses SSE
  4. 04Streaming UI

A real-world example

Take Safarnameh, a travel agency with a website chat. Travelers ask about their bookings day and night: can they change a date, what baggage is included, what documents they need. Many of the answers are in the booking confirmation the traveler already has.

With Mahvash, the chat streams an answer word by word while the traveler watches. The traveler can attach their booking confirmation once and keep asking follow-up questions, and the assistant remembers the conversation.

What the business gets

  • Instant, natural answers that appear as they are written, like talking to a person.
  • Context that carries over. Follow-up questions work without the traveler repeating themselves.
  • Honest limits. When the answer is not in the booking or the knowledge base, the assistant can say so and hand over to an agent.
  • Reliable sessions. A dropped connection is recovered from the stored response instead of being charged twice.

See it with a real conversation

TurnTravelerAssistantStored by the backend
1Attaches booking-confirmation.pdf: “Can I move my Kish flight from the 12th to the 14th?”Streams: “Your ticket is a flexible economy fare, so you can change the date once; you pay only the fare difference…”Asset UUID, response resp_A
2“Roughly how much would that cost?”“Your confirmation doesn't include current prices, so I can't give an exact amount. I can connect you with an agent to check availability.”Response resp_B, continued from resp_A
3Asks about baggage; the phone loses signal mid-answer.After reconnecting, the full answer is shown from the stored response.Response resp_C, retrieved by ID — no second charge

What the Safarnameh backend sends for each turn:

{
  "model": "auto",
  "stream": true,
  "input": "Can I move my Kish flight from the 12th to the 14th?",
  "asset_ids": ["<booking-confirmation-asset-uuid>"]
}

// Your backend saves the id from response.created: resp_A

The browser never talks to Mahvash directly. It talks to Safarnameh's own chat endpoint, which holds the project key, streams the text through, and stores each response ID with the conversation.

Note:The company, customer and policy details in this example are fictional. Mahvash answers from the documents and data you provide; it does not invent your policies.

Your chat remains the source of truth

Your application owns users, chat permissions and how messages look. Its backend opens a Mahvash Responses stream with a project key and forwards only safe events, or derived text, to the browser.

Set up reusable context

  • Choose a project and a model that supports streaming.
  • When users refer to a file across turns, upload it once as a project asset and keep its UUID in your chat record.
  • If the chat should answer from your knowledge base, add a file_search tool for a ready vector store.

Stream a turn and continue

  1. 1Create a Responses request with stream: true, the user's message, and any asset_ids.
  2. 2Save the response ID from response.created immediately.
  3. 3Append response.output_text.delta to the message as it arrives.
  4. 4On response.completed, store the final text and usage.
  5. 5On the next turn, pass the previous response ID as previous_response_id so the model sees the conversation.

Handle response.incomplete and response.failed explicitly — they are normal outcomes, not exceptions.

Streaming server example

The TypeScript and Python examples consume SSE frames and record the terminal response ID. In production, relay events through your authenticated backend and never treat a disconnected socket as proof of failure.

Implementation example

// Server route called by your authenticated chat UI.
const response = await fetch("https://api.mahvashai.ir/v1/responses", {
  method: "POST",
  headers: { Authorization: "Bearer " + process.env.MAHVASH_PROJECT_KEY!, "Content-Type": "application/json" },
  body: JSON.stringify({ model: "auto", input: userMessage, stream: true,
    previous_response_id: chat.lastCompletedResponseId || undefined,
    asset_ids: chat.assetIds })
});
if (!response.ok || !response.body) throw new Error("Stream could not start");
// Parse the SSE data frames on your server and forward only the events your UI needs.
for await (const event of parseSse(response.body)) {
  if (event.type === "response.output_text.delta") await ui.sendDelta(event.delta);
  if (event.type === "response.completed") {
    await db.chats.saveCompletedResponse(chat.id, event.response.id);
  }
  if (event.type === "response.failed" || event.type === "response.incomplete") {
    await ui.showRecoveryState();
  }
}

Example response checkpoint

data: {"type":"response.created","sequence_number":1,"response":{"id":"resp_example","status":"in_progress"}}

data: {"type":"response.output_text.delta","sequence_number":4,"output_index":0,"content_index":0,"delta":"I can help"}

data: {"type":"response.completed","sequence_number":9,"response":{"id":"resp_example","status":"completed"}}

data: [DONE]

Illustrative response excerpt. Your application owns the surrounding workflow and customer data.

Chat UI state

Model three states in your UI: streaming while deltas arrive, complete after the terminal event, and recoverable when the connection drops before the end. Keep the response ID and asset IDs with the correct customer conversation.

A safe retry boundary

If the connection drops after the request was sent, retrieve the stored response by ID before doing anything else — a new request creates another paid run. Offer "retry" to the user only when the stored response shows the turn actually failed.

previous_response_id continues a conversation only inside the project that owns it; never try to continue a response that belongs to another project.

Use the same pattern for

  • Online stores — a shopping assistant that answers from product and order information.
  • Banking and fintech apps — in-app help grounded in published terms.
  • Universities — admissions and student-services chat over official regulations.
  • Software products — in-app help that can read a screenshot or file the user attaches.