Qualify real estate leads with an email agent Nylas introduces Agent Accounts, which provide dedicated email addresses and calendars for autonomous real estate lead qualification. The system uses webhooks to trigger immediate responses to property inquiries, enabling agents to book showings without human intervention. A developer demonstrates how to create an agent account via the Nylas CLI and API. A property inquiry has a half-life measured in minutes. Someone is scrolling listings at 11pm, they fill out the "request more info" form on the three-bedroom on Maple, and they fire the same form off to four other listings on the same street. Whichever agent replies first — with answers, not a "thanks, I'll be in touch" — is the one who gets the showing. By the time a human checks email at 9am, that lead has already toured two other houses in their head. Most "AI for real estate" tools try to fix this by bolting a chatbot onto a website, or by pointing a model at the agent's personal Gmail and hoping it doesn't reply to the agent's mom. Both are awkward. The first never sees the email leads that come in through Zillow or a portal; the second makes the model a creepy ghostwriter inside a human's private inbox. What you actually want is for the listing inquiries to land somewhere that is the agent — an address that can read the question, qualify the buyer, check its own calendar, and put a showing on it, autonomously, at 11pm, without a human awake. That's a Nylas Agent Account : a grant with its own email address and its own calendar. It's a real participant, not a bot reading over a human's shoulder. I work on the Nylas CLI, so the terminal commands below are the exact ones I reach for when I'm prototyping one of these loops — and I'll show the raw API call next to each one, because the CLI is just a friendly wrapper over the same /v3/grants/{grant id}/... endpoints. Strip the AI out and a real estate lead loop is four moves: Three of those four moves map straight onto Nylas primitives — inbound mail, in-thread reply, calendar event. The fourth — deciding whether a lead is qualified and what to ask next — is your application code. Nylas hands you the email text; your LLM extracts "pre-approved up to $650k, wants to move by September" and decides the lead is ready to book. I'll be honest about that boundary the whole way through, because pretending the API does the reasoning is exactly the demo magic that falls apart in production. The nice thing is the data plane never changes. An Agent Account is just a grant with a grant id . If you've used the Nylas email and calendar APIs, you already know every verb below. Nothing new to learn on the data plane. One thing to get out of the way first, because it's the most common wrong turn: Scheduler is not available for Agent Accounts. No booking pages, no availability-config API, no /v3/scheduling/ . That door is locked for this provider. What you have instead is the grant's own free/busy plus the Events API — and that's enough to book the showing yourself. You need: POST /v3/connect/custom with "provider": "nylas" and an email on a registered domain — no refresh token, no OAuth dance: 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": "Maple Realty Listings", "settings": { "email": "listings@yourbrokerage.com" } }' Or, from the CLI: nylas agent account create listings@yourbrokerage.com --name "Maple Realty Listings" grant id , and a Nylas API key exported as NYLAS API KEY . message.created webhook so the agent reacts within seconds of an inquiry — that responsiveness is the whole selling point. thread id .New to all this? Read What are Agent Accounts https://developer.nylas.com/docs/v3/agent-accounts/ first. All API examples use the US base host https://api.us.nylas.com and a bearer token — swap in api.eu.nylas.com for the EU region. Inbound mail to the agent fires the standard message.created webhook. Webhooks are application-scoped, not grant-scoped — you subscribe once at the app level with POST /v3/webhooks , and events for every grant arrive at that one endpoint. Each payload carries a grant id you filter on, so an app running ten listing agents still has a single webhook URL. 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://leads.yourbrokerage.com/webhooks/nylas", "description": "Listing inquiry handler" }' The CLI does the same in one line: nylas webhook create \ --url "https://leads.yourbrokerage.com/webhooks/nylas" \ --triggers message.created \ --description "Listing inquiry handler" Two things to bake into the handler before you do anything clever: id . id is constant across all retries of one event, so that's your delivery dedup key. Drop it in a seen notifications set with a TTL and bail early on repeats. You can additionally guard on the inner data.object.id , the message id, so you never act twice on the same message even across distinct events. GET /v3/grants/{grant id}/messages/{message id} , and branch on message.created.truncated — that variant fires for large messages and never includes the body, so re-fetch. // Express handler — verify X-Nylas-Signature first, then: app.post "/webhooks/nylas", async req, res = { res.status 200 .end ; // ack fast; Nylas retries on non-2xx const event = req.body; if event.type == "message.created" && event.type == "message.created.truncated" return; if event.data.object.grant id == AGENT GRANT ID return; // Delivery dedup: notification id is constant across retries. if await seen event.id return; await markSeen event.id ; await handleInquiry event.data.object ; // {id, thread id, ...} — fetch body next } ; The webhook gave you summary fields and a thread id . To extract budget and intent, you need the body — fetch it by id: curl --request GET \ --url "https://api.us.nylas.com/v3/grants/