Require human approval before your agent sends email Nylas developer advocate builds an approval queue for AI email agents using the Drafts API. Instead of letting an LLM send email directly, the agent creates a draft that a human must review and approve before it is sent byte-for-byte unchanged. The system uses Nylas Agent Accounts as the mailbox for the agent, with the draft serving as a stable, persisted object that eliminates race conditions between approval and sending. Most "AI email agent" demos end with a triumphant send . The model writes a reply, the code POSTs it, and a real message lands in a real stranger's inbox. That's a great demo and a terrible production default. The moment your agent can send mail with nobody watching, you've handed an LLM a corporate email address and the standing authority to use it. One hallucinated price, one confidently wrong refund promise, one apology to the wrong customer, and you're explaining to legal why a bot signed an email as your company. There's a boring, durable fix that predates AI by decades: don't send — draft. Stage the message, put a human in front of it, and only send once someone with a name and a pulse approves. Email systems have had a "Drafts" folder forever for exactly this reason. The Nylas Drafts API turns that folder into something better — an approval queue your agent writes into and your reviewers drain. This post builds that queue. The agent creates a draft, a human reviews the pending drafts, and an approved draft gets sent byte-for-byte unchanged . No re-rendering, no "the agent regenerates it on approval" race where the thing you approved isn't the thing that ships. I work on the Nylas CLI, so the terminal commands below are the exact ones I reach for, and I'll pair every one with the raw curl so you can wire it into a backend in whatever language you like. This is deliberately not about escalating inbound threads to a human that's a different problem, where the trigger is a message arriving . Here the trigger is the agent wanting to send , and the gate sits on the outbound path. You could build approval a dozen ways. You could buffer the agent's output in a queue table and call send later. You could stash a JSON blob in Redis. Both work, and both quietly reinvent something the email stack already gives you. A draft is a real, persisted email object , on the mailbox, with a stable id . That buys you three things a homegrown buffer doesn't: id . You don't reassemble recipients, subject, body, and attachments from a buffer and hope you got it right — the object you approved is the object that sends.That last point is the whole game. The contract you want is "what the human saw is what went out." A draft gives you that for free, because the send action references the stored draft rather than re-supplying the content. Everything below runs against a grant — a grant id that represents one mailbox. If you've used Nylas with a connected Gmail or Microsoft account, an Agent Account https://developer.nylas.com/docs/v3/agent-accounts/ is the same thing: a grant. Same /v3/grants/{grant id}/ endpoints, same auth header, same payloads. There's nothing new to learn on the data plane. The difference is provisioning — an Agent Account is a Nylas-hosted mailbox you create on demand, with no OAuth dance and no human to click "Allow." That matters here because the mailbox is the agent. When the agent drafts from support@yourapp.nylas.email , the draft sits in that account's own Drafts folder, and the eventual send comes from that address. The reviewer is approving mail "from the bot," which is exactly the identity you want under human control. One honest caveat up front, because it shapes the design: Agent Accounts don't support custom metadata. You can't tag a draft with {"approval status": "pending"} and filter on it server-side. So the You need: NYLAS API KEY . .nylas.email trial subdomain. New domains warm up over roughly four weeks, so provision early. nylas init once to store your API key.Examples use the US data region host https://api.us.nylas.com . Swap in api.eu.nylas.com if your app lives in the EU. The mailbox the agent drafts from is an Agent Account. Create one with POST /v3/connect/custom , using the built-in nylas provider and an address on your domain: 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": "Support Bot", "settings": { "email": "support@yourapp.nylas.email" } }' The response includes the grant id you'll use for everything else. The API also auto-creates a default workspace and policy for the account, so there's no extra setup to send mail. The CLI collapses all of that into one command: nylas agent account create support@yourapp.nylas.email --name "Support Bot" No --workspace flag exists on create, and there's no refresh token to manage — that's the OAuth-free part. If you later want to attach a stricter custom policy say, to cap inbound size or block recipients , you do that against the auto-created workspace with nylas workspace update