Guide
Parse and Extract Data from Emails in PowerShell
According to a 2023 McKinsey report, knowledge workers spend 28% of their workday reading and responding to email. Much of that is extracting data -- order confirmations, ticket numbers, shipping updates -- and typing it into another system. This guide shows how to automate that with PowerShell and Nylas CLI. Read emails, extract structured data with regex, process orders and tickets, and build inbox monitoring scripts. Works with Gmail, Outlook, Exchange, Yahoo, iCloud, and IMAP.
By Hazik
Setup
Install Nylas CLI and authenticate. See the PowerShell email guide for full install options.
irm https://cli.nylas.com/install.ps1 | iex
nylas auth config # paste your API keyExtract order numbers from confirmation emails
E-commerce platforms send order confirmations with order IDs, amounts, and item details. This script finds order emails and extracts the structured data.
# extract-orders.ps1 -- Parse order confirmations
$orderEmails = nylas email search "order confirmation" --json --limit 20 |
ConvertFrom-Json
$orders = $orderEmails | ForEach-Object {
$detail = nylas email read $_.id --json | ConvertFrom-Json
$body = $detail.body
# Extract order number (patterns: #12345, Order-12345, ORD-12345)
$orderNum = if ($body -match '(?:order|#|ORD)[-\s]?(\d{4,10})') {
$matches[1]
} else { "unknown" }
# Extract total amount ($123.45 or USD 123.45)
$amount = if ($body -match '(?:total|amount|charged)[\s:]*\$?([\d,]+\.?\d{0,2})') {
$matches[1]
} else { "unknown" }
# Extract tracking number if present
$tracking = if ($body -match '(?:tracking|shipment)[\s:#]*([A-Z0-9]{10,25})') {
$matches[1]
} else { $null }
[PSCustomObject]@{
Date = $_.date
From = $_.from
OrderNum = $orderNum
Amount = $amount
Tracking = $tracking
Subject = $_.subject
}
}
$orders | Format-Table Date, OrderNum, Amount, Tracking -AutoSize
$orders | Export-Csv "orders-extracted.csv" -NoTypeInformation
Write-Host "Extracted $($orders.Count) orders"Parse support ticket notifications
Help desks like Zendesk, Freshdesk, and Jira send structured email notifications. Extract ticket IDs, priorities, and assignees to build a local tracking dashboard.
# parse-tickets.ps1 -- Extract ticket data from help desk emails
$ticketEmails = nylas email search "from:notifications@yourcompany.zendesk.com" `
--json --limit 50 | ConvertFrom-Json
$tickets = $ticketEmails | ForEach-Object {
$detail = nylas email read $_.id --json | ConvertFrom-Json
$body = $detail.body
$subject = $_.subject
# Zendesk format: "[Ticket #12345] Subject here"
$ticketId = if ($subject -match '\[(?:Ticket\s)?#?(\d+)\]') {
$matches[1]
} else { "unknown" }
# Extract priority
$priority = if ($body -match '(?:priority|urgency)[\s:]*(\w+)') {
$matches[1]
} elseif ($subject -match '\b(urgent|high|critical)\b') {
$matches[1]
} else { "normal" }
# Extract status
$status = if ($body -match '(?:status)[\s:]*(\w+(?:\s\w+)?)') {
$matches[1]
} else { "open" }
[PSCustomObject]@{
TicketId = $ticketId
Subject = ($subject -replace '\[.*?\]\s*', '')
Priority = $priority
Status = $status
Date = $_.date
}
}
Write-Host "Ticket Summary:" -ForegroundColor Cyan
$tickets | Group-Object Priority | Sort-Object Count -Descending |
ForEach-Object { Write-Host " $($_.Name): $($_.Count)" }
$tickets | Format-Table TicketId, Priority, Status, Subject -AutoSizeContinuous inbox monitoring
Process new emails as they arrive by polling at regular intervals. This pattern is the foundation for email-driven automation.
# inbox-processor.ps1 -- Process new emails as they arrive
param([int]$PollIntervalSeconds = 30)
$seenIds = @{}
Write-Host "Monitoring inbox... (poll every $($PollIntervalSeconds)s)" -ForegroundColor Cyan
while ($true) {
try {
$emails = nylas email list --unread --json --limit 20 | ConvertFrom-Json
foreach ($email in $emails) {
if ($seenIds.ContainsKey($email.id)) { continue }
$detail = nylas email read $email.id --json | ConvertFrom-Json
# Route based on sender
switch -Regex ($email.from) {
'orders@' { Write-Host "ORDER: $($email.subject)" -ForegroundColor Green }
'alerts@' { Write-Host "ALERT: $($email.subject)" -ForegroundColor Red }
'zendesk.com' { Write-Host "TICKET: $($email.subject)" -ForegroundColor Yellow }
default { Write-Host "NEW: $($email.subject)" -ForegroundColor Gray }
}
$seenIds[$email.id] = $true
}
# Prevent unbounded memory growth
if ($seenIds.Count -gt 1000) {
$keep = $seenIds.Keys | Select-Object -Last 500
$seenIds = @{}
$keep | ForEach-Object { $seenIds[$_] = $true }
}
} catch {
Write-Host "Error: $_" -ForegroundColor Red
}
Start-Sleep -Seconds $PollIntervalSeconds
}Extract shipping tracking updates
Carrier emails from UPS, FedEx, and USPS contain tracking numbers and delivery estimates. Each carrier uses a different tracking number format.
# shipping-tracker.ps1 -- Extract tracking info from carrier emails
$carriers = @{
'ups' = @{ Pattern = '1Z[A-Z0-9]{16}'; Sender = '*@ups.com' }
'fedex' = @{ Pattern = '\b\d{12,22}\b'; Sender = '*@fedex.com' }
'usps' = @{ Pattern = '\b(9[234]\d{20,22})\b'; Sender = '*@usps.gov' }
}
$emails = nylas email list --json --limit 100 | ConvertFrom-Json
$shipments = foreach ($email in $emails) {
foreach ($carrier in $carriers.GetEnumerator()) {
if ($email.from -like $carrier.Value.Sender) {
$detail = nylas email read $email.id --json | ConvertFrom-Json
if ($detail.body -match $carrier.Value.Pattern) {
$deliveryDate = if ($detail.body -match '(?:deliver|expected).*?(\d{1,2}/\d{1,2}/\d{2,4})') {
$matches[1]
} else { "TBD" }
[PSCustomObject]@{
Carrier = $carrier.Key.ToUpper()
Tracking = $matches[0]
DeliveryEst = $deliveryDate
Subject = $email.subject
}
}
}
}
}
$shipments | Format-Table Carrier, Tracking, DeliveryEst, Subject -AutoSizeParse financial transaction emails
Bank alerts and payment processors send transaction notifications with amounts, merchant names, and account identifiers.
# financial-parser.ps1 -- Extract transaction data from bank emails
$bankEmails = nylas email search "from:alerts@yourbank.com" --json --limit 50 |
ConvertFrom-Json
$transactions = $bankEmails | ForEach-Object {
$detail = nylas email read $_.id --json | ConvertFrom-Json
$body = $detail.body
$amount = if ($body -match '\$\s?([\d,]+\.\d{2})') { $matches[1] -replace ',' }
else { $null }
$merchant = if ($body -match "(?:at|merchant|payee)[\s:]+([A-Za-z0-9\s&']+?)(?:\.|,|\n)") {
$matches[1].Trim()
} else { "Unknown" }
$type = if ($body -match 'debit|purchase|withdrawal') { "Debit" }
elseif ($body -match 'credit|deposit|refund') { "Credit" }
else { "Unknown" }
if ($amount) {
[PSCustomObject]@{
Date=$_.date; Type=$type; Amount=[decimal]$amount; Merchant=$merchant
}
}
}
$totalDebits = ($transactions | Where-Object { $_.Type -eq "Debit" } |
Measure-Object -Property Amount -Sum).Sum
Write-Host "Transactions: $($transactions.Count), Total debits: $totalDebits"
$transactions | Sort-Object Amount -Descending | Format-Table -AutoSizeRoute emails to different handlers
Many SaaS platforms send emails with consistent formats. Build a router that dispatches to different handlers based on sender or subject pattern.
# email-router.ps1 -- Route emails to processing handlers
$handlers = @{
'alerts@pagerduty.com' = {
param($email, $detail)
if ($detail.body -match 'Incident #(\d+)') {
Write-Host "PagerDuty incident #$($matches[1])" -ForegroundColor Red
}
}
'noreply@github.com' = {
param($email, $detail)
if ($email.subject -match '\[(.+?)\] (.+?) #(\d+)') {
Write-Host "GitHub: $($matches[1]) $($matches[2]) #$($matches[3])" -ForegroundColor Cyan
}
}
'forms@company.com' = {
param($email, $detail)
$fields = @{}
[regex]::Matches($detail.body, '(\w[\w\s]+?):\s*(.+?)(?:\n|$)') |
ForEach-Object { $fields[$_.Groups[1].Value.Trim()] = $_.Groups[2].Value.Trim() }
Write-Host "Form: $($fields.Count) fields" -ForegroundColor Yellow
$fields.GetEnumerator() | ForEach-Object { Write-Host " $($_.Key): $($_.Value)" }
}
}
$emails = nylas email list --unread --json --limit 30 | ConvertFrom-Json
foreach ($email in $emails) {
$handler = $handlers.GetEnumerator() |
Where-Object { $email.from -like "*$($_.Key)*" } | Select-Object -First 1
if ($handler) {
$detail = nylas email read $email.id --json | ConvertFrom-Json
& $handler.Value $email $detail
}
}Export parsed data to SQL Server
# email-to-sql.ps1 -- Parse emails and insert into database
$emails = nylas email search "order confirmation" --json --limit 50 |
ConvertFrom-Json
foreach ($email in $emails) {
$detail = nylas email read $email.id --json | ConvertFrom-Json
$orderNum = if ($detail.body -match 'order\s*#?\s*(\d{4,10})') { $matches[1] } else { continue }
$amount = if ($detail.body -match '\$([\d,]+\.\d{2})') { $matches[1] -replace ',' } else { "0" }
$query = "IF NOT EXISTS (SELECT 1 FROM Orders WHERE OrderNumber=@OrderNum) " +
"INSERT INTO Orders (OrderNumber,Amount,EmailDate) VALUES (@OrderNum,@Amount,@EmailDate)"
try {
Invoke-Sqlcmd -ServerInstance "db.company.local" -Database "OrderTracking" -Query $query -Variable "OrderNum=$orderNum","Amount=$amount","EmailDate=$($email.date)"
Write-Host "Inserted #$orderNum" -ForegroundColor Green
} catch {
Write-Host "Failed #$orderNum : $_" -ForegroundColor Red
}
}Frequently asked questions
How do I extract data from emails in PowerShell?
Use nylas email read <id> --json | ConvertFrom-Json to get the full email body. Then apply regex with -match to extract order numbers, tracking codes, amounts, or any structured data. PowerShell's $matches automatic variable captures the matched groups.
Can I process incoming emails automatically with PowerShell?
Yes. Build a polling loop that calls nylas email list --unread --json every 30-60 seconds. Track seen message IDs in a hashtable. Process new arrivals with your extraction logic. Schedule the script with Task Scheduler for persistent monitoring.
How do I parse HTML email bodies in PowerShell?
For simple extraction, use regex with -match on the raw HTML. For complex HTML, install HtmlAgilityPack from NuGet and query DOM elements. In most cases, regex is faster since you're extracting specific values, not navigating the full document tree.
Next steps
- Download email attachments in PowerShell -- filter by type, batch download, auto-save
- Generate and email reports -- CSV attachments, HTML tables, weekly digests
- Send email from PowerShell -- compose, reply, and send with attachments
- Full command reference -- every flag and subcommand