FlowKit

RAG on Confluence with n8n: Build a Chatbot That Queries Your Knowledge Base

Published 9 August 2026 · 7 min read

Confluence is often the one place where a company records its decisions, its internal procedures, and the history of its technical choices — and it is also one of the tools where information becomes hardest to find once a space grows past a hundred pages. Native search matches keywords: it finds a page if you guess the right term, not if you ask a real question like "what is our API key rotation procedure?" RAG (retrieval-augmented generation) closes exactly that gap by retrieving the relevant passages from a corpus and handing them to a language model at answer time — the approach was formalized by Lewis et al. in a paper presented at NeurIPS 2020, "Retrieval-Augmented Generation for Knowledge-Intensive NLP Tasks", which shows that grounding answers in retrieved documents improves factuality and enables citing a verifiable source. Here is how to build that pipeline with n8n, from Confluence page extraction to incremental synchronization.

Pipeline architecture

  1. Extraction: the Confluence REST API lists the pages in one or more spaces and fetches their content.
  2. Conversion: each page's storage format (XHTML with macros) is turned into usable text.
  3. Chunking: the text is split following the page's section headings.
  4. Vectorization: each chunk becomes an embedding, inserted into Supabase pgvector with its metadata (space, title, URL).
  5. Synchronization: a scheduled workflow queries modified pages via a CQL query and reindexes only those.

If you have already read our guides on RAG over Notion or RAG over SharePoint, the skeleton is identical — only the source API and its specific traps change: here, the lack of a native node, the macro-based storage format, and CQL search.

Step 1: authentication and extraction via the Confluence API

n8n has no official Confluence node. Community nodes exist (n8n-nodes-confluence-cloud, @bitovi/n8n-nodes-confluence), but for an indexing pipeline that needs to handle pagination, CQL filters, and the choice of page body format, the HTTP Request node calling the Confluence Cloud REST API (v2) directly gives finer control and avoids an external dependency.

First create a Confluence service account, restricted to read-only access on the spaces you plan to index, then generate an API token at id.atlassian.com/manage-profile/security/api-tokens. That token, combined with the account's email address, is used as Basic Auth — an HTTP Basic Auth credential in n8n is enough. Our guide on securing API credentials in n8n covers restricting and rotating this kind of token.

Two calls are enough to walk through a space:

GET https://{your-domain}.atlassian.net/wiki/api/v2/spaces?keys={SPACE_KEY}
→ returns the space's numeric id

GET https://{your-domain}.atlassian.net/wiki/api/v2/spaces/{id}/pages?body-format=storage&limit=250
→ lists the space's pages, body included

By default, the v2 API does not return page bodies: the body-format=storage parameter must be explicitly requested, otherwise you only get titles. Pagination works by cursor: as long as the response contains a _links.next field, another run of the HTTP Request node (inside a Loop Over Items) fetches the next page.

Step 2: convert the storage format into usable text

Confluence's storage format is XHTML enriched with proprietary macros: a code snippet macro is written <ac:structured-macro ac:name="code">, an info panel <ac:structured-macro ac:name="info">, a collapsible section <ac:structured-macro ac:name="expand">. A Code node handles the conversion into structured text:

const html = $json.body.storage.value;
const text = html
  .replace(/<ac:structured-macro ac:name="(code|info|warning|note|expand)"[^>]*>[\s\S]*?<ac:rich-text-body>([\s\S]*?)<\/ac:rich-text-body>[\s\S]*?<\/ac:structured-macro>/g, '\n$2\n')
  .replace(/<h([1-6])[^>]*>(.*?)<\/h\1>/g, (_, lvl, t) => `\n${'#'.repeat(+lvl)} ${t}\n`)
  .replace(/<\/?(p|li)[^>]*>/g, '\n')
  .replace(/<[^>]+>/g, '')
  .replace(/&amp;/g, '&').replace(/&lt;/g, '<').replace(/&gt;/g, '>')
  .replace(/\n{3,}/g, '\n\n')
  .trim();
return [{ json: { markdown: text } }];

This simplified version extracts the useful content of the most common macros instead of losing it, and preserves heading levels — essential for the next step. If your space is heavy on tables or nested macros, extend the rules case by case: a slightly noisy chunk beats an entire paragraph silently vanishing inside an unrecognized macro.

Step 3: chunking guided by the page's headings

Like a Notion workspace, a well-written Confluence page already carries its own semantic structure in its headings (h1 through h3). Split primarily on those boundaries, and keep an expand section's content attached to the heading it appears under rather than isolating it in its own chunk. The general rules — 500 to 1,000 token chunks, overlap, paragraph-level fallback splitting when a section exceeds the target size — are covered in our guide to document chunking for RAG.

Step 4: embeddings, pgvector and metadata

The Supabase Vector Store node (insert mode), wired to an embeddings model, vectorizes and inserts each chunk — the full setup of the documents table and similarity function is described in our RAG with n8n and Supabase guide, and the model choice in our embeddings model comparison. If the Supabase authentication itself is not set up yet, our guide on connecting n8n to Supabase covers the credential configuration.

