Guide
Send and Parse Email with One API
Most stacks bolt SendGrid onto a separate inbound-parse product. This guide wires both directions through one grant: nylas email send outbound, nylas email search for replies, one OAuth connection.
Written by Pouya Sanooei Software Engineer
Reviewed by Qasim Muhammad
Command references used in this guide: nylas email send, nylas email search, and nylas webhook create.
Why do sending and parsing email usually need two products?
Most email stacks split the two directions. A sending API such as SendGrid moves mail out over HTTP, but reading replies requires its separate Inbound Parse webhook — a distinct product with its own DNS setup. An email API that does send and parse together avoids that split: one reply loop, one integration, one auth system.
The split has real setup costs. SendGrid's Inbound Parse documentation requires an MX record at priority 10 pointing to mx.sendgrid.net, on a hostname that “should serve no other purpose other than parsing your incoming email.” Replies then arrive at your endpoint as a multipart/form-data POST your code unpacks field by field. None of that touches the mailbox people actually reply to — mail sent to your normal address still lands in Gmail or Outlook, governed by SMTP routing rules that date to RFC 5321 and its 1982 predecessor. A bidirectional email API reads that real mailbox instead, so there's no parsing subdomain to operate.
| Concern | Sending API + inbound parse | One bidirectional grant |
|---|---|---|
| Products to integrate | 2 (send + parse) | 1 |
| DNS setup | MX record on a dedicated subdomain | None |
| Inbound payload | multipart/form-data POST | Structured JSON fields |
| Reads the real mailbox | No | Yes |
| Auth systems | API key + webhook signing key | One OAuth grant |
How do I send email through an API that also reads the inbox?
The nylas email send command delivers mail over the same grant that lists, searches, and replies. One OAuth connection to Gmail or Outlook covers both directions, so there's no sending domain to verify and no API key per product. Setup is two commands and takes about 2 minutes.
Install with Homebrew and run nylas init to connect an account — other install methods are in the getting started guide. The OAuth token lands in your system keyring and refreshes automatically, so the connection survives unattended cron runs.
brew install nylas/nylas-cli/nylas
nylas init # browser OAuth, one timeSending takes one command. The --metadata flag attaches up to 50 key=value pairs to the outbound message, and --track-opens reports when the recipient opens it — useful when the reply you're waiting for never comes. The --yes flag skips the confirmation prompt for scripted runs, and --schedule 2h defers delivery without a queue of your own.
nylas email send --to customer@example.com \
--subject "Order 4812 update" \
--body "Your order shipped today. Reply with any questions." \
--metadata order=4812 --track-opens --yesHow do I parse replies with the same API?
The nylas email search command reads inbound mail on the grant that sent the original message. Filters cover sender, recipient, subject, folder, date range, and attachment status. The default page size is 20 results, and the CLI auto-paginates when --limit goes above 200.
There's no MIME to unpack. The search wraps each provider's native message API (the Gmail API for Google accounts, the Microsoft Graph message resource for Outlook), and --json returns sender, subject, date, and body as structured fields. Compare that with an inbound-parse payload, where your code splits a multipart POST into 15-plus form fields before it can read one address.
Attachments stay one command away too. When a reply carries files, nylas email attachments list shows them and nylas email attachments download saves them locally — the inbound-parse equivalent is base64-decoding form fields yourself. Folder scoping works with --in INBOX, and --has-attachment narrows a search to messages that actually carry files.
# Replies about order 4812 since June 1, newest first
nylas email search "Order 4812" --from customer@example.com \
--after 2026-06-01 --unread --json \
| jq '.[] | {from: .from[0].email, subject: .subject, date: .date}'How do I get pushed inbound email instead of polling?
The nylas webhook create command registers an HTTPS endpoint on the same application that sends. The message.created trigger fires for every new message on the grant, and thread.replied fires when a thread you sent gets an answer — no MX record changes and no polling loop.
Webhook payloads arrive as JSON matching the published Nylas notification schemas, and each delivery is signed so nylas webhook verify can check the signature before your handler trusts it. For local development, nylas webhook server --port 8080 runs a receiver on your machine. Run nylas webhook triggers to list every available trigger by category; the Message category alone has 8 triggers, covering creation, opens, link clicks, bounces, and send success or failure.
nylas webhook create --url https://example.com/hooks/email \
--triggers message.created --triggers thread.replied \
--description "Inbound mail for order workflows"
# Local testing without deploying anything
nylas webhook server --port 8080Two maintenance commands round out the inbound side. nylas webhook rotate-secret replaces the signing secret if it ever leaks, and nylas webhook verify --payload-file --signature replays a captured delivery to confirm your handler rejects forged payloads. Compared with a polling loop on a 5-minute cron, a push registration cuts reply latency from minutes to seconds.
How do I close the send-and-parse loop in one script?
A full send-and-parse round trip is a 15-line bash script on one grant: send with a searchable subject, find the unread reply, answer it with nylas email reply. Both directions ride one OAuth token, so the script needs zero stored credentials beyond the keyring entry nylas init created.
This is the script the TL;DR promised. The --quiet flag makes nylas email search print only message IDs, which feeds nylas email reply directly — no jq required. Schedule the second half in cron every 5 minutes and the loop runs unattended.
#!/usr/bin/env bash
# 1. Send the outbound message
nylas email send --to customer@example.com \
--subject "Order 4812 update" \
--body "Your order shipped today. Reply with any questions." --yes
# 2. Later (cron): grab the newest unread reply, if any
REPLY_ID=$(nylas email search "Order 4812" \
--from customer@example.com --unread --limit 1 --quiet)
# 3. Answer it on the same thread
if [ -n "$REPLY_ID" ]; then
nylas email reply "$REPLY_ID" \
--body "Thanks! A teammate will follow up within 1 hour." --yes
fiTested on Nylas CLI 3.1.17 against Gmail and Outlook grants. The same three commands work unchanged on either provider because the grant abstracts the underlying API — that's the whole argument for one email API doing send and parse instead of two products doing half each.
Next steps
- Receive inbound email from the CLI — the read side in depth: list, search, and read commands
- Parse inbound email with webhooks — build the push-based pipeline end to end
- Email API vs SMTP — why an HTTP API covers more than a send-only protocol
- Twilio SendGrid vs Nylas — the two-product stack compared head to head
- EmailEngine vs Nylas — self-hosted bidirectional email vs the hosted grant model
- Full command reference — every flag and subcommand documented