# Email Metadata Queries

## Content: Email Metadata Queries

### User Intent

"How do I query emails by sender, subject, labels, etc.?"

### Operation

* **SDK Method**: `queryContents()` with email-specific patterns
* **GraphQL**: `queryContents` query
* **Entity Type**: Content (type: EMAIL)
* **Common Use Cases**: Find emails by sender, filter by labels, search by subject, date range queries

### Email Metadata Structure

Emails have rich metadata captured in the `email` field:

```typescript
interface EmailMetadata {
  identifier: string;              // Message-ID
  threadIdentifier: string;        // Thread ID
  subject: string;
  from: PersonReference[];
  to: PersonReference[];
  cc: PersonReference[];
  bcc: PersonReference[];
  labels: string[];                // Gmail labels
  sensitivity: MailSensitivity;
  priority: MailPriority;
  importance: MailImportance;
  attachmentCount: number;
  unsubscribeUrl: string;
  publicationName: string;
  publicationUrl: string;
}
```

### TypeScript (Canonical)

```typescript
import { Graphlit } from 'graphlit-client';
import { ContentTypes, SearchTypes } from 'graphlit-client/dist/generated/graphql-types';

const graphlit = new Graphlit();

// Query all emails
const allEmails = await graphlit.queryContents({
  
    types: [ContentTypes.Email]
  });

// Search emails by keyword
const searchEmails = await graphlit.queryContents({
  search: "quarterly report",
  
    types: [ContentTypes.Email]
  });

// Recent emails
const recentEmails = await graphlit.queryContents({
  
    types: [ContentTypes.Email],
    createdInLast: 'P7D'  // Last 7 days
  });

// Emails from specific sender (keyword search)
const fromSender = await graphlit.queryContents({
  search: "kirk@graphlit.com",
  searchType: SearchTypes.Keyword,
  
    types: [ContentTypes.Email]
  });

// Emails with attachments
const withAttachments = await graphlit.queryContents({
  
    types: [ContentTypes.Email]
  });

// Filter client-side for attachments
const hasAttachments = withAttachments.contents.results.filter(
  email => email.email && email.email.attachmentCount > 0
);

console.log(`Found ${hasAttachments.length} emails with attachments`);
```

### Query Patterns

#### 1. Find by Sender

```typescript
// Search by sender email
const fromKirk = await graphlit.queryContents({
  search: "kirk@graphlit.com",
  searchType: SearchTypes.Keyword,
  
    types: [ContentTypes.Email]
  });

// Search by sender name
const fromName = await graphlit.queryContents({
  search: "Kirk Marple",
  searchType: SearchTypes.Keyword,
  
    types: [ContentTypes.Email]
  });

// Access sender details
fromKirk.contents.results.forEach(email => {
  if (email.email && email.email.from) {
    console.log(`From: ${email.email.from[0].name} <${email.email.from[0].email}>`);
    console.log(`Subject: ${email.email.subject}`);
  }
});
```

#### 2. Find by Subject

```typescript
// Search in subject
const subjectSearch = await graphlit.queryContents({
  search: "quarterly earnings report",
  
    types: [ContentTypes.Email]
  });

// Exact subject match
const exactSubject = await graphlit.queryContents({
  search: '"Q4 2024 Earnings"',
  searchType: SearchTypes.Keyword,
  
    types: [ContentTypes.Email]
  });
```

#### 3. Find by Recipient

```typescript
// Search by recipient
const toMaria = await graphlit.queryContents({
  search: "maria@example.com",
  searchType: SearchTypes.Keyword,
  
    types: [ContentTypes.Email]
  });

// Check if specific person is in TO/CC/BCC
const allEmails = await graphlit.queryContents({
  
    types: [ContentTypes.Email]
  });

const toRecipient = allEmails.contents.results.filter(email => {
  if (!email.email) return false;
  
  const allRecipients = [
    ...(email.email.to || []),
    ...(email.email.cc || []),
    ...(email.email.bcc || [])
  ];
  
  return allRecipients.some(r => r.email === "maria@example.com");
});

console.log(`Found ${toRecipient.length} emails to maria@example.com`);
```

#### 4. Filter by Labels (Gmail)

```typescript
// Get emails with specific label
const emails = await graphlit.queryContents({
  
    types: [ContentTypes.Email]
  });

// Filter by Gmail label
const important = emails.contents.results.filter(email =>
  email.email?.labels?.includes('IMPORTANT')
);

const unread = emails.contents.results.filter(email =>
  email.email?.labels?.includes('UNREAD')
);

const starred = emails.contents.results.filter(email =>
  email.email?.labels?.includes('STARRED')
);

console.log(`Important: ${important.length}`);
console.log(`Unread: ${unread.length}`);
console.log(`Starred: ${starred.length}`);
```

