Grow2.ai has shipped against Bitrix24 in six pilots and KeyCRM in three. They're the two most common CRMs in Ukrainian SMB. The integration patterns differ in ways that matter at runtime — this note is the practical cheat sheet, written by someone who has lost weekends to both.
Bitrix24 · what works, what fights you
Works: Bitrix has a permissive REST API, custom fields are a first-class concept, and webhooks fire reliably. We can write back conversation summaries, tagged outcomes, and ICP scores into custom fields without administrator intervention after the initial setup.
Fights: The deal/lead pipeline state machine is opinionated. Moving a lead from "new" to "qualified" via API requires the deal to be in the right pipeline, the right stage, and the right responsible-user — three pieces of state the agent has to keep in sync. We've learned to mirror Bitrix's state machine in our own state, do all transitions client-side, and only push the final state. Cuts the round-trip race conditions to ~zero.
KeyCRM · what works, what fights you
Works: KeyCRM's order-centric model maps cleanly to e-commerce and inventory-aware workflows (auto dealers, retail). The order has a status, a buyer, a list of items, and a pile of custom fields — the agent can reason against that model directly.
Fights: The API is rate-limited more aggressively than Bitrix (~120 requests/minute on standard plans) and the documented endpoints lag the actual API surface. We have hit at least three undocumented endpoints by reading network traffic from the web client. Webhooks fire on most events but not all — we run a 90-second poll fallback for the events that don't webhook reliably.
The field-mapping pattern we use for both
Regardless of CRM, Grow2.ai writes one structured payload per conversation, with the same shape. We then translate that payload into CRM-native fields at the integration boundary. The internal payload looks like:
conversation_summary· 1-paragraph human-readable.icp_score· 0–100, with explanation string.next_best_action· enum:book-call,nurture,disqualify,escalate.open_questions· array of strings the agent flagged as unanswered.preferred_channel· enum:tg,wa,email,voice.language· BCP-47 code.
On Bitrix, those map to six custom fields on the lead. On KeyCRM, they map to a custom-fields object on the buyer. Same shape, two adapters. Lets us swap CRM without touching the agent.
The one mistake to avoid on either
Don't let the agent be the source of truth for state. The CRM is the source of truth. The agent reads, reasons, and writes — but if the CRM and the agent disagree about the state of a deal, the CRM wins by default. We wired a reconciliation job (every 5 minutes) that pulls CRM state and corrects the agent's mirror. Without it, you get the kind of bug where the agent re-engages a closed deal because it didn't see the close event in time.