Assistants that reply on the channels where your clients write, from your information, and that hand the conversation over to a human when they go beyond their role.
We implement text assistants on our platform, aichat.md, which runs in production. The work means connected channels with proprietary code, an indexed knowledge base, tools that execute in your systems and a handover route to a human recorded in the database.
Already builtThe service relies on a proprietary platform in production, not on a promise. `https://aichat.md` responds publicly (307 to `/ro`, checked 06.09.2026), and the behaviours described below are read from the database schema and the platform routes, with file and line: search thresholds, tool types and their maximum time, conversation thread states, the pickup log. The second proprietary implementation cited is the shop catalogue function (`storeCatalog.service.js` plus three built-in tools), written as a platform function instead of per-client tools. What the evidence does not support we state just as explicitly: the WhatsApp template messages do not exist in the code, so we do not promise proactive messages outside the 24-hour window.
A useful assistant is not one that writes well, but one that is connected to the real information of the business and knows where to stop. Our work starts from the channel where your clients write and ends with the person who takes over the conversation when the assistant has nothing left to say. Between them we put the knowledge, the tools and the limits.
The platform we build on is aichat.md, made by us and in production. That changes the nature of the service: we are not integrating someone else’s black box, but configuring a system in which we can read any threshold and change any behaviour. When we say that the reply uses, by default, five fragments from your documents, with a similarity threshold of 0.70 and 30% weighting on word match, those are values from the database schema, not a brochure description.
The channels with their own code, checked on 06.09.2026, are six: Facebook Messenger, Instagram Direct, Telegram, 999.md, the website widget and WhatsApp Business through Cloud API on Graph v22.0. Each has its own module, not a checkbox — and the differences between them matter in practice: Messenger can show a product carousel, Instagram cannot, because Meta does not offer generic templates on Instagram.
The part clients underestimate is the handover to a human. In aichat it is not a promise, but a state in the database: the thread is `bot` or `human`, the handover log distinguishes handover by a human, return to bot and automatic reactivation, and the thread has a separate state for response-time compliance — `ok`, `warning`, `breached`. It can be shown, at the end of the month, how many conversations were taken over and in how much time.
What it covers
The work, by component
We connect the channel and check what happens when it falls silent
Six channels have their own code in the platform: Messenger, Instagram Direct, Telegram, 999.md, the web widget and WhatsApp Business Cloud API on Graph v22.0. Meta connection is done through Embedded Signup. For each channel we also check the bad case, not just the first successful message: an expired page token stops the channel silently, not with a visible error, which is why the platform has token monitoring and expiry notifications.
We index the knowledge and fix the thresholds by which it searches
Documents and pages are split into fragments with content hash and `text-embedding-3-small` embeddings. The default values we start with and then adjust: five fragments per question, similarity threshold 0.70, hybrid search, 0.30 weighting on word match, source attribution enabled. Each query can be logged with the returned fragments, scores and timings — so “why it answered like that” has an answer with data, not with assumptions.
We connect the tools to the systems that execute
A tool can be a webhook to your system, with method and headers of choice, a piece of JavaScript code run by the platform, or a tool built into the platform. Each has a maximum execution time, by default 15 seconds. The practical consequence, said before the contract, not after: an integration that responds in 40 seconds must be done asynchronously, not forced into the conversation.
Live catalogue, connected to the store address
For stores, there are three built-in tools — `store_catalog_search`, `store_catalog_match`, `store_catalog_product` — on top of a common catalogue service. For WooCommerce, the Store API is public, so the store connects with the site address, without access keys. Before, the catalogue was written as a separate tool for each client; it became a platform feature precisely because the version with tools written under the client’s name could not be maintained.
Handover to a human, with tracking
The thread has explicit `bot` or `human` state. The handover log knows three actions: handover by a human, return to bot and automatic reactivation. Separately, the thread has a response-time compliance state: `ok`, `warning`, `breached`. The handover module in the platform has a queue and roles. We decide from the first discussion who takes over and within how long, because this state is measured from the first thread, not added later.
Voice in written conversation
A voice note received on WhatsApp or Telegram is transcribed with `whisper-1` and enters the conversation as text. The reply can go back as voice, via `tts-1` or via ElevenLabs, with stability and similarity settings per voice. For those who dictate instead of typing — a tradesperson in the field, a driver — that is the difference between a usable channel and a decorative one.
Translation in conversation, one-way today
The operator’s reply can be rewritten in the client’s language, with the agent’s tone and without adding facts. The reverse direction — the client’s message translated for the operator — does not exist in the code at the time of checking, so the operator reads the original. We mention it because it is the difference between a team that can serve a Russian or English client and one that thinks it can.
Behaviour for duplicate messages and bursts
The Meta webhook does resend the same message: deduplication is done by message identifier, with 120 seconds of memory. And when someone writes three lines one after another, they are gathered into a 15-second buffer and receive a single reply, to everything the person wrote, not three partial replies.
The model is not tied to a provider
Calls go through a proprietary gateway at `api.megapromoting.com/v1`, which speaks OpenAI format for all back-end providers. The model is chosen per assistant and can be changed without rewriting the integration. For embeddings, we use `text-embedding-3-small`.
What it looks like
The path, step by step.
01
We choose the channel and the person who takes over
One channel, one audience, one tracked result. In the same discussion, it is established who takes over threads marked `human` and within how long. We deliver: the list of scenarios the assistant covers and the explicit list of those it does not cover.
02
We connect the sources and check what comes out of them
Documents and pages come in as knowledge sources. For catalogues, we check the extracted products one by one where price matters. We deliver: the indexed sources, the search thresholds set for your case and a set of control questions with the answers obtained.
03
We connect the tools and test the slow case
Webhook to CRM or ERP, code for custom transformations, built-in tool for what already exists. We test the slow case and the error case, not only the happy one, because the 15-second limit is what decides. We deliver: the configured tools and the documented behaviour when the limit is exceeded.
04
We open the channel with monitoring turned on
The channel goes live with token monitoring active and with human handover practised at least once on a real case. We deliver: panel access, the handover procedure and the points where the system stops on its own.
05
We read the logs and tune
After the first real conversations, we look at the query log: what it found, with what score, in how much time. From there, we tune the threshold, the number of fragments or the source text. We deliver: the list of changes made and the reason for each.
The path of a message
The data
What we touch, where it lives, and how long it stays
The questions anyone with a data protection officer would ask — asked here before they do.
Where the conversations are
MySQL, on our own server in Microsoft Azure. The schema has over 80 tables modelled in Prisma, with separate tables for Meta threads, Telegram threads, 999.md threads, comments, labels, internal notes and the audit log.
Channel tokens are stored encrypted
The WhatsApp token is stored encrypted with Fernet, with fallback to plain text only for old records. Messages have their own encryption module in the channel service. There is token monitoring and expiry notification.
What the widget sees and where from
The widget has a list of allowed origins per assistant. The default behaviour must be known: if the list is empty, the check passes — it is a compatibility choice with old installations, written as such in code. This means that domain restriction is configured explicitly for each implementation that requires it; we configure it, we do not assume it.
What personal data comes in without you asking for it
The free field of a conversation can contain anything — a phone number, an address, a medical issue. It cannot be technically stopped, but it can be handled: what is kept, for how long, who has access and what the team is instructed not to ask for on the channel are set. The implementation processing register is written at the same time as the implementation, not after.
Retention period
It is set per implementation and written in the client's published policy. The rule we apply to ourselves and also ask for in projects: if a code change changes what is collected, who receives it or how long it is kept, the published policy is changed in the same commit.
A case
A price question that starts on Instagram and ends with a human
The situation
An online catalogue shop was receiving price questions in Instagram DMs and on Messenger. The same five questions, all day, and the correct answer was already on the site.
What we built
We connected the two channels, indexed the sources and linked the catalogue through the shop's built-in tools, which for WooCommerce read the public Store API — so without access keys in the platform. We set the concatenation buffer, so three messages written in a row become one question, and we wrote explicitly what the assistant is not allowed to do. On Messenger we started the product carousel, with the three conditions on which it depends; on Instagram we stayed with text plus image, because Meta does not offer generic templates there.
What came out
Price and availability questions get a reply with the displayed product and link. What falls outside the catalogue goes to `human` status and reaches the designated person, with the full conversation context. The query log shows what the assistant found, with what score and in how much time, so tuning is done on data.
What the case does not say
The result depends on the quality of the catalogue. A wrong price on the site becomes a wrong price in the conversation — the assistant reads the source, it does not correct it. And we did not measure the reduction in the team's workload, so we do not state it as a result.
Questions
What people ask us before they call
Can it write first on WhatsApp to a client who has not written in a week?
No. And the reason is Meta plus a real gap on our side, both stated. Outside the 24-hour window from the client's last message, Meta rejects free text with error 131047, and the platform marks the message as failed and shows the error instead of pretending it was sent. Sending outside the window requires message templates approved by Meta, and those do not exist in our code — a search for type `template` across the whole code returns zero results. So: reply within the 24-hour window, yes; proactive WhatsApp campaign, no, until templates are implemented.
On which channels does it actually work, today?
Six, with own code verified on 06.09.2026: Facebook Messenger, Instagram Direct, Telegram, 999.md, web widget and WhatsApp Business Cloud API. An older internal audit found code for SMS and e-mail as well; we did not re-verify them at this date, so we do not list them as available without a verification on the concrete implementation. A channel that appeared in the meantime is not assumed connected.
Is the 999.md integration official?
It is not a partner integration with a public API. It works with your account session: the platform asks for the value of the session cookie from your browser and uses it to read and reply to messages. The practical consequence, stated in advance: if 999.md changes the session mechanism, the integration stops and must be reconnected. Anyone who does not accept that risk should not start on this channel.
How does it know the answer and what does it do when it does not know it?
Search your sources with a hybrid search — by default five snippets, threshold 0.70, 30% weight on words, the rest semantic — and it may attach the source to the response. When it does not find anything, the designed path is handover to a human: the thread moves to the `human` state, and the log records who took over and when. There is also automatic return to the bot, likewise as a recorded action.
Can it show products with a photo and price?
On Messenger, yes — a card carousel. On Instagram, no: Meta does not offer generic templates on Instagram, so there it remains text plus a photo. The carousel requires three things at once: it must be enabled in the assistant configuration, the tool must return at least two products, and the assistant instruction must ask for a list with price and link — because the detector looks at the agent text, not only at the data. If any of the three is missing, the carousel is not built. We configure all three, but it is useful to know why.
Who answers the client who writes in Russian to the team that speaks Romanian?
The operator’s reply can be automatically rewritten in the client’s language, while keeping the agent’s tone and without adding facts — that works. The reverse direction, the client’s message translated for the operator, does not exist in the code at the date of verification. In practice: the client receives a reply in their language, but the operator reads the original message. If you also need the other direction, that is work to be done, not a setting to tick.
What happens if a client sends the same message twice?
They do not receive two replies. Duplicates are filtered by message identifier, with 120 seconds of memory — the Meta webhook really does resend. And messages written one after another are combined into a 15-second buffer and receive a single reply, for all of them.
Which AI model does it reply with and what happens if I want another one?
Calls go through our own gateway at `api.megapromoting.com/v1`, which speaks the OpenAI format for all back-end providers; the model is chosen per assistant. Changing the model does not require rewriting the integration. For embeddings we use `text-embedding-3-small`; changing the embedding model, however, requires reindexing the sources — it is not the same thing.
What does this service not do?
It does not do voice over the phone — that is another platform of ours, Kallina, and it is another job. It does not send proactive messages on WhatsApp, for the reason above. It does not generate PDFs and does not attach received photographs to an order — at the date of verification, the received photograph is only marked. And we do not guarantee percentages of workload reduction: we do not have the measurement, so we do not claim it.
20 of them are code and files from our repositories. We don't publish their name or line: together, on a single page, they would describe too precisely how systems that aren't only ours are built. We go through them with you, in the repository, on request — verification stays possible, it just happens in a conversation.