#### 5. Date Range Queries

```typescript
// Emails from last month
const lastMonth = await graphlit.queryContents({
  
    types: [ContentTypes.Email],
    createdInLast: 'P30D'
  });

// Emails in specific date range
const dateRange = await graphlit.queryContents({
  
    types: [ContentTypes.Email],
    creationDateRange: {
      from: '2024-01-01T00:00:00Z',
      to: '2024-12-31T23:59:59Z'
    }
  });

// Q4 2024 emails
const q4 = await graphlit.queryContents({
  
    types: [ContentTypes.Email],
    creationDateRange: {
      from: '2024-10-01',
      to: '2024-12-31'
    }
  });
```

#### 6. Thread Queries

```typescript
// Get all emails
const emails = await graphlit.queryContents({
  
    types: [ContentTypes.Email]
  
  limit: 100
});

// Group by thread
const threads = new Map<string, any[]>();

emails.contents.results.forEach(email => {
  if (email.email?.threadIdentifier) {
    const threadId = email.email.threadIdentifier;
    if (!threads.has(threadId)) {
      threads.set(threadId, []);
    }
    threads.get(threadId)?.push(email);
  }
});

console.log(`Found ${threads.size} email threads`);

// Find longest threads
const sortedThreads = Array.from(threads.entries())
  .sort((a, b) => b[1].length - a[1].length);

console.log('\nTop 5 longest threads:');
sortedThreads.slice(0, 5).forEach(([threadId, emails]) => {
  console.log(`Thread ${threadId}: ${emails.length} emails`);
  console.log(`  Subject: ${emails[0].email?.subject}`);
});
```

#### 7. With Attachments

```typescript
// Query all emails
const emails = await graphlit.queryContents({
  
    types: [ContentTypes.Email]
  });

// Filter by attachment count
const withAttachments = emails.contents.results.filter(email =>
  email.email && email.email.attachmentCount > 0
);

const multipleAttachments = emails.contents.results.filter(email =>
  email.email && email.email.attachmentCount > 1
);

console.log(`With attachments: ${withAttachments.length}`);
console.log(`Multiple attachments: ${multipleAttachments.length}`);

// List emails with most attachments
const sorted = emails.contents.results
  .filter(email => email.email && email.email.attachmentCount > 0)
  .sort((a, b) => (b.email?.attachmentCount || 0) - (a.email?.attachmentCount || 0));

console.log('\nTop 5 emails by attachment count:');
sorted.slice(0, 5).forEach(email => {
  console.log(`${email.name}: ${email.email?.attachmentCount} attachments`);
});
```

#### 8. Newsletter Detection

```typescript
// Find newsletters
const emails = await graphlit.queryContents({
  
    types: [ContentTypes.Email]
  });

const newsletters = emails.contents.results.filter(email =>
  email.email?.publicationName || email.email?.unsubscribeUrl
);

console.log(`Found ${newsletters.length} newsletters`);

// Group by publication
const byPublication = new Map<string, number>();
newsletters.forEach(email => {
  const pub = email.email?.publicationName || 'Unknown';
  byPublication.set(pub, (byPublication.get(pub) || 0) + 1);
});

console.log('\nNewsletters by publication:');
Array.from(byPublication.entries())
  .sort((a, b) => b[1] - a[1])
  .forEach(([pub, count]) => {
    console.log(`  ${pub}: ${count}`);
  });
```

### Sample Reference

* `Graphlit_2024_09_07_Locate_Google_Emails_by_Person.ipynb`
* `Graphlit_2024_12_09_Locate_Microsoft_Emails_by_Organization.ipynb`

## Query emails

emails = await graphlit.queryContents( filter=ContentFilterInput( types=\[ContentTypes.Email] ) )

## By sender

from\_kirk = await graphlit.queryContents( search="<kirk@graphlit.com>", search\_type=SearchTypes.Keyword, filter=ContentFilterInput( types=\[ContentTypes.Email] ) )

## Recent emails

recent = await graphlit.queryContents( filter=ContentFilterInput( types=\[ContentTypes.Email], created\_in\_last='P7D' ) )

## Access metadata

for email in emails.contents.results: if email.email: print(f"From: {email.email.from\_\[0].email}") print(f"Subject: {email.email.subject}")

````

