Skip to content
All posts

RAG with screenshots: showdo guides as AI knowledge

Use your showdo guides as the source for chatbots and AI assistants. Diagrams, steps and copy-ready prompts for a RAG that answers with real screenshots.

Pavan Kotapally8 min read

A chat answer to "How do I add a user?" with a screenshot under each step.

Most help chatbots answer "how do I...?" questions with a wall of text. People then go looking for the button the text describes. Your showdo guides already hold the better answer: every step has its words, its screenshot and the exact spot to click.

This post shows how to make your guides the source documents for a chatbot or AI assistant, so it answers from your real steps and shows the real screenshots. There is a no-code path that takes a few minutes and a developer path that builds a small RAG (retrieval-augmented generation) system. Every prompt below has a Copy button: paste it into Claude, Claude Code or another AI coding tool and it does the work.

Tip: Want it built for you? Jump to the one prompt that writes the whole thing: the sync, the search, the answers and a small chat page. Paste it into Claude Code in an empty folder and it does the rest.

Why guides make good AI knowledge

RAG works best when the documents are short, clear and about one thing each. A showdo guide is already shaped that way.

  • Already split into steps. Each step is one action on one screen. That is exactly the size of piece a search index wants.
  • Words and pictures that match. The step text says "Click Save changes" and the screenshot shows that button, with where it was clicked.
  • Private details already hidden. Screenshots are blurred before they leave the device, so the copies your assistant keeps are the blurred ones.
  • Easy to keep current. When a screen changes, record the task again. The next sync picks it up.
  • More than one language. Guides can be written or translated into 60 languages, including the 22 scheduled languages of India. Each translation is its own guide, so your assistant can answer from the Hindi or Tamil version too.

Pick your path

Markdown exportYour own RAG (API)Claude with a showdo tool (MCP)
Code neededNoneA small project, written for you by the prompt belowA small tool, written for you by the prompt below
Where answers show upAny chatbot that accepts Markdown filesYour app, help widget or internal toolClaude Desktop or Claude Code
Screenshots in answersLinks to them, when the chatbot shows Markdown imagesYes, inline under each stepClaude sees them and can describe them
Stays up to dateExport again after changesAutomatic, every syncAlways live

Start with the Markdown export if you want something working today. Build the RAG when you want answers with pictures inside your own product.

Path 1: Markdown export into any chatbot

Record in showdo, export Markdown with images, add it to your chatbot's knowledge, ask
The no-code path. One Markdown file per guide.
  1. Export the guides. In your library, select the guides you want (up to 50 at a time), click the download button and choose Markdown with images (.zip). You get one folder per guide, each with a guide.md and its screenshots. For a single guide, open it and choose Export, then Markdown with images.
  2. Add the files to your chatbot. Upload the guide.md files wherever your chatbot keeps its knowledge: a Claude Project's knowledge, your help desk bot's sources, or an internal assistant.
  3. Give it instructions. Paste the prompt below into the chatbot's instructions (in a Claude Project, that is Project instructions).

Each step in the file looks like this, with the screenshot right under its words:

md
3. Click **Save changes**

   Saves the new member and sends them an invitation.

   ![Step 3: Click Save changes](images/step-03.jpg)

When a guide is published and shared by link, the Markdown export can point its screenshots at copies showdo hosts instead of files in the zip. Chatbots that show Markdown images can then show the screenshot in their answer. Chatbots that only read text still answer from your exact steps.

