Skip to main content

Outbound Server

Overview

The Outbound Server specializes in analyzing outbound campaign engagement data. It helps you understand email campaign performance, reply rates, contact engagement, and identify accounts for outbound campaigns.

Server Details

  • Server Name: outbound-server
  • Version: 0.0.1
  • Endpoint: /mcp/outbound
  • Authentication: Organization-level required

Purpose

Analyze outbound email campaigns, track engagement metrics (opens, clicks, replies), measure campaign performance, and identify accounts that should be added to campaigns or have been contacted.

Available Tools

1. engagements-schema-tool

Purpose: Get field mappings for the Engagements index

When to use: Before constructing queries to understand available fields

Returns: Elasticsearch mapping with field names, types, and descriptions

2. engagements-compute-tool

Purpose: Execute Elasticsearch queries against engagement events

Input: Elasticsearch query JSON Returns: Query results with engagement events and aggregations

Important: The Engagements index is event-level - each document represents one engagement event (email sent, opened, replied, etc.)

3. companies-schema-tool

Purpose: Get field mappings for the Companies index (outbound-specific)

When to use: For cross-index queries involving account attributes

4. companies-compute-tool

Purpose: Execute queries against the Companies index

When to use: For negation queries or when you need account data not in Engagements

5. chart-format-tool

Purpose: Generate Chart.js configurations for visualizing campaign data

Key Concepts

Event Types

Filter by eventType.keyword to get specific engagement types:

  • email_sent - Outbound email was sent
  • email_open - Lead opened the email
  • email_reply - Lead replied to the email
  • email_bounce - Email bounced (undeliverable)
  • email_link_click - Lead clicked a link in the email
  • lead_unsubscribed - Lead unsubscribed from campaign

Nested Account Fields

The Engagements index contains two special nested fields with complete Company data:

targetAccountEngaged

Full Company object when the lead's company matches an engaged/identified account. Access fields using dot notation:

  • targetAccountEngaged.Uuid - Account UUID
  • targetAccountEngaged.Name - Company name
  • targetAccountEngaged.Domain - Company domain
  • targetAccountEngaged.Icp - ICP score (0-100)
  • targetAccountEngaged.Intent - Intent score (0-100)
  • targetAccountEngaged.Buying Stage.keyword - Buying stage
  • targetAccountEngaged.Industry.keyword - Industry
  • targetAccountEngaged.Employees - Employee count
  • targetAccountEngaged.Urls - Pages visited (use wildcard search)
  • And all other Company model fields...

targetAccount

Full Company object when the lead's company matches a prospect/target account list.

Critical Rule for Negation Queries

IF the query contains keywords like:

  • "not contacted"
  • "haven't been touched"
  • "no outbound"
  • "overdue for"
  • "should add to campaigns"
  • "not in any campaign"
  • "never contacted"
  • "missing from campaigns"

THEN you MUST use BOTH tools:

  1. Call companies-compute-tool to get accounts matching criteria
  2. Call engagements-compute-tool to get accounts already in campaigns
  3. Subtract step 2 from step 1

Why? The Engagements index only contains accounts that HAVE engagement events. You cannot find "not contacted" accounts by querying Engagements alone.

Common Use Cases

1. Campaign Reply Rate

Workflow:

{
"query": {
"term": {"campaignName.keyword": "Q1 Outreach"}
},
"size": 0,
"aggs": {
"sent": {
"filter": {"term": {"eventType.keyword": "email_sent"}}
},
"replied": {
"filter": {"term": {"eventType.keyword": "email_reply"}}
}
}
}

Calculate: replied._count / sent._count * 100

2. Reply Rates Across All Campaigns

{
"size": 0,
"aggs": {
"by_campaign": {
"terms": {"field": "campaignName.keyword", "size": 50},
"aggs": {
"sent": {
"filter": {"term": {"eventType.keyword": "email_sent"}}
},
"replied": {
"filter": {"term": {"eventType.keyword": "email_reply"}}
},
"reply_rate": {
"bucket_script": {
"buckets_path": {
"sent": "sent>_count",
"replied": "replied>_count"
},
"script": "params.sent > 0 ? params.replied / params.sent * 100 : 0"
}
}
}
}
}
}

3. Unique Contacts per Campaign

{
"size": 0,
"aggs": {
"by_campaign": {
"terms": {"field": "campaignName.keyword", "size": 50},
"aggs": {
"unique_contacts": {
"cardinality": {"field": "toEmail.keyword"}
}
}
}
}
}

4. Contacts Who Opened But Didn't Reply

{
"size": 0,
"aggs": {
"by_contact": {
"terms": {"field": "toEmail.keyword", "size": 200},
"aggs": {
"has_open": {
"filter": {"term": {"eventType.keyword": "email_open"}}
},
"has_reply": {
"filter": {"term": {"eventType.keyword": "email_reply"}}
},
"opened_not_replied": {
"bucket_selector": {
"buckets_path": {
"opens": "has_open>_count",
"replies": "has_reply>_count"
},
"script": "params.opens > 0 && params.replies == 0"
}
}
}
}
}
}

5. Engagement Trend Over Time

{
"query": {
"range": {"eventTimestamp": {"gte": "now-30d/d"}}
},
"size": 0,
"aggs": {
"by_day": {
"date_histogram": {
"field": "eventTimestamp",
"calendar_interval": "day"
},
"aggs": {
"by_event_type": {
"terms": {"field": "eventType.keyword"}
}
}
}
}
}

