Skip to main content

Half your helpdesk tickets are "how do I reset my SSO password" and "how do I get a new laptop" — questions your IT docs already answer. You want a page that answers them before a ticket gets filed, without pulling an engineer off other work to build it.

Not yet tested end-to-end on a live Lovable account. The prompt below states the exact, corrected Chat API shape literally rather than describing it in prose, so an agent can't guess wrong — that part is verified. Whether Lovable actually produces a working app from it is not, since that's outside this cookbook's control.

A Lovable account
A Glean API token with the CHAT scope (kept as a Lovable backend secret)
1

Get your secrets ready before you start.

  • GLEAN_API_TOKEN — Admin Console → Platform → API Tokens → scope it to CHAT only.
  • GLEAN_INSTANCE — the <instance> part of your https://<instance>-be.glean.com URL.
2

Start a new Lovable project and paste this whole block — fill in your instance name first.

Build "Acme IT Deflection Page" — a single-page chat tool that answers
common IT helpdesk questions (SSO/password resets, laptop issues, VPN
setup) using the Glean Chat API, so employees get an answer before they
file a ticket. I don't want to write or review implementation code; you
own that. I do want to review the running app and its use of secrets.

The browser must never see a Glean API token. If a question calls a
Glean API from the client, the token is exposed to anyone who opens
devtools — that's not acceptable here. Route the Glean call through
whatever server-side mechanism you use for secrets and backend calls
(you may need to connect a backend/database integration to get one) —
ask me before you do, and ask me for the two values below when it's
ready. Don't skip this by calling Glean directly from React.

1. Build a React page: a text input, a submit button, an answer area,
and a "Sources" list below the answer. Frame it as "Ask before you
file a ticket."

2. Server-side, install `@gleanwork/api-client` (pin the version — do
not use a `^` or `latest` range) and construct the client like this:

```ts
import { Glean } from '@gleanwork/api-client';

const glean = new Glean({
apiToken: process.env.GLEAN_API_TOKEN,
instance: process.env.GLEAN_INSTANCE, // e.g. "<your-glean-instance>"
});
```

Both `GLEAN_API_TOKEN` and `GLEAN_INSTANCE` must be stored as
server-side secrets, never hardcoded and never bundled into the
frontend. Stop and ask me for these two values by name before running
the app — do not invent placeholder values and move on.

3. Call the Chat API like this — the response shape is specific, follow
it exactly rather than guessing at field names:

```ts
export async function askGlean(question: string) {
const response = await glean.client.chat.create({
messages: [{ author: 'USER', fragments: [{ text: question }] }],
});

const contentMessages = (response.messages ?? []).filter(
(m) => m.messageType === 'CONTENT',
);
const fragments = contentMessages.flatMap((m) => m.fragments ?? []);

const answer = fragments.map((f) => f.text ?? '').join('');

const citations = fragments
.map((f) => f.citation?.sourceDocument)
.filter(
(doc): doc is NonNullable<typeof doc> => !!doc?.title && !!doc?.url,
);
const uniqueCitations = Array.from(
new Map(citations.map((doc) => [doc.url, doc])).values(),
);

return { answer, citations: uniqueCitations };
}
```

Notes on the response shape, since guessing at field names here is
easy to get wrong:
- The response can include earlier step-narration messages
(search/read progress) before the real answer — filter to
`messageType === 'CONTENT'` or that narration text ends up
prepended to the answer.
- Citations live per-fragment, in `fragment.citation.sourceDocument`
— not a top-level `citedDocuments` field, and not the older
`message.citations[]` field (deprecated, and not populated at all
on a live agentic response). Dedupe by `url` since the same source
is commonly cited by more than one fragment.

4. Frontend: on submit, call your server-side function with the
question, render `answer` as text, and render each citation as a link
using its `title` and `url`. Show a loading state while waiting. Show
the raw error message if the request fails (this is an internal tool
— don't hide errors from me while I'm testing it).

5. Give the assistant a short system framing so it stays on-topic: it
should present itself as "Acme IT Help" and answer IT/helpdesk
questions using only what Glean returns — don't have it improvise
troubleshooting steps Glean didn't cite.

6. When you're done, tell me the two things I need to test:
- Ask "Where do I reset my SSO password?" and confirm the answer
cites the SSO reset guide.
- Ask "How do I request a new laptop?" and confirm it cites the IT
helpdesk FAQ (loaner laptops, same-day, from the IT desk).

Do not add authentication, a ticketing integration, or user accounts —
Glean already enforces per-user permissions on the backend token's
behalf for this demo, and this is a single-tenant internal tool.

Lovable's default stack leans on a connected backend/database integration for anything server-side, including secrets — that surface changes over time, so the prompt asks the agent to set one up rather than assuming a fixed mechanism. What doesn't change is the constraint: the token never reaches the browser.

3

Test with the two demo queries below, then check your browser's Network tab to confirm requests to *.glean.com originate from your backend, not the page itself.

Take it further
Scope the CHAT token even further if your Glean plan supports per-collection tokens, so this tool can only ever answer from IT content.
Compare this to no-code-pto-lookup-replit — same Chat API call, same "browser never holds the token" constraint, different no-code tool and persona.
Once your team outgrows the no-code version, acme-answers shows the same pattern as a hand-written, version-controlled app.
Scaffold starter code

Copies a prompt your AI assistant can build from.

At a glance
SurfacesClient API
StatusShowcase
Time~45 min
Required scopes
CHAT