The metadata worth keeping for each chunk:

{
  "confluence_page_id": "{{ $json.id }}",
  "space_key": "{{ $json.spaceId }}",
  "title": "{{ $json.title }}",
  "url": "{{ $json._links.base }}{{ $json._links.webui }}",
  "version": "{{ $json.version.number }}",
  "last_modified": "{{ $json.version.createdAt }}"
}

The url, rebuilt from _links.base and _links.webui, lets the chatbot cite the source Confluence page with a clickable link; confluence_page_id enables targeted deletion of old chunks at resync time; version.number serves as a lightweight audit trail across revisions.

Step 5: incremental sync with CQL

Reindexing an entire space on every run wastes API calls and embeddings, especially on a space with several hundred pages. The right approach: query only the pages modified since the last sync, via CQL (Confluence Query Language) search — a feature that still lives on the v1 search API, GET /wiki/rest/api/content/search?cql=..., even though the rest of the pipeline uses v2:

cql = space = "{SPACE_KEY}" AND type = "page" AND lastmodified >= "{{ $json.lastSyncedAt }}"

A Schedule Trigger runs this query at a regular interval (hourly or nightly, depending on how often the space changes), and for every page returned applies the delete then insert sequence: remove all vectors carrying its confluence_page_id, then insert the freshly generated chunks. Skip that sequence and stale versions pile up, with the chatbot eventually citing a procedure that is no longer in force — the full pattern, including page deletions, is covered in our guide on keeping a RAG index up to date.

The cross-cutting trap: the points-based quota

The Confluence Cloud API does not enforce a fixed requests-per-second limit but a points-based quota: every call consumes a variable cost depending on the volume of data returned, a page with a large body costing more than a simple title listing. Beyond the quota, the API returns 429 with a Retry-After header to respect before retrying. In practice: enable retry on fail with a delay on the HTTP Request nodes, process pages sequentially rather than in parallel during initial indexing, and reserve body-format=storage for calls that actually need it.

Querying: agent, citations, and what comes next

On the query side, this is a standard RAG: an AI Agent with the Vector Store as a tool, and a prompt that requires citing the source page(s) via the URL stored in metadata. If your users often search for exact terms — project names, internal IDs, company-specific acronyms — add hybrid search combining vectors and keywords, the refinement with the best effort-to-impact ratio for this kind of corpus. Alavi and Leidner already put it this way back in 2001, in their foundational paper on knowledge management systems, "Knowledge Management and Knowledge Management Systems: Conceptual Foundations and Research Issues" (MIS Quarterly, 2001): the value of a knowledge system is not in the volume stored, but in how easily that knowledge can be retrieved and reapplied — exactly what a well-built RAG adds to an already-full Confluence space. The RAG Assistant Pack (€119) bundles the downstream half ready to plug in — a chatbot with citations, a question-answering API, a Supabase vector store — for you to graft this article's Confluence sync onto.

Key takeaways

  • Extraction: no native Confluence node — the HTTP Request node against the v2 API (spaces/{id}/pages?body-format=storage) gives the best control, with _links.next cursor pagination. Without that explicit parameter, the API returns only titles, with no error to flag it.
  • Conversion: the storage format is XHTML with macros (ac:structured-macro) — a Code node needs to unwrap them rather than ignore them, while preserving heading levels. An unrecognized macro otherwise makes an entire paragraph silently disappear.
  • Chunking: follow the page's heading structure rather than costly semantic splitting.
  • Metadata: confluence_page_id, url (rebuilt from _links.webui), version.number — for citations and clean resynchronization.
  • Incremental sync: lastmodified filter in CQL (v1 search API), delete-then-insert sequence, run from a dedicated read-only service account rather than a personal token.
  • Quota: a points-based cost tied to the volume of data returned, not a fixed requests-per-second ceiling; respect the Retry-After header.

FAQ

Frequently asked questions

Does n8n have a native Confluence node?

No. Community nodes exist, such as n8n-nodes-confluence-cloud or @bitovi/n8n-nodes-confluence, but for a complete RAG pipeline (pagination, CQL filters, choice of page body format), the HTTP Request node calling the Confluence Cloud REST API directly gives more control and avoids a dependency on a community-maintained third-party package.

What kind of credential should I use to read Confluence from n8n?

An Atlassian API token generated on a dedicated service account (id.atlassian.com/manage-profile/security/api-tokens), combined with that account's email address as Basic authentication. Restrict this account to read-only access on the spaces you index, rather than using a personal administrator account's token.

How do I handle Confluence macros (code, info, expand) when converting to text?

The storage format returned by the API is XHTML with ac:structured-macro tags for each macro. A Code node needs to recognize the common macros (code, info, warning, expand) and extract their useful text rather than ignoring them or letting their raw markup pollute the chunks.

How can the chatbot cite the source Confluence page in its answers?

By storing the page URL (rebuilt from the API's _links.webui field) in each chunk's metadata at indexing time. The agent's prompt then requires citing that URL in every answer, as a clickable link back to the original Confluence page.

Bundle FlowKit Complet

€269