Misar Docs
MisarMailMisar.BlogMisarReachMisarPostMisar.DevMisar PlatformMisar IdentityMisar Posts API
Api Reference

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

POST/mail/api/list-health/run

Triggers a full health analysis. The job runs asynchronously — poll /api/list-health/stats until lastRunAt updates.

Response fields

successboolean

true when the job was queued.

dataobject

Contains 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

GET/mail/api/list-health/stats

Returns the current list health stats.

Response fields

successboolean

true when the request succeeded.

dataobject

Health 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

POST/mail/api/list-health/win-back

Targets contacts who have not opened an email in the last thresholdDays days and schedules the specified campaign to send to them.

Request body

campaignIdstringbodyrequired

ID of the campaign to send.

thresholdDaysintegerbodydefault: 90

Days of inactivity to qualify.

Response fields

successboolean

true when the win-back campaign was scheduled.

dataobject

Contains 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)

POST/mail/api/contacts/health/run

Same 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

successboolean

true when the job was queued.

dataobject

Contains 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)

GET/mail/api/contacts/health/stats

Returns a contacts-level subset of the full health stats — same response shape as /api/list-health/stats without campaign-level signals.

Response fields

successboolean

true when the request succeeded.

dataobject

Contacts-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

GET/mail/api/contacts/health/unengaged

List contacts that qualify as unengaged, with pagination.

Query parameters

thresholdDaysintegerquerydefault: 90

Days of inactivity to qualify as unengaged.

pageintegerquerydefault: 1

Page number.

limitintegerquerydefault: 20

Results per page (max 100).

Response fields

successboolean

true when the request succeeded.

dataobject

Contains 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

GET/mail/api/contacts/scoring-rules

Returns the current ruleset that defines how engagement points are awarded.

Response fields

successboolean

true 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

POST/mail/api/contacts/scoring-rules

Replaces the scoring ruleset. Send an array of rule objects.

Request body

eventstringbodyrequired

Event type: open, click, reply, bounce, complaint.

pointsintegerbodyrequired

Points awarded (negative values subtract).

decay_daysintegerbodyrequired

Days 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

  1. Run /api/list-health/run to refresh stats.
  2. Check /api/list-health/stats — note unengagedCount and healthScore.
  3. Create a re-engagement campaign in the dashboard (subject line: "We miss you — here's 20% off").
  4. Call /api/list-health/win-back with the campaign ID and your chosen thresholdDays.
  5. 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.