Walker Formulation Academy for developers API reference Error catalog OpenAPI
Sections

#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" (the internal mode, 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).

ModeWho is the source of truth for status, owner, outcomeWhat our agents doOur CRM screen
internal (default)Our CRMWrite to our CRM. Events go to webhook subscribers, if there are anyA working tool
externalBefore the lead is accepted, we are. After it is accepted, the external CRM isDo not change the status themselves: they send the external CRM an "action request" (lead.action_requested) and wait for confirmation or refusalA read-only projection
bothField 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 checkA 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.

  1. Before acceptance. The lead is ours: we create it, score it, and run the conversation. The external CRM receives a lead.created event and decides whether it needs this lead.
  2. 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_requested request (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.

FieldOwnerRule
Status, owner, outcome, loss reasonThe latest write with the higher versionOn a conflict, the external CRM wins
Score, flags, intent, AI summaryUsThe external CRM can add its own score (external_score) without replacing ours
Contact (full name, email, phone)Whoever changed it last, with a versionA contact merge arrives as an event
Attribution, sourceUsRecorded 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

SituationMode
You work with leads in our dashboard, and there is no external CRMinternal
Managers work only in the external CRM; you need our conversation and scoreexternal
Part of the process (for example, initial qualification) runs with us and the rest with you; you need both sidesboth
You only want copies of new leads delivered to your system, while working in oursinternal + 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).

EventWhen
lead.createdA lead is created
lead.updatedFields changed (the payload carries the version)
lead.scoredThe lead was scored
lead.status_changedThe status changed
lead.action_requestedAn agent asks for an action (the external mode)
conversation.handoff_requestedThe AI seller asks to hand the conversation to a person; data contains lead_id if the conversation has a lead
task.created, task.completedA task was created and closed
contact.mergedContacts were merged
conversation.summary_readyThe conversation summary is ready
crm.mode_changedThe 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.

TransitionWhat happens
internal → externalOpen leads remain ours until the external CRM calls POST /leads/{id}/accept; accepted leads pass to the external CRM
external → internalAccepted 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

See also: errors, pagination, idempotency, cells and data.