Skip to main content
POST
/
api
/
v1
/
master-inbox
/
unread-replies
curl -X POST "https://server.smartlead.ai/api/v1/master-inbox/unread-replies?api_key=YOUR_KEY&fetch_message_history=false" \
  -H "Content-Type: application/json" \
  -d '{
    "offset": 0,
    "limit": 20,
    "filters": {
      "emailStatus": "Replied",
      "campaignId": [12345, 12346],
      "leadCategories": {
        "categoryIdsIn": [1]
      }
    },
    "sortBy": "REPLY_TIME_DESC"
  }'
{
  "messages": [
    {
      "id": "msg_unread_123",
      "campaign_lead_map_id": "2433664091",
      "lead": {
        "email": "john@enterprise.com",
        "first_name": "John",
        "last_name": "Smith",
        "company": "Enterprise Corp"
      },
      "campaign": {
        "id": 12345,
        "name": "Q1 Enterprise Outreach"
      },
      "email_account": {
        "id": 789,
        "email": "sales@yourcompany.com"
      },
      "last_message": {
        "id": "email_abc789",
        "subject": "Re: Demo Request",
        "body": "Hi, thanks for reaching out. I'd like to schedule a demo for next week...",
        "received_at": "2025-01-20T09:15:00Z",
        "sent_from": "john@enterprise.com",
        "sent_to": "sales@yourcompany.com"
      },
      "email_status": "Replied",
      "category": {
        "id": 2,
        "name": "Meeting Request"
      },
      "assigned_to": {
        "id": 456,
        "name": "Jane Smith"
      },
      "is_read": false,
      "is_important": true,
      "tags": ["enterprise", "demo-request"],
      "reply_age_hours": 2.5
    }
  ],
  "total_count": 1,
  "offset": 0,
  "limit": 20
}
Critical for inbox management - get all unread replies in one view. Essential for ensuring timely responses to leads and preventing missed opportunities.

Overview

Retrieves all unread replies from leads across all campaigns. This endpoint is essential for maintaining inbox zero and ensuring no lead response goes unnoticed. Key Features:
  • Unified view of all unread responses
  • Same comprehensive filtering as other inbox endpoints
  • Real-time unread count tracking
  • Optional full message history
  • Priority handling for urgent responses
Common Use Cases:
  • Daily inbox review: Start your day with unread replies
  • Team notifications: Alert team members of unread messages
  • SLA monitoring: Track response times on unread messages
  • Priority routing: Identify high-value unread leads
  • Dashboard metrics: Display unread count badges

Query Parameters

api_key
string
required
Your SmartLead API key
fetch_message_history
boolean
default:"false"
Include full email thread history
  • false: Only latest message (recommended for list views)
  • true: Complete conversation thread (use for detail views)

Request Body

offset
number
default:"0"
Number of records to skip for pagination. Must be non-negative.
limit
number
default:"20"
Number of records to return per page. Must be between 1 and 20.
filters
object
Advanced filtering options to segment unread replies
sortBy
string
default:"REPLY_TIME_DESC"
Sort order for results
  • REPLY_TIME_DESC: Most recent replies first (default, recommended)
  • SENT_TIME_DESC: Most recently sent emails first
curl -X POST "https://server.smartlead.ai/api/v1/master-inbox/unread-replies?api_key=YOUR_KEY&fetch_message_history=false" \
  -H "Content-Type: application/json" \
  -d '{
    "offset": 0,
    "limit": 20,
    "filters": {
      "emailStatus": "Replied",
      "campaignId": [12345, 12346],
      "leadCategories": {
        "categoryIdsIn": [1]
      }
    },
    "sortBy": "REPLY_TIME_DESC"
  }'

Response Codes

200
Success
Request successful - unread replies retrieved
401
Unauthorized
Invalid or missing API key
422
Validation Error
Request validation failed. Common issues:
  • limit > 20
  • More than 5 campaign IDs
  • More than 10 items in other array filters
  • Invalid date format
500
Internal Server Error
Server error occurred
{
  "messages": [
    {
      "id": "msg_unread_123",
      "campaign_lead_map_id": "2433664091",
      "lead": {
        "email": "john@enterprise.com",
        "first_name": "John",
        "last_name": "Smith",
        "company": "Enterprise Corp"
      },
      "campaign": {
        "id": 12345,
        "name": "Q1 Enterprise Outreach"
      },
      "email_account": {
        "id": 789,
        "email": "sales@yourcompany.com"
      },
      "last_message": {
        "id": "email_abc789",
        "subject": "Re: Demo Request",
        "body": "Hi, thanks for reaching out. I'd like to schedule a demo for next week...",
        "received_at": "2025-01-20T09:15:00Z",
        "sent_from": "john@enterprise.com",
        "sent_to": "sales@yourcompany.com"
      },
      "email_status": "Replied",
      "category": {
        "id": 2,
        "name": "Meeting Request"
      },
      "assigned_to": {
        "id": 456,
        "name": "Jane Smith"
      },
      "is_read": false,
      "is_important": true,
      "tags": ["enterprise", "demo-request"],
      "reply_age_hours": 2.5
    }
  ],
  "total_count": 1,
  "offset": 0,
  "limit": 20
}