The instructions prompt (paste it into the chatbot's instructions):

Prompt
You answer "how do I" questions for our team using ONLY the guides in your knowledge.
Each guide is a Markdown file made by showdo: a title, a short summary, then numbered
steps. A step can have a screenshot line like ![Step 3: ...](...).

How to answer:
1. Find the guide that matches the question. If two guides could fit, ask which one
   the person means, naming both titles.
2. Reply with the steps from that guide, in order, in short numbered lines. Keep the
   wording of buttons and menus exactly as the guide writes them (often in **bold**).
3. If a step has a screenshot line and you can show Markdown images, copy that line
   unchanged under the step. Never make up an image link or change one.
4. End with: "Source: <guide title>".
5. If no guide covers the question, say so in one sentence and suggest who to ask.
   Do not guess steps from general knowledge.
6. Answer in the language the person writes in. If a guide exists in that language,
   use it.

Tip: Re-export after you change or add guides, and replace the old files. A shared link that you reset in showdo stops old screenshot links from working, so export again after a reset too.

Path 2: your own RAG that answers with screenshots

This is the architecture. It has two halves: a sync that copies your guides in, and an answer flow that runs for every question.

Sync: showdo API to a sync job, which saves screenshots and stores one vector per step. Answer: question, search, expand to whole guides, model cites step ids, your app swaps ids for screenshots.
One vector per step for search, whole guides for answers, screenshots looked up by id.

The one idea that makes it work: the model never writes an image link. It names the steps it used, and your code swaps each name for the screenshot you stored. That way the picture under "Click Save changes" is always the real screenshot of that step, and a made-up picture cannot appear.

Step 1: Create a read-only access key

In showdo, open Settings, then Connected apps, and click Create access key. Give it a name like "Help bot", tick only View guides, and copy the key. It starts with sdo_ and is shown once.

Keep it on your server, as a secret, never in a web page. Every field and error code is in the API reference. The key can read everything its owner can read in the workspace, including guides meant only for your team (see Keep it safe).

Step 2: Sync your guides

Two read-only calls give you everything:

http
GET /api/v1/guides?limit=100&offset=0
Authorization: Bearer sdo_...

returns { guides, total }, most recently updated first. Ask again with offset=100, offset=200 and so on until you reach total. Then for each guide:

http
GET /api/v1/guides/<id>
Authorization: Bearer sdo_...

returns { guide, steps }. Steps come in order. Each one has an id, index, kind, title, description, the url and pageTitle of the screen, the element that was clicked, bounds (where it is, as fractions of the screenshot) and an imageUrl.

Download each screenshot during the sync. The imageUrl is a signed link that works for one hour, so save the file in your own storage (a folder, S3, R2) under the step's id. The blur is already applied. Skip guides whose updatedAt has not changed since the last sync, and delete guides that are no longer in the list.

Here is the core of a sync in TypeScript:

ts
const SHOWDO = process.env.SHOWDO_URL!; // the address you open showdo at
const headers = { Authorization: `Bearer ${process.env.SHOWDO_API_KEY}` };

async function* allGuides() {
  for (let offset = 0; ; offset += 100) {
    const res = await fetch(`${SHOWDO}/api/v1/guides?limit=100&offset=${offset}`, { headers });
    const { guides, total } = await res.json();
    yield* guides;
    if (!guides.length || offset + guides.length >= total) return;
  }
}

for await (const g of allGuides()) {
  if (alreadySynced(g.id, g.updatedAt)) continue;
  const { guide, steps } = await (await fetch(`${SHOWDO}/api/v1/guides/${g.id}`, { headers })).json();
  for (const step of steps) {
    if (step.imageUrl) {
      const img = await fetch(new URL(step.imageUrl, SHOWDO)); // the link can be relative
      await saveImage(step.id, Buffer.from(await img.arrayBuffer()));
    }
  }
  await indexGuide(guide, steps); // Step 3
}

alreadySynced, saveImage and indexGuide are yours to write (the full prompt below writes them for you).

Step 3: One chunk per step

Make each step its own chunk. Embed a short text that gives the step its context, and keep the details you need for the answer next to it.

A showdo step split into the text you embed, the details kept next to it, and the screenshot saved once
What one step turns into.

The text to embed, for each step:

text
Guide: <guide title>
Step <n> of <total>: <step title>
<step description>
Page: <pageTitle> (<element.role> "<element.label>")

Include the guide title in every chunk. "Click Save changes" alone matches hundreds of questions; "Add a user to your workspace, Step 3: Click Save changes" matches the right one. Notes (kind "note") are tips and warnings from the author: index their body too, without an image.

Any embedding model works. Store the vectors wherever you already keep data: Postgres with pgvector, SQLite, or a hosted vector database.

Step 4: Search steps, answer with whole guides

When a question comes in:

  1. Embed the question and find the 8 nearest steps.
  2. Group them by guide and keep the best 2 or 3 guides.
  3. Load every step of those guides, in order. A single matching step is rarely the whole answer: "Click Save changes" only makes sense after "Open Members".

Search finds the right place; the whole guide gives the model the full set of steps around it.

Step 5: Ask the model to cite step ids

Send the guides to the model with each step labelled by its id, and ask it to cite those ids. This is the system prompt (copy it as is):

Prompt
You are the help assistant for our product. Answer only from the GUIDES below.

GUIDES are written by showdo. Each guide has an id and a title, then its steps in
order. Each step starts with its id in this form: [[step:STEP_ID]].

Rules:
1. Answer with the steps the person needs, as a short numbered list, in the order the
   guide gives them. Keep button and menu names exactly as written.
2. After each step you used, add its id on its own line, exactly as given:
   [[step:STEP_ID]]. Only use ids that appear in GUIDES. Never invent or change an id.
   Never write an image link or a URL yourself.
3. End with one line: Source: [[guide:GUIDE_ID]] for each guide you used.
4. If the guides do not answer the question, say so in one sentence. Do not guess.
5. Answer in the language of the question.

GUIDES:
{{guides}}

And the {{guides}} block is built like this:

text
[[guide:gde_31xq]] Add a user to your workspace
[[step:stp_3m1a]] 1. Open Settings, then Members.
[[step:stp_5c9d]] 2. Click Invite people.
[[step:stp_8f2k]] 3. Click Save changes. Saves the new member and sends them an invitation.

Step 6: Swap the ids for screenshots

Left: the model's answer with step ids. Right: the same answer with each id replaced by that step's screenshot.
Your code, not the model, puts the pictures in.

Before you show the answer, replace each [[step:...]] with that step's screenshot, and each [[guide:...]] with a link to the guide. Drop any id that was not in the guides you sent:

ts
function render(answer: string, steps: Map<string, Step>, guides: Map<string, Guide>) {
  return answer
    .replace(/\[\[step:([\w-]+)\]\]/g, (_, id) => {
      const s = steps.get(id);
      return s?.image ? `![${s.title}](/images/${id}.png)` : "";
    })
    .replace(/\[\[guide:([\w-]+)\]\]/g, (_, id) => {
      const g = guides.get(id);
      return g ? `[${g.title}](${g.shareUrl ?? g.appUrl})` : "";
    });
}

steps and guides hold only what you sent to the model for this question. That is what makes an invented id harmless: it simply disappears.

Tip: Each step also has bounds, the box around what was clicked as fractions of the screenshot. Use it to crop the picture around the button, or to draw your own highlight.

Build the whole thing with one prompt

Paste this into Claude Code (or another AI coding tool) in an empty folder. It builds the sync, the index, the answer flow and a small chat page. Change the first line if you prefer Python or a different database.

Prompt83 lines
Build a small RAG service that answers "how do I" questions from our showdo guides
and shows each step's real screenshot in the answer.

Stack: TypeScript on Node 20+, no framework. SQLite (better-sqlite3) for guides, steps
and embeddings (store vectors as Float32 blobs, cosine similarity in code). Images in
./data/images. Keep it small and readable, with a README.

Settings (.env, add a .env.example, never log secrets):
- SHOWDO_URL: our showdo address, e.g. https://showdo.example.com
- SHOWDO_API_KEY: an access key starting with sdo_ (only "View guides" ticked)
- SYNC_VISIBILITY: comma list of guide visibilities to include, default "org,link,public".
  For a public-facing bot we set it to "link,public".
- EMBEDDINGS_URL, EMBEDDINGS_API_KEY, EMBEDDINGS_MODEL: any OpenAI-compatible
  /v1/embeddings endpoint (body {model, input}, response data[].embedding).
- ANTHROPIC_API_KEY and ANSWER_MODEL (default claude-sonnet-5-5) for answers, using
  the Anthropic Messages API.

showdo API (read-only, header "Authorization: Bearer <SHOWDO_API_KEY>"):
- GET {SHOWDO_URL}/api/v1/guides?limit=100&offset=N returns {guides, total}, most
  recently updated first. Page with offset until offset >= total. Each guide has id,
  title, summary, language, status, visibility, stepCount, updatedAt, appUrl, shareUrl.
- GET {SHOWDO_URL}/api/v1/guides/{id} returns {guide, steps}. Steps are in order. Each
  step: id, index, kind (click, type, key, navigate, select, scroll, screenshot, note),
  title, description, body (notes only), variant (notes: tip, alert, info, header),
  url, pageTitle, element {role, label}, bounds {x,y,w,h as 0..1 fractions}, imageUrl,
  imageWidth, imageHeight.
- imageUrl is a signed link valid for ONE HOUR and may be relative: resolve it with
  new URL(imageUrl, SHOWDO_URL) and download it during the sync. Save it as
  ./data/images/{stepId}.{ext from content-type}. The privacy blur is already applied.
- 401 means a bad or expired key: stop and say so. Retry 429 and 5xx with backoff.

1. `npm run sync`:
   - List all guides (paged), keep those whose visibility is in SYNC_VISIBILITY.
   - Skip a guide whose updatedAt matches what is stored. Otherwise fetch it, download
     its step images, replace its steps and their embeddings in one transaction.
   - Delete guides (and their images and vectors) that are no longer listed.
   - One chunk per step. Embed this text:
       Guide: {guide title}
       Step {n} of {total}: {step title}
       {step description, or note body}
       Page: {pageTitle} ({element.role} "{element.label}")
     Skip lines that would be empty. Header notes (variant "header") are section
     titles: prefix the following steps' text with them instead of indexing them alone.
   - Print a short summary: guides added, updated, removed, steps indexed.

2. `npm run ask -- "question"` and POST /ask {question} on `npm run serve` (port 8787):
   - Embed the question, take the 8 nearest steps, group by guide, keep the best 3
     guides, load all of their steps in order (at most 60 steps in total).
   - Build a GUIDES block: a line "[[guide:ID]] Title" per guide, then one line per step
     "[[step:ID]] n. title. description".
   - Call the model with this system prompt, with {{guides}} replaced:
     """
     You are the help assistant for our product. Answer only from the GUIDES below.
     GUIDES are written by showdo. Each guide has an id and a title, then its steps in
     order. Each step starts with its id in this form: [[step:STEP_ID]].
     Rules:
     1. Answer with the steps the person needs, as a short numbered list, in the order
        the guide gives them. Keep button and menu names exactly as written.
     2. After each step you used, add its id on its own line, exactly as given:
        [[step:STEP_ID]]. Only use ids that appear in GUIDES. Never invent or change an
        id. Never write an image link or a URL yourself.
     3. End with one line: Source: [[guide:GUIDE_ID]] for each guide you used.
     4. If the guides do not answer the question, say so in one sentence. Do not guess.
     5. Answer in the language of the question.
     GUIDES:
     {{guides}}
     """
   - Post-process: replace [[step:ID]] with a Markdown image
     ![step title](/images/ID.ext) and [[guide:ID]] with a link to its shareUrl (or
     appUrl). Remove any id that was not in the GUIDES you sent.
   - Return {answer (Markdown), sources: [{guideId, title, url, stepIds}]}.

3. `npm run serve` also serves:
   - GET /images/:file from ./data/images (only files that exist, no path tricks).
   - GET / : one plain HTML chat page that posts to /ask and renders the Markdown,
     with images at most 640px wide.

4. Tests (node:test) with a fake showdo API: paging, skipping unchanged guides,
   deleting removed guides, relative imageUrl, and that an invented step id in a model
   answer is removed.

When done, run the tests, then tell me the exact commands to run the first sync and
ask a question.

Bonus: let Claude search your guides directly

If your team already works in Claude Desktop or Claude Code, you can skip the vector database. showdo can search your guides itself (GET /api/v1/guides?q=...), so a small MCP server with two tools is enough. Claude calls them while it answers and sees the screenshots.

Prompt26 lines
Build an MCP server (TypeScript, @modelcontextprotocol/sdk, stdio transport) that
gives Claude read-only access to our showdo guides.

Settings from environment variables: SHOWDO_URL (our showdo address) and
SHOWDO_API_KEY (an sdo_ access key with only "View guides"). Send
"Authorization: Bearer <SHOWDO_API_KEY>" on every request. Never print the key.

Tools:
1. search_guides({ query: string, limit?: number = 5 })
   GET {SHOWDO_URL}/api/v1/guides?q=<query>&limit=<limit>
   Return a short text list: id, title, summary, stepCount, shareUrl or appUrl.
2. get_guide({ id: string, with_images?: boolean = true, max_images?: number = 8 })
   GET {SHOWDO_URL}/api/v1/guides/{id} returns {guide, steps}.
   Return one text block with the title, summary and every step in order
   ("n. title. description", notes as "Note: body"). If with_images, for the first
   max_images steps that have an imageUrl, fetch new URL(imageUrl, SHOWDO_URL) (a
   signed link valid for one hour) and add it as MCP image content right after that
   step's text, resized so the longest side is at most 1280px.

Tool descriptions should tell Claude: search first, then open the best guide, answer
with the guide's steps in order, keep button names exactly as written, and say which
guide the answer came from.

Add a README with the build command and how to add it to Claude Code:
claude mcp add showdo -e SHOWDO_URL=... -e SHOWDO_API_KEY=... -- node /path/to/dist/index.js
and to Claude Desktop (the mcpServers entry in its config file).

Keep it safe

  • Use a read-only key. Tick only View guides. Keep it on the server, never in a browser or a mobile app.
  • Choose what goes in. The key can read every published guide in the workspace, including ones open to your team only. For a bot that customers use, sync only guides with visibility "link" or "public" (the full prompt has a setting for it).
  • Keep the blurred copies. The screenshots the API gives you are already blurred. Do not replace them with screenshots from anywhere else.
  • Remove what showdo removes. Delete guides from your index when they disappear from the list, so a guide you trashed stops showing up in answers.
  • Keys that sit unused expire. An access key stops working after 180 days without use. If your sync starts getting 401, create a new key.

Questions

Do I need a vector database? Not always. For a few hundred guides, SQLite with vectors compared in code is plenty. The MCP path uses showdo's own search and needs none.

Which embedding model and which chat model? Any. The prompts above keep both as settings, so you can switch without changing code.

Can the assistant answer in Hindi or Tamil? Yes, if those guides exist. Translate a guide in showdo and the copy is synced like any other guide. The answer prompt tells the model to reply in the language of the question.

Does the model ever see private details? It sees the step text and, in the MCP path, the screenshots, both with private details hidden by showdo. Typed text that showdo hid comes back empty from the API.

All posts

Try showdo on your next task

Record it once. showdo makes the guide and a narrated video, blurs private details and translates both for your team.

Cookie settings

Pick what showdo can save on this device.

  • Needed to work

    Keeps you signed in and remembers your cookie choices.

    Always on
    See what's stored
    • Your cookie choicesCookie

      6 months

      Remembers what you chose here so we don't ask again.

    • Sign-inCookie

      30 days

      Keeps you signed in to showdo.

    • Sign-in for this visitCookie

      Until you close the browser

      Signs you out when you close the browser, if you didn't choose to stay signed in.

    • Sign-up age checkCookie

      1 hour

      Remembers that you ticked "I'm 18 or older" while you finish making an account, including with GitHub or Google.

    • Sign-in with GitHub or GoogleCookie

      5 minutes

      Keeps the sign-in with GitHub or Google safe while it finishes.

    • Tips you have seenSaved on this device

      Until you clear it

      Remembers which tours you finished, skipped or turned off, so they don't come back each time you open showdo.

  • Remember my settings

    Remembers things like your theme, where you left off in a guide and which tips you have seen.

    See what's stored
    • ThemeSaved on this device

      Until you clear it

      Remembers light, dark or your device setting.

    • Side menu sizeSaved on this device

      Until you clear it

      Remembers whether the side menu is open or small.

    • Recent guides listSaved on this device

      Until you clear it

      Remembers whether the recent guides in the side menu are shown or minimised.

    • Requests layoutSaved on this device

      Until you clear it

      Remembers if guide requests show as a list or a board.

    • Extra settings shownThis tab only

      Until you close the tab

      Keeps a section's extra options open while you move around Settings.

    • Home layoutSaved on this device

      Until you clear it

      Remembers if your recent guides show as cards or a list.

    • Guides layoutSaved on this device

      Until you clear it

      Remembers if your guides show as cards or a list.

    • Folders panelSaved on this device

      Until you clear it

      Remembers whether the folders panel is open.

    • Passkey sign-inSaved on this device

      Until you clear it

      Asks for your passkey first when you sign in on this browser, because you used one here before. Keeps a scrambled form of your address, not the address itself.

    • How you last recordedSaved on this device

      Until you clear it

      Starts New guide with Chrome or Mac, whichever you used last.

    • showdo for MacSaved on this device

      Until you clear it

      Remembers that this browser has talked to showdo for Mac.

    • Where you left offSaved on this device

      Until you clear it

      Reopens a guide at the last step you reached.

    • Intro video tucked awaySaved on this device

      Until you clear it

      Remembers that you tucked the home page intro video into the side, so it stays there next time.

    • Video export choicesSaved on this device

      Until you clear it

      Remembers your last size, quality and format when you export.

    • Team set-up progressThis tab only

      Until you close the tab

      Keeps what you typed if you refresh while setting up your team.

    • Workspace switch promptThis tab only

      Until you close the tab

      Asks about switching workspaces only once per visit.

  • Usage statistics

    Helps us see which parts of showdo get used so we can improve them.

    See what's stored
    • Usage statisticsSaved on this device

      Until you clear it

      A random number for this browser, so we can count which pages and buttons get used.

    • Usage statistics for this visitThis tab only

      Until you close the tab

      Groups the pages you open in this tab into one visit.

    • Usage statistics turned offSaved on this device

      A moment

      Stops usage statistics straight away when you turn them off. Removed at the same moment.

More in our Cookie Policy.