6. ICP-Fit Accounts That Replied

{
"query": {
"bool": {
"filter": [
{"term": {"eventType.keyword": "email_reply"}},
{"range": {"targetAccountEngaged.Icp": {"gte": 70}}}
]
}
},
"_source": [
"toEmail",
"toName",
"campaignName",
"targetAccountEngaged.Name",
"targetAccountEngaged.Domain",
"targetAccountEngaged.Uuid",
"targetAccountEngaged.Icp"
],
"size": 50
}

7. Accounts That Viewed Pricing Pages

{
"query": {
"bool": {
"filter": [
{"exists": {"field": "targetAccountEngaged"}},
{"wildcard": {"targetAccountEngaged.Urls": "*pricing*"}}
]
}
},
"_source": [
"toEmail",
"campaignName",
"targetAccountEngaged.Name",
"targetAccountEngaged.Urls",
"targetAccountEngaged.Uuid"
],
"size": 100
}

8. High-ICP Accounts NOT in Campaigns (Negation Query)

⚠️ CRITICAL: This requires BOTH tools

Step 1 - Get candidate accounts:

// Call companies-compute-tool
{
"query": {
"bool": {
"filter": [
{"range": {"Icp": {"gte": 70}}},
{"term": {"Buying Stage.keyword": "Awareness"}},
{"range": {"Identified People": {"gte": 1}}}
]
}
},
"_source": ["Name", "Domain", "Uuid", "Icp", "Buying Stage"],
"size": 100
}

Step 2 - Get accounts already touched:

// Call engagements-compute-tool
{
"query": {
"terms": {"eventType.keyword": ["email_sent", "email_open", "email_reply", "email_bounce", "email_link_click"]}
},
"aggs": {
"touched_accounts": {
"terms": {"field": "targetAccountEngaged.Uuid.keyword", "size": 10000}
}
},
"size": 0
}

Step 3 - Compare and subtract:

  • Take UUIDs from Step 1
  • Remove UUIDs found in Step 2 aggregation
  • Return remaining accounts as "not contacted"

9. Decision-Stage Accounts by Industry

{
"query": {
"bool": {
"filter": [
{"term": {"targetAccountEngaged.Buying Stage.keyword": "Decision"}},
{"exists": {"field": "targetAccountEngaged"}}
]
}
},
"size": 0,
"aggs": {
"by_industry": {
"terms": {"field": "targetAccountEngaged.Industry.keyword", "size": 20},
"aggs": {
"unique_accounts": {
"cardinality": {"field": "targetAccountEngaged.Uuid.keyword"}
},
"avg_icp": {
"avg": {"field": "targetAccountEngaged.Icp"}
}
}
}
}
}

Best Practices

1. Always Filter by eventType

For outbound campaign analysis, always include eventType filter:

{
"terms": {
"eventType.keyword": ["email_sent", "email_open", "email_reply", "email_bounce", "email_link_click"]
}
}

2. Use Aggregations for Metrics

Never retrieve all documents to calculate rates. Use aggregations with bucket_script.

3. Unique Contact Counts

Use cardinality aggregation on toEmail.keyword for unique contact counts.

4. Date Filtering

Always use eventTimestamp for time-based filtering:

{"range": {"eventTimestamp": {"gte": "now-7d/d", "lte": "now/d"}}}

5. Accessing Nested Account Fields

Use dot notation with .keyword for exact matches on text fields:

{"term": {"targetAccountEngaged.Industry.keyword": "Technology"}}

For numeric fields, no .keyword needed:

{"range": {"targetAccountEngaged.Icp": {"gte": 70}}}

For wildcard searches on text, no .keyword:

{"wildcard": {"targetAccountEngaged.Urls": "*pricing*"}}

6. Check for Nested Field Existence

To find leads with target accounts:

{"exists": {"field": "targetAccountEngaged"}}

When to Use Both Tools

Use BOTH Companies and Engagements tools when:

  1. Query involves account attributes AND campaign status
  2. Negation queries (accounts NOT in campaigns)
  3. Comparison queries (account characteristics vs campaign performance)
  4. Gap analysis (accounts meeting criteria but lacking campaign activity)

Use ONLY Engagements tool when:

  • Query is purely about campaign performance (reply rates, open rates)
  • Account attributes accessed via targetAccountEngaged.* fields

Use ONLY Companies tool when:

  • Query is purely about account attributes (ICP, intent, buying stage)
  • No mention of campaigns or engagement

Query Construction Tips

Combining Account Filters with Engagement

{
"query": {
"bool": {
"filter": [
{"term": {"eventType.keyword": "email_reply"}},
{"range": {"targetAccountEngaged.Icp": {"gte": 70}}},
{"term": {"targetAccountEngaged.Industry.keyword": "Technology"}},
{"range": {"eventTimestamp": {"gte": "now-30d/d"}}}
]
}
}
}

Multiple Event Types

{
"query": {
"terms": {
"eventType.keyword": ["email_open", "email_reply", "email_link_click"]
}
}
}

Text Search on Campaign Names

{
"query": {
"wildcard": {"campaignName": "*q1*outreach*"}
}
}

Sorting by Engagement Quality

{
"sort": [
{"sentimentScore": "desc"},
{"eventTimestamp": "desc"}
]
}