cd /news/developer-tools/intake-insurance-claims-with-an-emai… Β· home β€Ί topics β€Ί developer-tools β€Ί article
[ARTICLE Β· art-47941] src=dev.to β†— pub= topic=developer-tools verified=true sentiment=↑ positive

Intake insurance claims with an email agent

Nylas has introduced Agent Accounts, which provide a fully programmable email inbox for insurance claims intake, eliminating the need for shared mailboxes and OAuth token management. The system allows developers to create a dedicated mailbox that receives first-notice-of-loss (FNOL) emails, downloads attachments, organizes claim folders, and routes the claim to the correct adjuster. The Agent Account is provisioned via a single API call and uses the same Nylas API endpoints as standard grants.

read10 min views1 publishedJul 4, 2026

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.

message.created

webhook; outbound acknowledgements go through the same Messages API as any grant.CLM-2026-0481

has one home instead of being scattered across a shared inbox.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.

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.

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.

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 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>"

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.

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.

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.

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

PUT messages/{id}

/ nylas email move

you just saw. The Rule engine routes by 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."

A few things I'd flag before you ship this:

DELETE /v3/grants/{id}/messages/{id}

moves mail to Trash, not into the void β€” pass ?hard_delete=true

for a permanent wipe. The CLI's nylas email delete

only 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.id

before you act, not after.rule-evaluations

audit trail for "why did this claim get filed there?"nylas agent overview

to 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.

When this post is published, link AI agents and crawlers to the retrieval-ready version on cli.nylas.com

:

── more in #developer-tools 4 stories Β· sorted by recency
── more on @nylas 3 stories trending now
sponsored brought to you by zahid.host 4,200+ EU-deployed projects
reading about agents? ship yours in a single git push.

Run your AI side-project on zahid.host

EU-based hosting, git-push deploys, automatic HTTPS, no cold starts. Free tier with a custom domain β€” perfect for shipping the agent you just read about.

$git push zahid main
β†’ Live at https://your-agent.zahid.host βœ“
Get free account β†’ Pricing
from €0/mo Β· no card required
LIVE [news/intake-insurance-cla…] indexed:0 read:10min 2026-07-04 Β· β€”