slack.messagespeech.transcribe()tools.route()erp.read_onlylog.append()lang: mk | en | el
← Back to projects
Case study

AI ERP Assistant for a Distributor

Flagship AI system · In production since 19 October 2026 · in daily internal use · Slack · by text or voice · Macedonian, English, Greek

5.4 s
median response (acceptance)
7.5 s
p90 response (acceptance)
50 / 50
questions answered
3
languages
n8nSlackClaude (Anthropic API)OpenAI speech APIShopifyERP connector
Jump to proof of work

Overview

A Slack assistant for a food distributor's team: ask about stock, price or expiry of a product, by text or voice, in Macedonian, English or Greek, and get the answer from the ERP in seconds. Built in a two-developer team on one n8n instance; my part is the assistant, its tools, its log, its prompts and its acceptance set.

In daily internal use since 19 October 2026: the team pulls live stock, price and expiry data from the ERP and compares products without opening it.

01 / Problem

The challenge

Five people share one ERP, and every stock or price question meant someone opening it, searching the product and reading the status back, usually while the person asking was on the road between customer visits.

The team wanted the answer where they already talk, in Slack, and by voice when typing is not an option. One colleague works in Greek, so the assistant had to understand three languages and answer in a way everyone can read.

02 / Role

What I did

I built the assistant end to end: the Slack flow, the tool router, the question log, the prompts, the acceptance set and the handover docs. The assistant reads the ERP through a separate connector workflow in the same project; ownership of each workflow is visible in git.

03 / Architecture

How it works

01
Slack message or voice cliptriggers the flow in one of two channels
02
Speech to textvoice goes to OpenAI's speech API with a three-language prompt (Macedonian, English, Greek), then through a repair step for spoken product codes ("zero one dot zero two zero" becomes 01.020)
03
Question log + thread memoryevery question is written to a log table and checked against the thread's history, so follow-ups like "show me a picture of it" keep the product from the previous question
04
Claude agent via the Anthropic APIanswers with six read-only tools through a tool router: product search with a transliterated fallback (пармиџано finds Parmigiano), stock, price, expiry, and cost behind a channel-based access gate
05
Language rule per usera Code node decides the answer language: Macedonian questions get Macedonian answers; one colleague asks in Greek or English and, at his own request, is always answered in English
06
Data boundariesonly the ERP connector talks to the ERP; product images come from the connected Shopify catalogue, because the ERP stores codes and quantities, not pictures
07
Operations channelslow or unverifiable answers raise a warning

Privacy: product data from the ERP passes through the Anthropic API; customer data does not (the customer-sales tool is a stub). Cost and margin figures are available only in a restricted channel.

04 / Result

Where it stands

Acceptance on staging, October 2026: 50 logged questions across the acceptance set and ad-hoc tests, all answered; median response 5.4 s, p90 7.5 s; 2 of 50 answers flagged for unverified figures; Macedonian, English and Greek; voice tested in production.

Acceptance results, October 2026. Source: the assistant's question log (50 rows, 10–12 October 2026) and the 35-question acceptance set.

In production since 19 October 2026, in daily internal use.

05 / Lessons

What I learned

Threads are memory. The first version answered "stock of 01.099" correctly and then failed on "show me a picture" in the same thread: no answer, then "I cannot show images". The fix was not a bigger model but keeping the thread's resolved product in the log and letting the next tool call use it, and realising the picture lives in Shopify, not in the ERP.

Pick the provider per job, not per loyalty. The reasoning model does not transcribe, so speech runs through OpenAI's API and the answer through Claude; the glue between them, the spoken-code repair and the language prompt, is where the real work was.

Scope by channel, not by prompt. The same assistant runs in two Slack channels. In the general one, "what do we pay for 03.000" gets a polite refusal; in a restricted channel the same question returns purchase price, taxes and margin from the ERP. The permission is a property of the channel, membership is managed in Slack by the owners, and the agent only reads a flag from its context. No prompt engineering decides who sees cost data.

Language is a user setting, not a guess. Instead of detecting the language of every message, a Code node maps the user to a rule: Macedonian in, Macedonian out; for the colleague who asks in Greek or English, always English out, because that is what he asked for. Fewer wrong-language answers than any detection I tried, and the rule is one line to change.

06 / Proof

Proof / Evidence

Every figure on this page comes from the assistant's own question log and the acceptance set, both kept in the project repository with the exported workflows. The client is named here only as a food distributor in North Macedonia; screenshots, a demo video and the client's name follow once written permission is in place.

Architecture diagram

Slack text or voiceSpeech API +spoken-code repairQuestion log + threadmemoryClaude agent · toolrouterERP connector(read-only)Channel access gate(cost)Shopify catalogue(images)Language rule → SlackanswerOperations channelwarnings
Architecture diagram of the ERP assistant: a Slack text or voice message is transcribed and repaired, logged with the thread's context, answered by a Claude agent through a read-only tool router over the ERP connector, a channel-based access gate for cost data and the Shopify catalogue for images, then returned in the language set for the user, with slow or unverifiable answers flagged in the operations channel.

How it is run

  • Pull before work, push after every publish; one commit per change with the exported workflow as the record.
  • A status file that says who owns what and what is live.
  • A log row for every execution, with a flag on every answer whose figures could not be verified against the source.
  • A 35-question acceptance set in three languages, run before cut-over.

Confidentiality & Publication Note

This case study describes a client system in anonymised form. The public material documents architecture, integration boundaries and the acceptance method. The client's name, ERP tool names, product and customer data, question texts, credentials and raw workflow exports are not published. Team members are not named.

← Back to all projects