Common Workflows

Morning Inbox Zero Routine

def morning_inbox_check():
    """Start the day by processing unread replies"""
    # Get all unread replies
    payload = {
        "filters": {"emailStatus": "Replied"},
        "sortBy": "REPLY_TIME_DESC",
        "limit": 20
    }
    
    response = get_unread_replies(payload)
    unread = response.get('messages', [])
    
    print(f"📬 Good morning! You have {len(unread)} unread replies\n")
    
    # Categorize by priority
    high_priority = [m for m in unread if m.get('is_important')]
    interested = [m for m in unread 
                  if m.get('category', {}).get('id') == 1]
    
    print(f"🔥 {len(high_priority)} high-priority")
    print(f"👍 {len(interested)} interested leads")
    print(f"📋 {len(unread) - len(high_priority) - len(interested)} general")
    
    return unread

Real-time Unread Counter

def get_unread_count(filters=None):
    """Quick unread count (no message content)"""
    payload = {
        "filters": filters or {"emailStatus": "Replied"},
        "limit": 1  # Minimal data transfer
    }
    
    response = get_unread_replies(payload)
    return response.get('total_count', 0)

# Usage for dashboard badge
total_unread = get_unread_count()
print(f"Unread badge: {total_unread}")

# By campaign
campaign_unread = get_unread_count({"campaignId": 12345})
print(f"Campaign 12345: {campaign_unread} unread")

Unread SLA Monitor

def monitor_unread_sla(sla_hours=24):
    """Alert on unread replies exceeding SLA"""
    from datetime import datetime, timedelta
    
    sla_threshold = (datetime.now() - timedelta(hours=sla_hours)).isoformat() + 'Z'
    
    payload = {
        "filters": {
            "emailStatus": "Replied",
            "replyTimeBetween": [
                "2000-01-01T00:00:00Z",  # Beginning of time
                sla_threshold
            ]
        },
        "sortBy": "REPLY_TIME_DESC",
        "limit": 20
    }
    
    response = get_unread_replies(payload)
    overdue = response.get('messages', [])
    
    if overdue:
        print(f"⚠️  SLA BREACH: {len(overdue)} unread replies over {sla_hours}h old")
        
        for msg in overdue:
            hours_old = (datetime.now() - 
                        datetime.fromisoformat(msg['last_message']['received_at'].replace('Z', '+00:00'))).total_seconds() / 3600
            
            print(f"  - {msg['lead']['email']}: {hours_old:.1f}h old")
    else:
        print(f"✅ All unread replies within {sla_hours}h SLA")
    
    return overdue

Priority Triage System

def triage_unread_replies():
    """Auto-categorize unread replies by urgency"""
    payload = {
        "filters": {"emailStatus": "Replied"},
        "limit": 20
    }
    
    response = get_unread_replies(payload)
    messages = response.get('messages', [])
    
    triage = {
        'urgent': [],      # >24h old, important, or meeting request
        'high': [],        # Interested category or >12h old
        'normal': [],      # Everything else
        'low': []          # Already categorized as not interested
    }
    
    from datetime import datetime
    now = datetime.now()
    
    for msg in messages:
        hours_old = (now - datetime.fromisoformat(
            msg['last_message']['received_at'].replace('Z', '+00:00')
        )).total_seconds() / 3600
        
        category_id = msg.get('category', {}).get('id')
        is_important = msg.get('is_important', False)
        
        if hours_old > 24 or is_important or category_id == 2:
            triage['urgent'].append(msg)
        elif category_id == 1 or hours_old > 12:
            triage['high'].append(msg)
        elif category_id == 3:
            triage['low'].append(msg)
        else:
            triage['normal'].append(msg)
    
    # Display triage results
    print("\n📊 Unread Triage Report:")
    print(f"🚨 Urgent:  {len(triage['urgent'])} - Respond immediately")
    print(f"⚠️  High:    {len(triage['high'])} - Respond today")
    print(f"📋 Normal:  {len(triage['normal'])} - Respond within 24h")
    print(f"⬇️  Low:     {len(triage['low'])} - Can wait")
    
    return triage

Batch Mark as Read

def batch_review_unread(limit=10):
    """Review and mark multiple unread as read"""
    payload = {
        "filters": {"emailStatus": "Replied"},
        "sortBy": "REPLY_TIME_DESC",
        "limit": limit
    }
    
    response = get_unread_replies(payload, fetch_history=True)
    messages = response.get('messages', [])
    
    for i, msg in enumerate(messages, 1):
        print(f"\n[{i}/{len(messages)}] {msg['lead']['email']}")
        print(f"Subject: {msg['last_message']['subject']}")
        print(f"Preview: {msg['last_message']['body'][:100]}...")
        
        # Show full thread if available
        if msg.get('message_history'):
            print(f"Thread: {len(msg['message_history'])} messages")
        
        # Prompt for action (in real app, this would be UI)
        action = input("Action [r]ead / [c]ategorize / [s]kip: ")
        
        if action == 'r':
            # Mark as read using change-read-status endpoint
            mark_as_read(msg['campaign_lead_map_id'])
            print("✅ Marked as read")
        elif action == 'c':
            category = input("Category ID: ")
            update_category(msg['campaign_lead_map_id'], int(category))
            mark_as_read(msg['campaign_lead_map_id'])
            print("✅ Categorized and marked as read")

