English · Español
A B2B ordering agent that lets clients of a specialty yerba mate roaster browse the catalog, register, place orders and check delivery status — entirely by chatting on Telegram, in natural language. Orchestrated in n8n, reasoning with Claude (tool-calling), backed by Salesforce (CRM + Apex) and MongoDB (event broker + history).
Portfolio note: the brand Yerbatera del Litoral and all data are fictional. The integration (Telegram · n8n · Claude · Salesforce · MongoDB) is real. Secrets live only in n8n credentials — never in this repo.
B2B orders arrive by chat, scattered and at every hour: a client types “hola”, then “mandame 2 tradicionales”, then “y 1 barbacuá” — three messages, one intent. The sales team has working hours; the client doesn't. By the time someone replies, the client has already bought from whoever answered first. And every order still has to be typed into the CRM by hand.
Yerbi answers 24/7, understands natural language (typos and all), quotes from the live catalog, confirms, and writes the order straight into Salesforce — in seconds.
- 🖥️ Case study: https://valentino-millimaci.vercel.app/en/projects/yerbi
- 🧉 Live catalog — reads active products, prices and types from Salesforce in real time.
- 🛒 Conversational ordering — builds the order from plain language, shows a summary, waits for explicit confirmation before creating anything.
- 🪪 Auto-registration — unknown clients are onboarded into Salesforce via a multi-step flow.
- 📦 Order history & status — looks up past orders and their delivery stage.
- 🌿 Domain expert — answers questions about yerba types, brewing temperature, storage.
- 🔒 Business rules server-side — never invents data, never modifies confirmed orders, never exposes internal IDs.
Event-driven, single workflow (publisher → broker → consumer):
- Publisher (intake) — each Telegram fragment is stored in MongoDB (
buffers) and fires an HTTP event to the consumer. It does not reply. - Broker — MongoDB Atlas:
buffers(ephemeral, for debounce) +conversations(durable history). - Consumer (debounce) — a webhook receives the event, acks instantly, waits a 5s silence window, and only proceeds if no newer fragment arrived. It merges the burst, runs the agent, and sends one reply. The buffer is deleted on process (idempotent).
- Agent — Claude reasons over the merged message + client context and calls tools; the data (catalog, prices, orders) always comes from Salesforce, never from the model.
- Resilience — 2 retries + an error branch: the client always gets an answer.
flowchart TD
U(["Telegram client"]) -->|fragments| TT["Telegram Trigger"]
subgraph PUB["Intake (Publisher · push)"]
TT --> EX["Extract Message Fields"]
EX --> MF1["MongoDB — Find Buffer"]
MF1 --> AF["Append Fragment (messages + lastAt)"]
AF --> MU["MongoDB — Upsert Buffer"]
MU --> EMIT["Emit Event → Consumer (HTTP)"]
end
MU -. writes .-> MONGO[("MongoDB Atlas<br/>buffers / conversations")]
EMIT -. event .-> WH["Webhook EVENT_IN"]
subgraph CON["Consumer (push · deadline)"]
WH --> WAIT["Wait 5s (deadline)"]
WAIT --> MF2["MongoDB — Find Buffer"]
MF2 --> GATE{"silence ≥ 5s?"}
GATE -->|no| ABORT(["abort"])
GATE -->|yes| MDEL["merge + Delete Buffer"]
MDEL --> SF1["Salesforce — Identify Client"]
SF1 --> CF{"Client found?"}
CF -->|no| REG["Multi-step registration"]
CF -->|yes| CTX["Set Client Context"]
CTX --> PROMPT["Prepare Agent Prompt"]
PROMPT --> AGENT["AI Agent · Claude"]
AGENT -->|ok| SEND["Send Telegram Response"]
AGENT -->|error| FB["Fallback — Notify User"]
SEND --> MLOG["MongoDB — Log Conversation"]
end
MONGO -. reads .-> MF2
AGENT <-->|tools| TOOLS{{"search_catalog · get_orders · create_order"}}
TOOLS <--> SFAPI[("Salesforce REST + Apex")]
SEND -->|1 reply| U
The full diagram is in flujo.mermaid; component view in docs/architecture.md.
| Service | Role | Cost |
|---|---|---|
| Telegram Bot API | Receive & send messages (webhook) | Free |
| Anthropic · Claude | Agent reasoning (tool-calling) | Paid (cents/message) |
| Salesforce REST API | SOQL: identify client, catalog, orders | Free (Dev org) |
| Salesforce Apex REST | POST /apexrest/pedidos — atomic order creation |
Free (Dev org) |
| MongoDB Atlas | Debounce broker + conversation history | Free (M0) |
| n8n | Orchestration of the whole flow | Self-host / Cloud |
The Salesforce API contract lives in openapi.yaml.
You'll need a Telegram bot, a Salesforce Dev org, an Anthropic key and a MongoDB Atlas M0 cluster.
Full step-by-step setup for each service is in README.es.md
(section 3) and the workflow import guide in n8n/workflows/README.md.
Short version:
- Import
n8n/workflows/yerbi-agent.jsoninto n8n. - Assign credentials (Telegram, Salesforce OAuth2, Anthropic, MongoDB) and replace
YOUR_INSTANCEwith your Salesforce instance in the HTTP nodes. - Deploy the Apex class in
salesforce/apexand load the catalog seed fromsalesforce/data. - Activate the workflow and message your bot:
hola→ catalog → an order →confirmo.
Demo variant: the tools and client lookup can be swapped for mock Code nodes so the agent runs standalone (no Salesforce/Mongo) — handy for recording. This repo ships the real Salesforce + Apex + MongoDB integration.
├─ n8n/workflows/ # yerbi-agent.json — the full workflow + import guide
├─ salesforce/ # Apex REST controller + tests, object metadata, catalog seed
├─ docs/ # architecture
├─ specs/ # spec-driven design (requirements, plan, tasks, contracts)
├─ openapi.yaml # Salesforce API contract
├─ flujo.mermaid # full flow diagram
└─ SECURITY.md # how secrets are handled
No secrets in this repository. Every credential (Anthropic, Telegram, Salesforce OAuth, MongoDB)
lives only in n8n. Infrastructure values are placeholders (YOUR_INSTANCE.my.salesforce.com,
REPLACE_WITH_YOUR_WEBHOOK_ID). See SECURITY.md.
Built by Valentino Millimaci.