**C#**:
```csharp
using Graphlit;

var client = new Graphlit();

// Query emails
var emails = await graphlit.QueryContents(new ContentFilter
{
    Types = new[] { ContentTypes.Email }
});

// By sender
var fromKirk = await graphlit.QueryContents(new ContentFilter
{
    Search = "kirk@graphlit.com",
    SearchType = SearchKeyword,
    Filter = new ContentCriteria
    {
        Types = new[] { ContentTypes.Email }
    }
});

// Recent emails
var recent = await graphlit.QueryContents(new ContentFilter
{
    Filter = new ContentCriteria
    {
        Types = new[] { ContentTypes.Email },
        CreatedInLast = "P7D"
    }
});

// Access metadata
foreach (var email in emails.Contents.Results)
{
    if (email.Email != null)
    {
        Console.WriteLine($"From: {email.Email.From[0].Email}");
        Console.WriteLine($"Subject: {email.Email.Subject}");
    }
}
````

### Developer Hints

#### Email Addresses are Searchable

```typescript
// Keyword search works for email addresses
const results = await graphlit.queryContents({
  search: "kirk@graphlit.com",
  searchType: SearchTypes.Keyword,
   types: [ContentTypes.Email] });
```

#### Labels are Gmail-Specific

```typescript
// Gmail uses labels
email.email?.labels  // ["INBOX", "IMPORTANT", "UNREAD"]

// Outlook uses folders (not in labels field)
// Check feed type to determine source
```

#### Thread Detection

```typescript
// Emails with same threadIdentifier are part of same conversation
if (email.email?.threadIdentifier) {
  console.log(`Part of thread: ${email.email.threadIdentifier}`);
}
```

### Common Issues & Solutions

**Issue**: Can't filter by specific label **Solution**: Query all emails, filter client-side

```typescript
const all = await graphlit.queryContents({
   types: [ContentTypes.Email] });

const important = all.contents.results.filter(
  e => e.email?.labels?.includes('IMPORTANT')
);
```

**Issue**: Want emails TO a specific person **Solution**: Search by email or filter client-side

```typescript
// Keyword search
await graphlit.queryContents({
  search: "maria@example.com",
   types: [ContentTypes.Email] });

// Or filter after query
const all = await graphlit.queryContents({
   types: [ContentTypes.Email] });

const toMaria = all.contents.results.filter(e =>
  e.email?.to?.some(r => r.email === "maria@example.com")
);
```

**Issue**: Need to count emails by sender **Solution**: Query and aggregate client-side

```typescript
const emails = await graphlit.queryContents({
   types: [ContentTypes.Email] });

const bySender = new Map<string, number>();
emails.contents.results.forEach(email => {
  const sender = email.email?.from[0]?.email || 'unknown';
  bySender.set(sender, (bySender.get(sender) || 0) + 1);
});
```

### Production Example

```typescript
async function analyzeEmails() {
  console.log('\n=== EMAIL ANALYSIS ===\n');
  
  // Get all emails
  const emails = await graphlit.queryContents({
    
      types: [ContentTypes.Email],
      createdInLast: 'P30D'  // Last 30 days
    
    limit: 500
  });
  
  console.log(`Total emails: ${emails.contents.results.length}`);
  
  // Analyze senders
  const senders = new Map<string, number>();
  emails.contents.results.forEach(email => {
    if (email.email?.from[0]) {
      const sender = email.email.from[0].email;
      senders.set(sender, (senders.get(sender) || 0) + 1);
    }
  });
  
  console.log(`\nTop 10 senders:`);
  Array.from(senders.entries())
    .sort((a, b) => b[1] - a[1])
    .slice(0, 10)
    .forEach(([sender, count]) => {
      console.log(`  ${sender}: ${count} emails`);
    });
  
  // Attachment statistics
  const withAttachments = emails.contents.results.filter(
    e => e.email && e.email.attachmentCount > 0
  );
  console.log(`\nEmails with attachments: ${withAttachments.length} (${(withAttachments.length / emails.contents.results.length * 100).toFixed(1)}%)`);
  
  // Thread analysis
  const threads = new Map<string, any[]>();
  emails.contents.results.forEach(email => {
    if (email.email?.threadIdentifier) {
      const tid = email.email.threadIdentifier;
      if (!threads.has(tid)) threads.set(tid, []);
      threads.get(tid)?.push(email);
    }
  });
  
  console.log(`\nThreads: ${threads.size}`);
  const threadLengths = Array.from(threads.values()).map(t => t.length);
  const avgThreadLength = threadLengths.reduce((a, b) => a + b, 0) / threadLengths.length;
  console.log(`Average thread length: ${avgThreadLength.toFixed(1)} emails`);
  
  // Labels (Gmail)
  const allLabels = new Set<string>();
  emails.contents.results.forEach(email => {
    email.email?.labels?.forEach(label => allLabels.add(label));
  });
  console.log(`\nUnique labels: ${allLabels.size}`);
  console.log(`Labels: ${Array.from(allLabels).join(', ')}`);
  
  // Newsletters
  const newsletters = emails.contents.results.filter(
    e => e.email?.publicationName
  );
  console.log(`\nNewsletters: ${newsletters.length}`);
}

await analyzeEmails();
```


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.graphlit.dev/api-guides/use-cases/content/content-email-metadata-queries.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
