Work  /  Customer Support  /  12

Company Knowledge Assistant That Cites Its Sources

Professional services · Agencies · Healthcare · Manufacturing · SaaS

Built — counted from the workflow JSON in the repository; not yet run against live accounts

95nodes
5workflows
22sticky notes
98connections
Binders and a folder beside a chat window whose answer marks the passage it quotes.

The problem

A confidently wrong answer about your own refund policy is more expensive than no answer at all.

The same questions get asked every week, and the answers live in SOPs, a shared drive, old tickets and one person’s head. A generic chatbot bolted onto that makes it worse.

How it runs

  1. Ingest

    01 / 07 Workflow 3 of 5 · Ingest & Index · 25 nodes

    Drive documents, PDFs and resolved tickets are chunked, embedded and stored in a Supabase database the client owns.

  2. Keep it current

    02 / 07 Workflow 1 of 5 · Change Detection & Re-Index · 13 nodes

    Every six hours Drive is checked for changed or deleted files, and only those are re-indexed.

  3. Ask

    03 / 07 Workflow 4 of 5 · Slack Answer Path · 22 nodes

    A question arrives in Slack, or from a widget on the client’s site.

  4. Retrieve what the asker may see

    04 / 07

    The permission filter runs inside the SQL search itself; an asker it does not recognise sees public documents only.

  5. Answer with a quote

    05 / 07

    The model answers, names the source document and quotes the passage it relied on.

  6. Check the quote

    06 / 07

    Code confirms the quote is a literal substring of a retrieved chunk. If it is not, the answer is refused.

    quote not found → refusal, and a person

  7. Log the gaps

    07 / 07 Workflow 2 of 5 · Gap-Log Digest · 9 nodes

    Unanswered questions become a weekly digest — in practice, the list of SOPs to write next.

The five workflows, as built

