#CRM: Operating Modes
Status: Planned. The CRM mode (
internal/external/both) and the public leads API are not implemented yet: today the platform works only as "our CRM" (theinternalmode, with no choice). This page records the intent from the public API design. All names of fields, paths, and events may change before release. No dates are promised.
The Walker Formulation Academy platform manages leads itself: a lead is created from a conversation with the AI seller, from a form, from a pre-registration, and from other sources, and it gets a score, a status, and an owner. If your organization already has its own CRM (amoCRM, Bitrix24, HubSpot, or a custom system), the question arises: who is in charge at each moment of the lead's life. The CRM mode answers it.
Access: the scopes leads:read, leads:write, leads:score (details: Leads API); the mode is changed only in the dashboard. Paid: scoring only (leads:score). PII: yes, a lead's contact contains a name, an email, and a phone number; see PII transfer.
#The main rule: the lead is always with us
Whichever mode you choose, the lead always exists on the Walker Formulation Academy platform. It is tied to the conversation, the attribution (where the person came from), the score, and the AI seller's memory; without these the agent cannot continue the conversation. The mode changes not whether we hold the lead but who owns its fields: status, owner, outcome.
An external CRM does not "take" the lead away: it receives a copy and runs its own process, while we remain the source of the conversation, the attribution, and the score.
#Three modes
The mode is stored in the tenant's settings (settings.crm.mode) and applies to the whole tenant. An integration sees the current mode in GET /me (crm_mode).
| Mode | Who is the source of truth for status, owner, outcome | What our agents do | Our CRM screen |
|---|---|---|---|
internal (default) | Our CRM | Write to our CRM. Events go to webhook subscribers, if there are any | A working tool |
external | Before the lead is accepted, we are. After it is accepted, the external CRM is | Do not change the status themselves: they send the external CRM an "action request" (lead.action_requested) and wait for confirmation or refusal | A read-only projection |
both | Field by field, per the ownership table (below) | Write to our CRM and send events; changes from the external CRM are accepted through the API with a version check | A working tool |
The default mode is internal: this is how the platform behaves today. Connecting the leads API does not change it.
#Accepting a lead (the external mode)
A lead goes through two phases.
- Before acceptance. The lead is ours: we create it, score it, and run the conversation. The external CRM receives a
lead.createdevent and decides whether it needs this lead. - After acceptance. The external CRM calls
POST /leads/{id}/accept. From that moment:- the status, owner, outcome, and loss reason belong to the external CRM;
- our agents stop their own touches of the lead: they no longer change the status, create tasks, or send follow-ups on their own decision;
- instead of acting, the agent creates a
lead.action_requestedrequest (see Agent actions), which the external CRM confirms or rejects; - our screen shows the lead as a read-only projection.
If the tenant switches the mode back to internal, accepted leads become ours again; see Switching the mode.
#Field ownership in the both mode
In the both mode, both systems can change a lead, so each field has an owner.
| Field | Owner | Rule |
|---|---|---|
| Status, owner, outcome, loss reason | The latest write with the higher version | On a conflict, the external CRM wins |
| Score, flags, intent, AI summary | Us | The external CRM can add its own score (external_score) without replacing ours |
| Contact (full name, email, phone) | Whoever changed it last, with a version | A contact merge arrives as an event |
| Attribution, source | Us | Recorded once, not subject to editing |
The version is the lead's version field: every change increases it, and a write from outside must pass the expected version in If-Match (otherwise 428 precondition_required; on a mismatch, 412 precondition_failed). A write to a field owned by the other party is rejected with 409 invalid_state. Details: Leads API.
#How to choose a mode
| Situation | Mode |
|---|---|
| You work with leads in our dashboard, and there is no external CRM | internal |
| Managers work only in the external CRM; you need our conversation and score | external |
| Part of the process (for example, initial qualification) runs with us and the rest with you; you need both sides | both |
| You only want copies of new leads delivered to your system, while working in ours | internal + a subscription to lead.created |
internal is the default mode; subscribing to events does not require changing the mode.
#What the external CRM sees
Webhook v2 events. The payload is "thin" by default: IDs, version, type; the content is requested through the API. A subscription with the include_pii: true flag receives the content directly in lead.*, contact.*, and conversation.* events (PII transfer).
| Event | When |
|---|---|
lead.created | A lead is created |
lead.updated | Fields changed (the payload carries the version) |
lead.scored | The lead was scored |
lead.status_changed | The status changed |
lead.action_requested | An agent asks for an action (the external mode) |
conversation.handoff_requested | The AI seller asks to hand the conversation to a person; data contains lead_id if the conversation has a lead |
task.created, task.completed | A task was created and closed |
contact.merged | Contacts were merged |
conversation.summary_ready | The conversation summary is ready |
crm.mode_changed | The tenant switched the CRM mode |
There is no separate lead.handoff_requested event: a handoff to an operator arrives as a single conversation.handoff_requested event.
The event envelope, the signature, and delivery retries are covered in the section on webhooks.
#Switching the mode
The mode is switched only in the dashboard, by the tenant owner's right (integrations.manage and crm.settings). The mode is not changed through the API. Subscribers receive a crm.mode_changed event for every switch.
| Transition | What happens |
|---|---|
internal → external | Open leads remain ours until the external CRM calls POST /leads/{id}/accept; accepted leads pass to the external CRM |
external → internal | Accepted leads become ours with their last known state. Open action requests (requested) expire (expired) |
Transitions involving both are not described in the design; see "Open questions".
#Open questions
- Transitions between
bothand the other modes: the fate of open action requests and the order of field ownership at the moment of switching (technical). - The contents of
datafor thecrm.mode_changedevent (technical; to be settled in OpenAPI). - Whether the
externalandbothmodes are available on all three cells at once (for the owner).
See also: errors, pagination, idempotency, cells and data.