Documentation/Reference apps/Concierge is an open-source help desk whose AI reads the help center
Concierge is an open-source help desk whose AI reads the help center
Concierge is an open-source help desk with an AI assistant, a ticket queue and an agent console. MIT source on GitHub, live demo at concierge.supero.live.
- apps
- customer-support
- ai
- knowledge-base
- ticketing
Overview
This open-source help desk puts a public help center and a staffed ticket queue on four schemas, and its AI assistant answers from the same twelve articles a visitor can read.
Ask Concierge how to reset a password and the model is told to name the help article it used. That article is a row in the
Article table. It is the same row the help center renders. Nothing else sits behind the chat box. In ui/app.js a function called kbContext takes the first 24 articles the browser has loaded and joins each title and category to its summary plus the first 400 characters of body text. askConcierge places the result in a prompt under the heading KNOWLEDGE BASE, instructs the model to answer in two to four sentences using only what is there, and sends it through services.ai.complete. There is no vector store, meaning no separate search index built for the model. There is no second copy of the content. The code names no model either. It hands the prompt to the ai service and renders whatever text comes back.The whole app is one folder on GitHub, apps/customer-support/concierge.
One table, read three ways
Articles are public. Two lines near the bottom of
schemas.py, the file that defines the app's four tables, say so:python
ALL_SCHEMAS = [Article, Ticket, Message, Macro]
PUBLIC_SCHEMAS = ["article"]The second line is why an anonymous visitor can browse six categories and open any article without signing in. On 2 October 2026 the live app's public endpoint for
article returned twelve rows, two per category, matching the seed, the demo data that setup.py loads. The same endpoint for ticket answered 403.Those twelve rows are also the model's context. And they are the fallback. When the
ai service is unavailable or its reply comes back empty, askConcierge catches the failure and hands the question to kbSearch, a scoring function that counts how many of the question's words appear in each article's title, summary, tags or category and returns the three best matches for the chat to display. The bubble is then labelled "Concierge · KB" where a model answer would say "Concierge AI". A reader can tell which path spoke.One edit in the agent console therefore changes the help page, the prompt and the fallback together.
The agent gets a draft and still has to press send
Staff sign in to a separate console holding the queue, an article editor and a list of macros (saved replies). Open a ticket there and a "Suggest reply" button sits above the reply box. It calls
suggestReply, builds a second prompt from the knowledge base plus the ticket subject and the full thread, and drops whatever comes back into the textarea.Nothing is sent.
The agent can send the draft or rewrite it. A dropdown beside the button swaps in one of five seeded macros instead.
An agent's first reply moves the ticket from
open to pending. "Resolve" stamps resolved_at. The customer then sees five star buttons on the resolved ticket, and a click writes csat, the customer satisfaction score, and closes it. The 1 to 5 range is declared in the schema, where the UI cannot forget it:python
"validations": [
{"id": "csat-range", "when": {"!=": [{"var": "csat"}, None]},
"assert": {"and": [{">=": [{"var": "csat"}, 1]}, {"<=": [{"var": "csat"}, 5]}]},
"message": "CSAT must be between 1 and 5.", "severity": "error"},
],A ticket is a parent and its thread is the children
Message declares "parent_type": "ticket", and the thread view fetches a conversation with client.getScopedList('ticket', uuid, 'message'). Each message records whether a customer, an agent or the AI sent it. The seed writes seven tickets holding sixteen messages. Four of the seven are flagged ai_handled, and the console's "AI deflection" tile is that flag counted across every ticket and shown as a percentage.Who sees which ticket is settled in
setup.py. It defines policies for two roles. Agents are tenant_admin. Customers are tenant_user, and their rules on ticket and message carry filter_field="owner_username" with filter_match="$user.name". That is policy-based row scoping: one call to client.getObjects('ticket') returns a customer's own conversations to the customer and the whole queue to an agent, and there is no branch anywhere in the front end deciding which of the two it should be.An event binding sits beside those policies. It ties
@create:concierge:ticket to a workflow called ticket_acknowledgement, whose single step emails the customer when a ticket is created. The front end never calls it. Creating the ticket row is the trigger.Point the open-source help desk at your own articles
The login screen on the live app lists two demo accounts: one customer, one agent. The seed gives the customer seven tickets, and under the policy above those are the only ones that account can list.
Then run it against your own content:
bash
git clone https://github.com/supero-platform/supero-apps
cd supero-apps/apps/customer-support/concierge
cp .env.example .env # set SUPERO_DOMAIN and SUPERO_PASSWORD
./run.shYou do not need a Supero account.
run.sh registers the domain name you chose in .env, creates the seed users on first run and opens the app at http://localhost:5662. Allow about two minutes. Everything in the concierge folder is MIT licensed. The platform it talks to is a hosted service and is not open source.Replace the
ARTICLES list in setup.py with your product's help content and the concierge answers from that. To start from an empty schema file instead, go to supero.dev.On this page