#Status map
Status: Planned. Canonical lead statuses already exist on the platform today; the public mapping setup
PUT /crm/status-mapand validation of allowed status values do not. There are no ready-made integrations with amoCRM, Bitrix24, or HubSpot: the examples on this page show how a map could be configured, not a shipped feature. No dates are promised.
Every CRM has its own status names: "New lead", "Negotiations", "Successfully completed". The platform stores leads in canonical statuses, and the status map translates your names into canonical ones and back. This is so that events and agents' action requests speak the same language as your CRM.
Access: leads:write (configuring the map). Paid: no. PII: no.
#Canonical statuses
There are six canonical statuses. The archive is not a status but a flag on a lead.
| Status | Meaning | Deal stage role |
|---|---|---|
new | A new lead, not yet qualified | new |
qualified | The lead is qualified: interest is confirmed | qualified |
proposal | An offer has been made to the customer (for example, a trial lesson) | proposal |
negotiation | Negotiations, preparing for payment | negotiation |
won | Success: the customer bought or enrolled | won (the "won" stage) |
lost | The lead is lost, a reason is given | lost (the "lost" stage) |
The archive works as a mark on a lead: the archived (boolean) and archived_reason fields, for example ai_not_client. The archive is not part of the status map (Leads API).
#Relation to deals
A lead and a deal are different objects. A deal lives in a pipeline and sits at a stage; a lead's status maps to a stage role, not to its name. A pipeline stage has a role (stage_key): new, qualified, proposal, negotiation, won, lost. A tenant can rename a stage freely and in its own language (for example, "Consultation" instead of qualified); the role remains, and automation keeps working. A deal's outcome is determined by the stage marked as won or lost.
An example for a course enrollment pipeline (stage names are configurable; here, the initial Russian ones, translated):
| Lead status | Deal stage |
|---|---|
new | New |
qualified | Consultation |
proposal | Trial lesson |
negotiation | Payment |
won | Enrolled (won) |
lost | Declined (lost) |
The "Partially paid" and "Paid" stages in learning pipelines are found by name, not by role; they do not affect a lead's status map.
#PUT /crm/status-map
Sets the mapping of your CRM's statuses to canonical ones. The map is stored per integration: each integration has its own. The scope is leads:write. PUT is idempotent by nature: a repeated request with the same map gives the same result, and the Idempotency-Key header is accepted and ignored. The request replaces the map entirely.
| Parameter | Type | Required | Constraints | Example |
|---|---|---|---|---|
map | object | yes | The key is an external CRM status, the value is one of the six canonical statuses | {"Новая заявка":"new"} |
The body format (a map wrapper or a flat object) is recorded in the design as {external_status → canonical}. The map wrapper is used below; this is to be settled at release.
Rules:
- Several external statuses can lead to one canonical status (
"В работе"and"Звонок"→qualified). - The canonical status must come from the list, otherwise
422 validation_failed;errors[].fieldpoints to the map key, for example/map/Новая заявка. The valuearchivedis not allowed: the archive is not a status. - An unrecognized external status in a
PATCHgets422 validation_failed. - Without
leads:write:403 scope_missing.
200 OK response: the saved map ({"map": {...}}).
{
"map": {
"Новая заявка": "new",
"Квалификация": "qualified",
"Коммерческое предложение": "proposal",
"Переговоры": "negotiation",
"Успешно реализовано": "won",
"Закрыто и не реализовано": "lost"
}
}
#Examples
curl -X PUT https://formula-cream.pro/api/public/v1/crm/status-map \
-H "Authorization: Bearer $API_KEY" -H "Content-Type: application/json" \
-d '{"map":{"Новая заявка":"new","Успешно реализовано":"won"}}'
# API_KEY=sap_ru_test_EXAMPLE0KEYID_… (an example, not a real key)
await fetch("https://formula-cream.pro/api/public/v1/crm/status-map", {
method: "PUT",
headers: {
Authorization: `Bearer ${process.env.API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({ map: { "Новая заявка": "new", "Успешно реализовано": "won" } }),
});
httpx.put(
"https://formula-cream.pro/api/public/v1/crm/status-map",
headers={"Authorization": f"Bearer {os.environ['API_KEY']}"},
json={"map": {"Новая заявка": "new", "Успешно реализовано": "won"}},
)
$ch = curl_init("https://formula-cream.pro/api/public/v1/crm/status-map");
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => "PUT",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
"Authorization: Bearer " . getenv("API_KEY"),
"Content-Type: application/json",
],
CURLOPT_POSTFIELDS => json_encode(
["map" => ["Новая заявка" => "new", "Успешно реализовано" => "won"]],
JSON_UNESCAPED_UNICODE
),
]);
#Example maps for popular CRMs
There are no ready-made connectors to these systems. The maps below illustrate what a setup could look like. The stage names in your CRM are almost certainly different; check them against your own pipeline.
Below is one way to match typical stage names. The list of names is given for illustration; the exact names and their set depend on your CRM's version and settings, so check them against your pipeline. The mapping is arbitrary: the same stage can be assigned to different canonical statuses.
| Canonical | amoCRM | Bitrix24 | HubSpot |
|---|---|---|---|
new | Incoming leads | New | (the first pipeline stage) |
qualified | Initial contact | Preparing documents | Qualified to buy |
proposal | Decision making | Invoice | Presentation scheduled |
negotiation | Negotiations | In progress | Contract sent |
won | Closed - won | Deal won | Closed won |
lost | Closed - lost | Deal failed | Closed lost |
#Notes
- Today the status in the database is free text with no validation. One path writes the value
in_progress, which is not among the canonical ones; before release it must be cleaned up and validation of allowed values added. - Several parts of the code change a lead's status on their own (manual edit, the "Score" button, conversation analysis, pipeline events, the sales engine, auto-loss after 30 days without activity, archiving). In the
externalandbothmodes they must go through a single lead write service that checks ownership. - An external CRM changes the status through
PATCH /leads/{id}withIf-Match(Leads API); thelead.status_changedevent reports a change on our side.
#Open questions
- The body format of
PUT /crm/status-map: amapwrapper or a flat object (technical). - The reverse direction: when sending events, the platform translates the canonical status into yours; unambiguity is needed if several external statuses lead to one canonical status (technical).
- An endpoint for reading the map (
GET /crm/status-map) is not named in the design (technical). - Whether the deal stage map will be configured separately from the lead status map (for the owner).