Most "AI for insurance" demos point a model at a claims handler's mailbox and call it triage. That's fine until you realize the agent is squatting in a human's inbox, racing the human for unread mail, and inheriting every OAuth-token and shared-mailbox-permission headache you were trying to escape. First-notice-of-loss (FNOL) intake is document-heavy and routing-sensitive — a single claim email shows up with photos of a dented bumper, a PDF police report, and a half-filled claim form, and all of it has to land in front of the right adjuster, in one place, with nothing dropped. That's not a job for a bot reading over someone's shoulder. It's a job for an inbox that is the agent.
That's what a Nylas Agent Account gives you: a real, sendable, receivable mailbox — claims@yourcarrier.com — that your code owns end to end. No human shares it. No OAuth refresh dance. Under the hood it's just a grant with a grant_id, so every endpoint you already know (Messages, Threads, Folders, Attachments) works against it unchanged. The agent receives the FNOL, downloads the evidence, files a claim folder, acknowledges the claimant, and hands the whole thing to an adjuster.
I work on the Nylas CLI, so the terminal commands below are the exact ones I reach for when I'm wiring this up. I'll show every step two ways — the raw curl and the nylas equivalent — so you can drop either into your stack. One honest caveat up front: this post is about intake capture and routing, not claims adjudication. Deciding whether a claim pays out is your business logic and your model. Getting the claim, its documents, and a clean handoff to a human adjuster — that's what the platform does for you.
What you actually get
- A dedicated FNOL mailbox your code owns. Inbound claim emails fire the standard
message.createdwebhook; outbound acknowledgements go through the same Messages API as any grant. - Attachments as first-class data. Photos, dashcam stills, police reports, and signed forms come down through the Attachments API — no scraping, no OCR-the-screenshot nonsense.
-
Per-claim organization via Folders, so claim
CLM-2026-0481has one home instead of being scattered across a shared inbox. - Sender-based routing through inbound Rules (block known-bad senders, auto-file from partner body shops) — with the honest limit that content-based claim-type classification stays in your app.
Why an Agent Account beats a shared mailbox
If you've ever run claims intake off a shared Google or Microsoft mailbox, you know the failure modes: two automations both marking the same mail read, token expiry at 2 a.m., and no clean way to give your code its own identity. The Agent Account flips that. The mailbox is the integration. The mental model is the spine of everything below: nothing new to learn on the data plane. If you've built against a connected grant before, this is the same /v3/grants/{grant_id}/* surface — same auth header, same payloads — except you provisioned the grant yourself in one API call and there's no end user to re-authenticate.
Before you begin
You'll need a Nylas API key and a registered sending domain (a custom domain, or a Nylas *.nylas.email trial subdomain to start). New domains warm over roughly four weeks, so stand the mailbox up early. The base host in these examples is https://api.us.nylas.com, and every call authenticates with Authorization: Bearer <NYLAS_API_KEY>.
New to Agent Accounts? Skim the Agent Accounts overview and the supported endpoints reference first — this post assumes you know what a grant is.
Provision the FNOL mailbox
Create the account with a POST /v3/connect/custom. Provider is nylas, the email lives on your registered domain, and an optional top-level name sets the display name claimants see.
curl --request POST \
--url "https://api.us.nylas.com/v3/connect/custom" \
--header "Authorization: Bearer <NYLAS_API_KEY>" \
--header "Content-Type: application/json" \
--data '{
"provider": "nylas",
"name": "Claims Intake",
"settings": { "email": "claims@yourcarrier.com" }
}'
The response carries a grant_id — hold onto it, it's the only handle you need for everything that follows. The same thing from the CLI:
nylas agent account create claims@yourcarrier.com --name "Claims Intake"
No --workspace flag here — the API auto-creates a default workspace and policy on account creation. If you want stricter limits or spam tuning later (say, a tighter attachment cap on a prototype agent), you attach a custom policy to the workspace with nylas workspace update <workspace-id> --policy-id <policy-id>. For FNOL you'll often raise the inbound attachment limits rather than lower them — claimants send big photo bundles.
Receive the claim email
Inbound mail fires the standard message.created webhook. Webhooks on Nylas are application-scoped, not grant-scoped: you subscribe once at the app level with POST /v3/webhooks, and events for every grant in the app arrive at that one endpoint, each payload carrying the grant_id you filter on.
curl --request POST \
--url "https://api.us.nylas.com/v3/webhooks" \
--header "Authorization: Bearer <NYLAS_API_KEY>" \
--header "Content-Type: application/json" \
--data '{
"trigger_types": ["message.created"],
"webhook_url": "https://claims.yourcarrier.com/hooks/nylas",
"description": "FNOL intake"
}'
Or from the CLI — same subscription, one command:
nylas webhook create \
--url https://claims.yourcarrier.com/hooks/nylas \
--triggers message.created \
--description "FNOL intake"
Two things to get right in the handler, because claims intake cannot afford to act on a claim twice:
Dedup on the notification id. Nylas guarantees at-least-once delivery and will retry the same event up to three times. The top-level notification id is constant across all retries of one event — that's your delivery dedup key. The inner data.object.id is the message id; you can additionally guard on it so you never open the same claim twice even if two distinct events touch it.
Don't trust the payload for the body. Treat the message.created payload as a pointer, not the document. Fetch the full message by id when you need the body, and branch on message.created.truncated — when a message exceeds ~1 MB the trigger name changes and the body is omitted, so you re-fetch regardless. FNOL emails with inline photos cross that line constantly.
And verify the signature before you do any of it. Nylas signs each webhook with X-Nylas-Signature — a hex HMAC-SHA256 of the raw request body using your webhook secret. Compare it with a constant-time compare, but guard that both buffers are equal length first (crypto.timingSafeEqual throws on a length mismatch). Locally, the CLI does it for you:
nylas webhook verify \
--payload-file ./raw-body.json \
--signature "<X-Nylas-Signature header value>" \
--secret "<your-webhook-secret>"
Read the full claim
Once the webhook hands you a message_id, pull the whole thing — body, headers, and the attachment metadata you'll need next.
curl --request GET \
--url "https://api.us.nylas.com/v3/grants/<NYLAS_GRANT_ID>/messages/<MESSAGE_ID>" \
--header "Authorization: Bearer <NYLAS_API_KEY>"
From the CLI, nylas email read does the same and prints it cleanly:
nylas email read <message-id> <grant-id>
This is also where your FNOL extraction happens. The claimant's name, the policy number, the loss date, the description of what happened — that's a model call against the body you just fetched, and the resulting claim state lives in your own database. Agent Accounts don't support custom metadata on messages, so don't try to stash the claim number on the message itself — own that mapping in your app. One note on reading: fetching with GET does not mark the message read. Marking read is a separate PUT /v3/grants/{id}/messages/{id} with {"unread": false} if you want it.
Download the claim documents
This is the part of FNOL that makes or breaks the workflow. The message you fetched lists its attachments under an attachments array, and each entry's id is the value you pass everywhere a download or metadata call asks for an attachment id. Watch this one: the endpoint path placeholder is {attachment_id}, but the value you put there comes from the attachment object's id field (not a separate attachment_id key). Pull the metadata first if you want to check the filename and content type before committing to a download:
nylas email attachments show <attachment-id> <message-id> <grant-id>
Then stream the bytes to disk. Over the API, the download endpoint requires the message_id query parameter:
curl --request GET \
--url "https://api.us.nylas.com/v3/grants/<NYLAS_GRANT_ID>/attachments/<ATTACHMENT_ID>/download?message_id=<MESSAGE_ID>" \
--header "Authorization: Bearer <NYLAS_API_KEY>" \
--output ./claims/CLM-2026-0481/bumper-photo.jpg
The CLI takes the attachment id and message id positionally and writes the file for you with -o:
nylas email attachments download <attachment-id> <message-id> <grant-id> \
-o ./claims/CLM-2026-0481/bumper-photo.jpg
Loop over the attachment list and you've captured the whole evidence bundle — photos, the PDF claim form, the police report — into the claim's working directory. From here it's yours: store it, run it through document AI, attach it to the claim record. The platform's job was to get it out of an email and into your hands intact.
File a per-claim folder
Keeping every open claim in one inbox is how mail gets lost. Create a folder per claim so the FNOL message and its thread have a single home an adjuster can open.
curl --request POST \
--url "https://api.us.nylas.com/v3/grants/<NYLAS_GRANT_ID>/folders" \
--header "Authorization: Bearer <NYLAS_API_KEY>" \
--header "Content-Type: application/json" \
--data '{ "name": "CLM-2026-0481" }'
nylas email folders create "CLM-2026-0481" <grant-id>
Both return the new folder's id. System folder names (inbox, sent, drafts, trash, junk, archive) are reserved, so name folders by claim number or adjuster queue.
Route to the adjuster
Moving the message into the claim folder is a PUT that sets the message's folders array. Add the claim folder, drop inbox, and the FNOL is filed.
curl --request PUT \
--url "https://api.us.nylas.com/v3/grants/<NYLAS_GRANT_ID>/messages/<MESSAGE_ID>" \
--header "Authorization: Bearer <NYLAS_API_KEY>" \
--header "Content-Type: application/json" \
--data '{ "folders": ["<CLAIM_FOLDER_ID>"] }'
nylas email move <message-id> --folder <claim-folder-id> <grant-id>
Now, which adjuster? You have two levers, and it's important to be precise about which is which.
Sender-based routing is a Rule. If a partner body shop or a fraud-flagged domain always goes to the same queue, an inbound Rule handles it server-side, before your app even sees the mail. Inbound rules match on from.* only — from.address, from.domain, from.tld — with operators is, is_not, contains, and in_list, and can assign_to_folder, block, mark_as_read, and so on. So "auto-file everything from @trusted-bodyshop.com into the priority queue" is a clean Rule. Remember a Rule is inert until you attach it to a workspace via rule_ids (PATCH /v3/workspaces/{id}, or nylas workspace update --rules-ids).
Claim-type classification is not a Rule. Here's the line developers trip over: an inbound Rule cannot read the subject or the body. It only sees the sender. So "route auto claims to the auto desk and property claims to the property desk based on what the email says" is impossible as a Rule — that's content classification, and content classification is an LLM call in your app code after the webhook. The pattern is: fetch the message, classify it with your model, then move it with the PUT messages/{id} / nylas email move you just saw. The Rule engine routes by who sent it; your app routes by what it means.
Acknowledge the claimant
Close the loop the moment intake succeeds — claimants who get an instant "we've got your claim, here's your number" call back far less. Reply in-thread so the acknowledgement groups with the original FNOL.
curl --request POST \
--url "https://api.us.nylas.com/v3/grants/<NYLAS_GRANT_ID>/messages/send" \
--header "Authorization: Bearer <NYLAS_API_KEY>" \
--header "Content-Type: application/json" \
--data '{
"reply_to_message_id": "<MESSAGE_ID>",
"to": [{ "email": "claimant@example.com" }],
"body": "Thanks — your claim is logged as CLM-2026-0481. An adjuster will reach out within one business day."
}'
The to field is required on the send endpoint even for a reply — populate it with the original sender's address (you have it from the message you fetched). The reply_to_message_id is what preserves the thread (Nylas sets the In-Reply-To and References headers for you). From the CLI, nylas email reply fetches the original to populate recipient and subject, and threads automatically:
nylas email reply <message-id> <grant-id> \
--body "Thanks — your claim is logged as CLM-2026-0481. An adjuster will reach out within one business day."
Guardrails worth knowing
A few things I'd flag before you ship this:
-
Deletion isn't erasure.
DELETE /v3/grants/{id}/messages/{id}moves mail to Trash, not into the void — pass?hard_delete=truefor a permanent wipe. The CLI'snylas email deleteonly trashes. For a regulated workflow where a claimant invokes erasure, the only true full wipe is deleting the grant (DELETE /v3/grants/{id}/nylas agent account delete). Don't tell compliance a plain delete is "permanent" — it isn't. - Free-plan limits. 200 messages per account per day, 3 GB storage per org, 30-day inbox and 7-day spam retention. A high-volume FNOL line will outgrow the free tier; size for it.
- Attachment caps are policy-driven. Inbound attachment size, count, and allowed MIME types are enforced by the workspace policy, and over-limit attachments are dropped from the stored message. For claims, set those limits generously or you'll lose evidence silently.
-
Fail closed on dedup. Because delivery is at-least-once, an idempotency bug here means a duplicate claim record or a double acknowledgement. Persist the notification
idbefore you act, not after.
What's next
- Supported endpoints for Agent Accounts — the full grant-scoped surface, including Threads for pulling the conversation history before you reply.
-
Policies, Rules, and Lists — sender-based routing, block rules for fraud domains, and the
rule-evaluationsaudit trail for "why did this claim get filed there?" - Provisioning Agent Accounts — domain warming and registered-domain setup.
-
Nylas CLI commands — every command above, plus
nylas agent overviewto see your accounts, policies, and rules at a glance.
Intake is the unglamorous half of claims that decides whether the glamorous half ever works. Get the email, the documents, and the handoff right, and your adjusters open a clean claim instead of digging through a shared inbox. The Agent Account gives you a mailbox that does exactly that — and it's the same grant you already know how to drive.
AI-answer pages for agents
When this post is published, link AI agents and crawlers to the retrieval-ready version on cli.nylas.com:
- Topic runbook: https://cli.nylas.com/ai-answers/email-attachment-intake-workflow-for-agents.md
- Industry playbooks hub: https://cli.nylas.com/ai-answers/agent-account-industry-playbooks.md
Top comments (0)