In AppEngine CRM, opportunities are deals progressing through a sales pipeline, and tasks are the follow-up actions a salesperson schedules against a contact, lead, or deal. Both live under the leads controller because the pipeline machinery is shared — a lead becomes an opportunity by being moved into a "deal" pipeline.
Pipelines
A pipeline is a named ordered list of stages. Most orgs run two: a lead pipeline (new → qualified → demo → proposal → won/lost) and a deal/opportunity pipeline (discovery → negotiation → closing → closed-won/closed-lost).
/crm/leads/pipelinesJWT/crm/leads/pipelinesJWT/crm/leads/pipelines/:idJWT/crm/leads/pipelines/:idJWT/crm/leads/pipelines/:idJWT{
"name": "Enterprise Deals",
"type": "opportunity",
"stages": [
{ "name": "Discovery", "probability": 10 },
{ "name": "Demo", "probability": 30 },
{ "name": "Proposal", "probability": 60 },
{ "name": "Negotiation", "probability": 80 },
{ "name": "Closed Won", "probability": 100, "terminal": true },
{ "name": "Closed Lost", "probability": 0, "terminal": true }
],
"routingRules": [
{ "stage": "Demo", "assignTo": "user-rep-east" }
]
}
Stage management
/crm/leads/pipelines/stages/:idJWT/crm/leads/pipelines/stages/:id/:stageIdJWT/crm/leads/pipelines/stages/:id/:stageIdJWT/crm/leads/pipelines/stages/reorder/:idJWTReorder accepts an array of stage IDs in the new order — the stages drag-reorder UI calls this once on drop.
Routing unassigned
/crm/leads/pipelines/:id/route-unassignedJWTRuns the pipeline's routingRules against every lead/opportunity that doesn't yet have an owner. Useful after adding a new rep or rebalancing territories.
Lead and opportunity CRUD
The same endpoints handle both — pipeline membership is what distinguishes them.
/crm/leads/detailJWT/crm/leads/detailJWT/crm/leads/detail/:idJWT/crm/leads/detail/:idJWT/crm/leads/detail/:idJWTA lead body:
{
"contactId": "contact-abc",
"pipelineId": "pipe-enterprise",
"stageId": "stage-discovery",
"value": 25000,
"currency": "USD",
"expectedCloseDate": "2026-07-15",
"owner": "user-rep-east"
}
Lifecycle actions
/crm/leads/qualify/:idJWT/crm/leads/disqualify/:idJWT/crm/leads/convert/:idJWT/crm/leads/assign/:idJWT/crm/leads/follow-up/:idJWTqualify moves a lead to the next pipeline stage and stamps qualifiedAt. convert takes a qualified lead and creates an opportunity in the deals pipeline (and optionally a customer record). follow-up schedules a task — the next-action workflow most reps live in.
Tasks (follow-up)
A follow-up task is the simplest task in the system. The endpoint accepts due date, assignee, and notes:
await fetch(`/api/crm/leads/follow-up/${leadId}`, {
method: 'POST',
headers: { orgid: ORG_ID, Authorization: `Bearer ${jwt}` },
body: JSON.stringify({
dueAt: '2026-04-30T10:00:00Z',
assignTo: 'user-rep-east',
notes: 'Follow up on demo recap email',
}),
});
For richer task management — recurring tasks, subtasks, dependencies — use the Automation module which can create tasks as a workflow action against any record.
Activities
/crm/leads/activities/:idJWT/crm/leads/activities/:idJWTLead-scoped activities (calls, emails, meetings, notes). The org-wide activity log lives separately under /crm/activity — see Activities and audit.
Lead scoring
/crm/leads/score/:idJWT/crm/leads/score/batchJWT/crm/leads/analyticsJWTScores feed routing, prioritisation, and analytics. Configure scoring rules in the Rule Engine. The batch endpoint re-scores a list of lead IDs — use it nightly or after rule changes.
Enrichment
/crm/leads/enrich/:idJWT/crm/leads/enrich/batchJWT/crm/leads/enrich/autoJWT/crm/leads/enrichment/status/:idJWTPulls firmographic and contact data from the Data Enrichment provider configured for the org. auto runs enrichment on every new lead matching a filter.
Stage transitions emit lead.stage_changed events. Wire these to automations to send nurture emails, notify reps, or update revenue forecasts.