Skip to content
DaneshyarDocs
Sign in
  • Introduction
  • Quickstart
  • API keys
  • Authentication
  • Apps, workspaces, resources
  • Ingestion
  • Chat and sessions
  • Citations
  • Skills
  • Conventions
  • Workspaces
  • Resources
  • Chat
  • Skills
  • Errors
  • Customer support assistant
  • Internal knowledge base
  • Documentation chatbot
  • Embedded widgetOn the roadmap: Soon
  • TypeScript SDKOn the roadmap: Soon
  1. Docs
  2. Examples
  3. Customer support assistant

Customer support assistant

Answer customer questions from your help centre, with a link to the source.

A help centre your customers can ask questions of instead of searching. One workspace, every published article in it, answers that link back to the article they came from.

Architecture

Your frontend never touches this API. It calls your own endpoint, which holds the key and forwards the question — that is what keeps a credential capable of deleting workspaces off the public internet.

  1. Browser → your /support/ask route (your session, your rate limits).
  2. Your route → this API, with the API key and the customer's id.
  3. Answer and sources back the same way.

Setup

Create one workspace and upload every published article into it. A public help centre has nothing to separate, so a single shared workspace is the right shape.

Re-upload an article when it changes and delete the old resource. There is no update-in-place: ingestion runs on upload, so a changed document is a new resource.

The code

Two functions: one to seed the workspace, one to answer. The sources are deduplicated per article, because three retrieved passages from one page should render as one link.

JavaScript
const BASE = "https://api.daneshyar.info/api/v1";
const KEY = process.env.DANESHYAR_API_KEY;

const WORKSPACE = "8f14e45f-ceea-467a-9f4c-1a2b3c4d5e6f";

/**
 * One shared workspace holding every help-centre article, because in a public
 * help centre there is nothing to separate — every reader may see every doc.
 */
export async function seedHelpCentre(articles) {
  for (const article of articles) {
    const body = new FormData();
    body.append("file", new Blob([article.markdown]), `${article.slug}.pdf`);
    body.append("title", article.title);

    await fetch(`${BASE}/workspaces/${WORKSPACE}/resources/`, {
      method: "POST",
      headers: { "X-API-Key": KEY },
      body,
    });
  }
}

/** Called from your own /support/ask route, which holds the key. */
export async function ask({ customerId, question, sessionId }) {
  const res = await fetch(`${BASE}/workspaces/${WORKSPACE}/chat/`, {
    method: "POST",
    headers: { "X-API-Key": KEY, "Content-Type": "application/json" },
    body: JSON.stringify({
      // Your customer's id, so one person's thread is never another's.
      external_user_id: customerId,
      session_id: sessionId,
      message: question,
    }),
  });

  const { data } = await res.json();
  const reply = data.assistant_message;

  return {
    sessionId: data.session.id,
    answer: reply.content,
    // Deduplicate per article: three passages from one page is one link.
    sources: [
      ...new Map(
        reply.citations.map((c) => [c.resource_id, c.resource_title]),
      ),
    ].map(([id, title]) => ({ id, title })),
  };
}

What you get

Customers get an answer with the articles it came from, and you get a session per customer that survives page reloads. Wire the source list to your own article URLs and a reader can verify every claim in one click.

PreviousErrorsNextInternal knowledge base

On this page

  • Architecture
  • Setup
  • The code
  • What you get

Every example on this site runs against the live API. If one doesn't, tell us — that's a bug in the docs.

ServicesAboutTalk to us