Documentation

Lead scoring

How scores are computed, decay, and gate the automation pipeline.

Lead scoring lives at the intersection of CRM and Automation. The score is a numeric attribute on the lead record. Automations read it via check_engagement_score, the CRM service exposes endpoints to compute and refresh it, and decay rules make sure stale leads don't sit at the top of the queue forever.

The score model

A lead score is an integer (typically 0–100) stored on lead.data.score. The score combines:

  • Demographic fit — fixed points for matching ICP fields (industry, size, geography).
  • Behavioral activity — points awarded by recent events (email opens, page visits, link clicks, content downloads, form submits).
  • Negative signals — point deductions for unsubscribes, bounces, opt-outs.
  • Decay — a time-based decay that subtracts a percentage of the score per period of inactivity.

The weights are configurable per org. The CRM LeadScoringService exposes the calculator; rules are stored as scoring rule records so they can be updated without code.

Recalculate a score

POST/crm/leads/score/:idJWT

Recomputes a single lead's score from current rules and recorded activity. Returns {leadId, score}.

curl -X POST https://appengine.appmint.io/crm/leads/score/$LEAD_ID \
  -H "Authorization: Bearer $JWT" -H "orgid: $ORG"
POST/crm/leads/score/batchJWT

Batch recalc — pass an array of lead IDs. Use this nightly via a scheduled_time automation to apply decay.

Use the score in a flow

The check_engagement_score condition reads the score from context.variables.engagementScore, .leadScore, or .contact.leadScore (it tries all three) and compares against a threshold:

{
  "id": "score-gate",
  "type": "condition",
  "config": {
    "conditionType": "check_engagement_score",
    "scoreThreshold": 75,
    "operator": "gte",
    "timeframe": "30d",
    "trueFlowId": "create-task-for-rep",
    "falseFlowId": "stay-in-nurture"
  }
}

If no score is found on the context, the condition computes a basic one from available activity (counts of opens, clicks, page visits). For production accuracy, hydrate the score before the condition runs by calling POST /crm/leads/score/:id from an action step or by triggering on data_updated of the lead record.

Decay rules

A decay rule reduces a stale lead's score over time. Configure them as scoring rules of type decay:

{
  "type": "decay",
  "trigger": "no_activity",
  "windowDays": 30,
  "reductionPercent": 25,
  "minScore": 0
}

Apply decay by scheduling a daily automation:

{
  "name": "Daily lead-score decay",
  "triggers": [
    { "id": "t1", "type": "scheduled_time", "config": { "schedule": { "type": "cron", "cronExpression": "0 2 * * *" } } }
  ],
  "steps": [
    {
      "id": "s1",
      "type": "action",
      "config": {
        "actionType": "send_webhook",
        "url": "{{baseUrl}}/crm/leads/score/batch",
        "method": "POST",
        "body": { "all": true, "applyDecay": true }
      }
    }
  ]
}

Or call the CRM service directly from a custom action.

Qualification thresholds

Three score bands drive the lead pipeline:

BandScore rangePipeline action
Cold0–39Stay in nurture campaigns
Warm40–74Create follow-up task; add to drip
Hot75+Auto-qualify; assign rep; create opportunity

The qualify_lead action moves a lead into the qualified state:

POST/crm/leads/qualify/:idJWT
{
  "id": "auto-qualify",
  "type": "action",
  "config": {
    "actionType": "qualify_lead",
    "leadId": "{{lead.id}}",
    "notes": "Auto-qualified at score {{lead.score}}"
  }
}

The complementary actions are disqualify_lead (POST /crm/leads/disqualify/:id) and convert_lead (POST /crm/leads/convert/:id) — typically wired into a flow after a hot lead closes a deal.

Wiring it together

A complete lead-scoring loop:

  1. Triggers update the score. A database_change automation on data_updated for activity records calls POST /crm/leads/score/:id to refresh the score whenever a contact does something noteworthy.
  2. A score-gate condition decides what's next. check_engagement_score branches between nurture, follow-up, and qualification.
  3. A daily cron automation applies decay. Schedule a scheduled_time trigger at 2am that batch-recalculates scores so inactive leads cool off.
  4. Pipeline moves trigger more flows. A database_change automation on lead updates can fire when qualified === true to create an opportunity, assign a rep, and notify Slack.

The score is just a number. The interesting logic is in the rules that compute it and the conditions that read it. Keep both versioned — when you change weights, automations branch differently overnight.