Filtering Best Practices

1. Prioritize by Time

# Today's unread (highest priority)
today_unread = {
    "filters": {
        "emailStatus": "Replied",
        "replyTimeBetween": [
            datetime.now().replace(hour=0, minute=0).isoformat() + 'Z',
            datetime.now().isoformat() + 'Z'
        ]
    }
}

# This week's unread
week_start = (datetime.now() - timedelta(days=7)).isoformat() + 'Z'
week_unread = {
    "filters": {
        "emailStatus": "Replied",
        "replyTimeBetween": [week_start, datetime.now().isoformat() + 'Z']
    }
}

2. Combine Category Filters

# Hot unread leads (interested or meeting requests)
hot_unread = {
    "filters": {
        "emailStatus": "Replied",
        "leadCategories": {
            "categoryIdsIn": [1, 2]  # Interested, Meeting Request
        }
    }
}

# Unread but not junk
quality_unread = {
    "filters": {
        "emailStatus": "Replied",
        "leadCategories": {
            "categoryIdsNotIn": [3, 4]  # Exclude Not Interested, Do Not Contact
        }
    }
}

3. Team-specific Views

# My team's unread
my_team_ids = [456, 457, 458]
team_unread = {
    "filters": {
        "emailStatus": "Replied",
        "campaignTeamMemberId": my_team_ids
    }
}

# Unassigned unread (needs triage)
unassigned_unread = {
    "filters": {
        "emailStatus": "Replied",
        "leadCategories": {"unassigned": True}
    }
}

4. Campaign-specific Monitoring

# High-value campaign unread
important_campaign_unread = {
    "filters": {
        "emailStatus": "Replied",
        "campaignId": [12345, 12346],  # Enterprise campaigns
        "campaignTagId": 5  # VIP tag
    }
}

Performance Tips

  1. Disable message history for counts: 10x faster
  2. Use limit=1 for counters: Minimal data transfer
  3. Filter by campaign: Reduces query scope
  4. Cache unread count: Update every 5-10 minutes
  5. Paginate large results: Limit=20 is optimal

Real-time Monitoring

import time

def monitor_unread_realtime(interval=60):
    """Monitor unread count in real-time"""
    previous_count = 0
    
    while True:
        current_count = get_unread_count()
        
        if current_count != previous_count:
            if current_count > previous_count:
                new_replies = current_count - previous_count
                print(f"🔔 {new_replies} new unread reply(ies)! Total: {current_count}")
                
                # Fetch the new ones
                payload = {
                    "filters": {"emailStatus": "Replied"},
                    "limit": new_replies
                }
                new_messages = get_unread_replies(payload)
                
                # Notify team
                for msg in new_messages.get('messages', []):
                    send_notification(f"New reply from {msg['lead']['email']}")
            else:
                print(f"✅ Unread count decreased to {current_count}")
            
            previous_count = current_count
        
        time.sleep(interval)  # Check every minute

Integration Examples

Slack Notifications

def send_unread_to_slack(webhook_url):
    """Send unread summary to Slack"""
    unread = get_unread_count()
    
    if unread > 0:
        # Get details
        payload = {"filters": {"emailStatus": "Replied"}, "limit": 5}
        messages = get_unread_replies(payload).get('messages', [])
        
        slack_message = {
            "text": f"📬 You have {unread} unread replies",
            "blocks": [
                {
                    "type": "section",
                    "text": {"type": "mrkdwn", "text": f"*{unread} Unread Replies*"}
                }
            ]
        }
        
        # Add top 5
        for msg in messages:
            slack_message["blocks"].append({
                "type": "section",
                "text": {
                    "type": "mrkdwn",
                    "text": f"• *{msg['lead']['email']}*\n  _{msg['last_message']['subject']}_"
                }
            })
        
        requests.post(webhook_url, json=slack_message)

Dashboard Widget

def get_unread_dashboard_data():
    """Get unread data for dashboard display"""
    total = get_unread_count()
    
    # Get breakdown by category
    interested = get_unread_count({"leadCategories": {"categoryIdsIn": [1]}})
    meeting = get_unread_count({"leadCategories": {"categoryIdsIn": [2]}})
    uncategorized = get_unread_count({"leadCategories": {"unassigned": True}})
    
    # Get age breakdown
    today = get_unread_count({
        "replyTimeBetween": [
            datetime.now().replace(hour=0, minute=0).isoformat() + 'Z',
            datetime.now().isoformat() + 'Z'
        ]
    })
    
    return {
        "total": total,
        "by_category": {
            "interested": interested,
            "meeting_request": meeting,
            "uncategorized": uncategorized
        },
        "by_age": {
            "today": today,
            "older": total - today
        }
    }