Every node with its icon, as on the n8n canvasstarts a runa model or a parser, wired into the node above itHover a node for its name and type

  • 01Change Detection & Re-Index

    13 nodes · 13 connections · 4 notes

    Every 6 Hours · Schedule TriggerEvery 6 HoursGet Last Checked Time · PostgresGet Last CheckedTimeBuild Drive Query · CodeBuild Drive QueryList Changed Or Trashed Files · Google DriveList Changed OrTrashed FilesAdvance Checkpoint · PostgresAdvanceCheckpointCount Changes · CodeCount ChangesAny Changes? · IfAny Changes?No Changes This Cycle · No OperationNo Changes ThisCycleExpand Files To Items · CodeExpand Files ToItemsBatch Files (10 at a time) · Loop Over ItemsBatch Files (10at a time)Map To Targeted Payload · CodeMap To TargetedPayloadCall: Ingest & Index (Targeted) · Execute WorkflowCall: Ingest &Index (Targeted)All Batches Dispatched · No OperationAll BatchesDispatched

    every six hours, only what changed

  • 02Gap-Log Digest

    9 nodes · 8 connections · 3 notes

    Manual: Run Digest Now · Manual TriggerManual: RunDigest NowWeekly: Monday 8am · Schedule TriggerWeekly: Monday8amFetch Unanswered Questions (7 days) · PostgresFetch UnansweredQuestions (7…Count Gaps · CodeCount GapsAny Gaps This Week? · IfAny Gaps ThisWeek?No Gaps — Skip Digest · No OperationNo Gaps — SkipDigestFormat Gap Digest (Markdown) · CodeFormat Gap Digest(Markdown)Slack: Post Gap Digest · SlackSlack: Post GapDigestMark Gaps As Reported · PostgresMark Gaps AsReported

    the weekly list of what it could not answer

  • 03Ingest & Index

    25 nodes · 27 connections · 5 notes

    Manual: Full Backfill · Manual TriggerManual: FullBackfillOn Change: Targeted Re-index · When Executed by Another WorkflowOn Change:Targeted…List Drive Folder (Full) · Google DriveList DriveFolder (Full)List Ticket Sources (Full) · PostgresList TicketSources (Full)Tag: Targeted Item · CodeTag: TargetedItemTag: Drive Items (Full) · CodeTag: Drive Items(Full)Tag: Ticket Items (Full) · CodeTag: TicketItems (Full)Is Delete Action? · IfIs DeleteAction?Delete Document + Chunks (Cascade) · PostgresDelete Document+ Chunks…Deleted — Logged · No OperationDeleted — LoggedRoute By Content Type · SwitchRoute By ContentTypeExport Google Doc Text · Google DriveExport GoogleDoc TextDownload PDF Binary · Google DriveDownload PDFBinaryExtract PDF Text · Extract from FileExtract PDF TextNormalize Document Record · CodeNormalizeDocument RecordGet Existing Hash · PostgresGet ExistingHashContent Changed Or New? · IfContent ChangedOr New?Unchanged — Skip Re-embed · No OperationUnchanged — SkipRe-embedUpsert Document Row · PostgresUpsert DocumentRowDelete Stale Chunks · PostgresDelete StaleChunksEmbed & Store Chunks · Supabase Vector StoreEmbed & StoreChunksLoad & Split Document · Default Data LoaderLoad & SplitDocumentRecursive Splitter (1000/200) · Recursive Character Text SplitterRecursiveSplitter…Embeddings (text-embedding-3-small) · Embeddings OpenAIEmbeddings(text-embedding-3-small)Mark Indexed · PostgresMark Indexed

    Drive, PDFs and tickets: chunk, embed, store

  • 04Slack Answer Path

    22 nodes · 22 connections · 5 notes

    Slack: New Question · Slack TriggerSlack: NewQuestionIgnore Bot Messages · CodeIgnore BotMessagesNormalize Question · CodeNormalizeQuestionLook Up Asker Permission Groups · PostgresLook Up AskerPermission…Resolve Permission Filter · CodeResolvePermission…Embed Question (OpenAI) · HTTP RequestEmbedQuestion…Format Embedding For pgvector · CodeFormatEmbedding For…Retrieve Matching Chunks · PostgresRetrieveMatching…Count Chunks · CodeCount ChunksAny Chunks Retrieved? · IfAny ChunksRetrieved?Build No-Match Refusal · CodeBuildNo-Match…Build Context Block · CodeGenerate Cited Answer · Basic LLM ChainGPT-4.1-mini (Answering) · OpenAI Chat ModelGPT-4.1-mini(Answering)Answer Schema · Structured Output ParserAnswer SchemaValidate Quote Is Verbatim · CodeValidator Passed? · IfValidatorPassed?Log Answered (Audit Trail) · PostgresSlack: Post Verified Answer · SlackSlack: PostVerified…Build Validator-Failed Refusal · CodeBuildValidator-Failed…Log Refusal + Route to Person · PostgresLog Refusal +Route to…Slack: Post Refusal + Ping Human · SlackSlack: PostRefusal +…

    a question in, a cited answer or a refusal out

  • 05Web Widget Answer Path

    26 nodes · 28 connections · 5 notes

    Webhook: Ask Endpoint · WebhookWebhook: AskEndpointValidate Request Shape · CodeValidateRequest ShapeWell-Formed Request? · IfWell-FormedRequest?400: Bad Request · Respond to Webhook400: BadRequestLook Up Widget Key Permission Groups · PostgresLook UpWidget Key…Resolve Permission Filter · CodeResolvePermission…Embed Question (OpenAI) · HTTP RequestEmbedQuestion…Format Embedding For pgvector · CodeFormatEmbedding For…Retrieve Matching Chunks · PostgresRetrieveMatching…Count Chunks · CodeCount ChunksAny Chunks Retrieved? · IfAny ChunksRetrieved?Build No-Match Refusal · CodeBuildNo-Match…Build Context Block · CodeGenerate Cited Answer · Basic LLM ChainGPT-4.1-mini (Answering) · OpenAI Chat ModelGPT-4.1-mini(Answering)Answer Schema · Structured Output ParserAnswer SchemaValidate Quote Is Verbatim · CodeValidator Passed? · IfValidatorPassed?Format Success Response · CodeFormatSuccess…200: Answer + Citation · Respond to WebhookLog Answered (Audit Trail) · PostgresLog Answered(Audit Trail)Build Validator-Failed Refusal · CodeBuildValidator-Failed…Format Refusal Response · Code200: Refused + Escalated · Respond to Webhook200: Refused+ EscalatedLog Refusal · PostgresSlack: Notify Ops of Unanswered Question · SlackSlack: NotifyOps of…

    the same, for a site widget

95 executing nodes, by type

Code 29Postgres 18If 9No Operation 5Google Drive 4Slack 4Respond to Webhook 3Schedule Trigger 2Other 21

The gates, 10 If and Switch nodes, decide whether anything happens next: a countable part of the system, not a claim about it. The 22 sticky notes carry the reasoning on the canvas, at the point it applies.

The guards, and what each one prevents

HoverTap a card to read what it prevents.

  • No verbatim quote, no answer

    Case, whitespace and curly quotes are normalised, then the quote must appear in what was retrieved. A failed check routes to a refusal, never to a softened answer.

  • Default-deny, inside the search

    Permissions are applied inside the similarity search, not checked on the answer afterwards, so a restricted document cannot leak through a prompt.

  • Unchanged documents are never re-embedded

    A content hash is compared first, so re-reading a folder that has not changed costs nothing.

  • Old text goes before new text arrives

    A changed document’s old chunks are deleted before its new ones are written, or it would answer with both versions and nobody could tell which.

  • Deleted at the source, deleted here

    A file trashed in Drive loses its record and chunks, so nothing cites a file that no longer exists.

  • The assistant ignores itself

    Messages from bots are dropped first — the classic loop of a Slack bot replying to its own answer, forever.

Limits, stated plainly

  • Built, not yet deployed. The workflows pass the importability checks; they have not run end to end against live Supabase, OpenAI and Slack accounts.
  • WhatsApp is not built. Slack and a site-widget webhook are; WhatsApp would be a new trigger into the same retrieval and quote check.
  • Tickets are read from a table a separate export has to fill; there is no Zendesk, Freshdesk or Intercom connector here.
  • The model’s confidence is logged, not gated: a low-confidence answer that passes the quote check still posts.

Built with

  • n8n
  • Supabase
  • PostgreSQL
  • OpenAI
  • Slack
  • Google Drive