List Health
Monitor your mailing list quality, detect unengaged subscribers, score contacts, and run win-back campaigns
List health tools help you maintain a clean, engaged mailing list. A healthy list improves deliverability, lowers spam complaint rates, and ensures your campaigns reach people who actually want to hear from you.
Authentication
All list health endpoints use session (cookie-based) authentication. They are dashboard-facing and do not accept API keys.
Health score
The healthScore is a 0–100 integer computed from your contact base:
- 100 — all contacts are active and engaged
- 0 — all contacts are suppressed, bounced, or unengaged
Score is weighted across four signals: active ratio, bounce rate, complaint rate, and recent engagement. Run a health check to update it.
Unengaged contacts
A contact is considered unengaged when they have not opened any email within the configured threshold (default: 90 days). Unengaged contacts lower your health score and are the primary target for win-back campaigns.
Run a list health check
/mail/api/list-health/runTriggers a full health analysis. The job runs asynchronously — poll /api/list-health/stats until lastRunAt updates.
Response fields
successbooleantrue when the job was queued.
dataobjectContains jobId and status (queued).
curl -X POST https://api.misar.io/mail/api/list-health/run \
-H "Cookie: session=..."{
"success": true,
"data": {
"jobId": "job_01hvxyz",
"status": "queued"
}
}Get health stats
/mail/api/list-health/statsReturns the current list health stats.
Response fields
successbooleantrue when the request succeeded.
dataobjectHealth stats: totalContacts, activeContacts, unengagedCount, bouncedCount, complainedCount, healthScore, and lastRunAt.
curl https://api.misar.io/mail/api/list-health/stats \
-H "Cookie: session=..."{
"success": true,
"data": {
"totalContacts": 12400,
"activeContacts": 10850,
"unengagedCount": 980,
"bouncedCount": 320,
"complainedCount": 44,
"healthScore": 87,
"lastRunAt": "2026-05-27T06:00:00Z"
}
}Schedule a win-back campaign
/mail/api/list-health/win-backTargets contacts who have not opened an email in the last thresholdDays days and schedules the specified campaign to send to them.
Request body
campaignIdstringbodyrequiredID of the campaign to send.
thresholdDaysintegerbodydefault: 90Days of inactivity to qualify.
Response fields
successbooleantrue when the win-back campaign was scheduled.
dataobjectContains campaignId, targetedContacts, thresholdDays, and scheduledAt.
curl -X POST https://api.misar.io/mail/api/list-health/win-back \
-H "Cookie: session=..." \
-H "Content-Type: application/json" \
-d '{ "campaignId": "cam_01hvabc", "thresholdDays": 60 }'{
"success": true,
"data": {
"campaignId": "cam_01hvabc",
"targetedContacts": 980,
"thresholdDays": 60,
"scheduledAt": "2026-05-27T10:05:00Z"
}
}Contacts health check (scoped)
/mail/api/contacts/health/runSame as the full list health check but scoped to contact-level signals only (bounces, complaints, engagement). Faster for large lists when you only need contact data refreshed. The response mirrors /api/list-health/run — returns { jobId, status: "queued" }.
Response fields
successbooleantrue when the job was queued.
dataobjectContains jobId and status (queued).
curl -X POST https://api.misar.io/mail/api/contacts/health/run \
-H "Cookie: session=..."{
"success": true,
"data": {
"jobId": "job_01hvxyz",
"status": "queued"
}
}Contacts health stats (scoped)
/mail/api/contacts/health/statsReturns a contacts-level subset of the full health stats — same response shape as /api/list-health/stats without campaign-level signals.
Response fields
successbooleantrue when the request succeeded.
dataobjectContacts-level health stats (same shape as /api/list-health/stats without campaign-level signals).
curl https://api.misar.io/mail/api/contacts/health/stats \
-H "Cookie: session=..."List unengaged contacts
/mail/api/contacts/health/unengagedList contacts that qualify as unengaged, with pagination.
Query parameters
thresholdDaysintegerquerydefault: 90Days of inactivity to qualify as unengaged.
pageintegerquerydefault: 1Page number.
limitintegerquerydefault: 20Results per page (max 100).
Response fields
successbooleantrue when the request succeeded.
dataobjectContains contacts (each with id, email, status, last_opened_at, days_inactive, engagement_score) and pagination (page, limit, total, total_pages).
curl "https://api.misar.io/mail/api/contacts/health/unengaged?thresholdDays=60&limit=50" \
-H "Cookie: session=..."{
"success": true,
"data": {
"contacts": [
{
"id": "con_01hvghi",
"email": "[email protected]",
"status": "subscribed",
"last_opened_at": "2026-01-10T09:22:00Z",
"days_inactive": 137,
"engagement_score": 12
}
],
"pagination": {
"page": 1,
"limit": 50,
"total": 980,
"total_pages": 20
}
}
}Get contact engagement scoring rules
/mail/api/contacts/scoring-rulesReturns the current ruleset that defines how engagement points are awarded.
Response fields
successbooleantrue when the request succeeded.
dataArray<Rule>Scoring rules. Each rule includes event, points, and decay_days.
curl https://api.misar.io/mail/api/contacts/scoring-rules \
-H "Cookie: session=..."{
"success": true,
"data": [
{ "event": "open", "points": 5, "decay_days": 90 },
{ "event": "click", "points": 10, "decay_days": 90 },
{ "event": "reply", "points": 20, "decay_days": 180 },
{ "event": "bounce", "points": -20, "decay_days": 0 },
{ "event": "complaint", "points": -50, "decay_days": 0 }
]
}Update contact engagement scoring rules
/mail/api/contacts/scoring-rulesReplaces the scoring ruleset. Send an array of rule objects.
Request body
eventstringbodyrequiredEvent type: open, click, reply, bounce, complaint.
pointsintegerbodyrequiredPoints awarded (negative values subtract).
decay_daysintegerbodyrequiredDays after which this rule's points expire.
curl -X POST https://api.misar.io/mail/api/contacts/scoring-rules \
-H "Cookie: session=..." \
-H "Content-Type: application/json" \
-d '[
{ "event": "open", "points": 5, "decay_days": 90 },
{ "event": "click", "points": 10, "decay_days": 90 },
{ "event": "reply", "points": 20, "decay_days": 180 },
{ "event": "bounce", "points": -20, "decay_days": 0 },
{ "event": "complaint", "points": -50, "decay_days": 0 }
]'Recommended win-back flow
- Run
/api/list-health/runto refresh stats. - Check
/api/list-health/stats— noteunengagedCountandhealthScore. - Create a re-engagement campaign in the dashboard (subject line: "We miss you — here's 20% off").
- Call
/api/list-health/win-backwith the campaign ID and your chosenthresholdDays. - After the campaign sends, run another health check. Contacts who open or click will have their engagement score updated and will no longer appear as unengaged.
Contacts who do not respond to a win-back campaign are candidates for suppression. Continuing to send to chronically unengaged addresses harms your sender reputation. Use the unengaged list to unsubscribe or suppress non-responders after the win-back window closes.