An LLM agent that qualifies B2B leads from AmoCRM: notes from a production build
A B2B client came to us with 50 to 80 inbound leads a day and one first-touch sales manager. About six hours of that person's day went to the same questions: what are your prices, can you send the deck, do you work with
A B2B client came to us with 50 to 80 inbound leads a day and one first-touch sales manager. About six hours of that person's day went to the same questions: what are your prices, can you send the deck, do you work with our volume. Filtering was uneven, and some leads never got a reply at all.
We replaced that first touch with an LLM agent. Here is how it is put together and what we would keep if we built it again tomorrow.
What the agent does
Every morning it pulls new leads from AmoCRM through API v4. For each lead it:
- classifies the request type, for example a price question or a request for materials;
- opens a conversation in the channel the lead came from: Telegram, or WhatsApp through Wappi;
- qualifies the lead on six criteria, among them volume, budget, timing and fit with the ideal customer profile;
- moves the deal to the right pipeline stage and tags it.
An admin panel shows every dialog, and a manager can step into any conversation at any moment. That part matters more than it sounds. The sales team trusted the bot because they knew they could take the wheel whenever they wanted.
The stack is boring on purpose: TypeScript, Node.js with Express, Gemini 2.0 Flash as the model, Firestore for conversation state.
Function calling instead of free text
The model never writes to the CRM in prose. It can only call a short list of functions, and the backend validates every argument before anything touches AmoCRM. A simplified version of the qualification call looks like this:
const qualifyLead = {
name: "qualify_lead",
description: "Save qualification answers once the lead has given them",
parameters: {
type: "object",
properties: {
leadId: { type: "number" },
monthlyVolume: { type: "string", enum: ["small", "medium", "large", "unknown"] },
budgetConfirmed: { type: "boolean" },
timing: { type: "string", enum: ["now", "this_quarter", "later", "unknown"] },
icpFit: { type: "boolean" },
notes: { type: "string" }
},
required: ["leadId", "monthlyVolume", "budgetConfirmed", "timing", "icpFit"]
}
};
Enums do a lot of the work here. When the model has to pick unknown instead of making up a number, the CRM stays clean, and the sales team can filter on those fields without reading transcripts.
The rest of the toolbox follows the same idea: move a deal between stages, add a tag, send the deck or price list, hand the conversation to a human. Anything outside that list the agent simply cannot do. We wrote a longer piece on where to put approval gates when an agent has CRM or ERP access.
What changed after launch
- Qualifying a lead takes about 8 minutes instead of 40.
- Every inbound lead gets an answer, including the ones that arrive at night or on weekends.
- The share of leads that reach a sales conversation went up 31%. That came from consistency alone: the same questions, asked every time, with nothing forgotten.
The manager who used to do first touch now works only with leads that are already qualified.
If you are building something similar
- Start from the CRM fields, not the prompt. Decide what a qualified lead looks like as data, then make the model fill exactly those fields.
- Keep the tool list short. Every extra function is one more way for the agent to do damage.
- Give people a visible override. The admin panel was one of the cheapest parts of the project, and it is the reason the team accepted the bot.
- Measure business numbers. Time to first reply and the share of leads that reach a sales call tell you more than any accuracy score.
The full case with screenshots is here: CRM Bot case study. We build agents like this for small and mid-size companies in Poland and the EU, more on the AI agents page.
Originally published by Dev.to AI. Aggregated on AIWithGhost for educational purposes — full credit and traffic to the original publisher.