Writing a clear, well-structured email takes time, and it's the kind of task an LLM is genuinely good at. But wiring up your own prompt-to-email pipeline means picking a model, threading the original message in as context, handling streaming, and keeping it all behind your API keys. The Nylas Smart Compose endpoints do that for you: send a natural-language prompt, get back a written message body, and the reply variant pulls in the original email as context automatically.
This post walks through Smart Compose from two angles: the HTTP API for your backend, and the nylas CLI for the terminal. I work on the CLI, so the terminal commands below are the ones I reach for when I'm testing a prompt.
How Smart Compose works
Smart Compose is two endpoints that turn a prompt into a message body. You send a natural-language prompt, and the response comes back with a suggestion field holding the generated text. There's a POST /messages/smart-compose for writing a brand-new message, and a POST /messages/{message_id}/smart-compose for writing a reply, where the original message is folded into the context so the response actually answers it.
The key thing to understand is that Smart Compose generates text, it doesn't send anything. The suggestion it returns is a message body you do something with: pass it straight to the Send Message endpoint, or pre-fill it into a draft for a human to review and edit first. That separation is deliberate, since it lets you put a person between the AI's output and the recipient, which is usually what you want for anything an LLM wrote.
Two things to know before you start. Smart Compose runs against connected OAuth grants only, not Agent Accounts. The prompt also has a ceiling: up to 1,000 tokens, and a longer prompt returns an error.
Generate a new message
To write a fresh email, POST /v3/grants/{grant_id}/messages/smart-compose takes a single prompt describing what you want. The response carries the generated body in suggestion, which you then send or save. The prompt is plain natural language, so "Write an email to Ron with a business brief about our launch" is a valid input.
curl --request POST \
--url "https://api.us.nylas.com/v3/grants/<GRANT_ID>/messages/smart-compose" \
--header "Authorization: Bearer <NYLAS_API_KEY>" \
--header "Content-Type: application/json" \
--data '{ "prompt": "Write an email to Ron with a business brief about our launch" }'
From the terminal, nylas email smart-compose --prompt generates the draft and prints the suggestion. It's the fastest way to see what a prompt produces before you wire it into an application:
nylas email smart-compose --prompt "Draft a thank you email for the meeting"
The response's suggestion is just text, so the natural next step is to hand it to a send or a draft. Pairing Smart Compose with the Drafts API gives you the review-before-send loop: generate the suggestion, create a draft from it, and let a person approve it before it goes out.
Generate a reply with the thread's context
Replies are where Smart Compose earns its place, because a good reply needs to know what it's answering. POST /v3/grants/{grant_id}/messages/{message_id}/smart-compose takes the same prompt but also the ID of the message you're replying to, and it reads that original message as context. You describe the intent, and the model handles the specifics from the email it's responding to.
curl --request POST \
--url "https://api.us.nylas.com/v3/grants/<GRANT_ID>/messages/<MESSAGE_ID>/smart-compose" \
--header "Authorization: Bearer <NYLAS_API_KEY>" \
--header "Content-Type: application/json" \
--data '{ "prompt": "Reply to my friend Emily and RSVP yes to the party" }'
The CLI adds the message with --message-id, and passing it switches the command from composing a new message to composing a reply:
nylas email smart-compose \
--message-id <message-id> \
--prompt "Reply accepting the invitation"
Because the endpoint reads the original message, your prompt can stay short and high-level. You don't paste the email you're answering into the prompt; you reference it by ID and let Smart Compose pull the context. That keeps your prompt well under the 1,000-token ceiling even when the original message is long.
Build a reply-suggestion loop
Smart Compose fits naturally into an inbound flow. When a message arrives, the message.created webhook delivers the new message, including its ID, which is exactly what the reply endpoint needs. Your handler calls POST /messages/{message_id}/smart-compose with a prompt describing how to respond, gets a suggestion, and creates a draft from it for review.
For a support or sales inbox, this turns every incoming email into a pre-written reply waiting for a person to approve. Your code classifies the message, picks a prompt that matches the situation ("draft a polite reply asking for the order number"), and the reviewer sees a ready draft instead of a blank compose window. The message ID ties the loop together: it identifies the email to respond to and gives Smart Compose its context. When you send the resulting draft, pass that same ID as reply_to_message_id so the reply threads correctly in the recipient's mailbox.
Write prompts that produce usable email
A Smart Compose prompt is an instruction, and the more specific it is, the less editing the suggestion needs. State who the email is to, what it should accomplish, and the tone you want: "Write a brief, friendly email to a customer confirming their refund was processed and will arrive in 5 business days" produces a tighter result than "write a refund email." The model fills in structure and phrasing; your prompt supplies intent and constraints.
For replies, lean on the context the endpoint already has. Because it reads the original message, you don't restate its contents in the prompt, you describe your response to it. "Reply declining the meeting and propose Thursday instead" is enough, since the model already sees the meeting request. Keeping prompts to the intent, and letting the message context carry the details, also keeps you well under the 1,000-token limit.
Stream the response or take it in one piece
Smart Compose offers two ways to receive the generated text, and the right one depends on your interface. To get the whole suggestion back as a single JSON response, the simplest to consume from a backend, add the Accept: application/json header or omit the Accept header entirely, and the endpoint returns one JSON blob with the finished suggestion.
For a user-facing experience, the generation can stream instead. Smart Compose supports Server-Sent Events, so you receive the response token by token as the model produces it, the same typing-out effect a chat interface shows. Streaming makes a noticeable difference in perceived speed, because the user sees text appear immediately rather than waiting for the full message. Either way the content is the same; SSE just changes when the tokens arrive.
The transport is a per-request choice rather than an account setting, so one integration can stream into an interactive compose box and take single-blob responses in a background job, just by switching the header on each call. Latency varies with the length and complexity of the prompt, so whichever transport you pick, plan for a short wait. A "working" indicator in your UI keeps users from wondering whether the request stalled, which matters more with the single-response method where nothing appears until the whole message is ready.
Turn a suggestion into a sent message or draft
A suggestion on its own is just a string; the value comes from what you do with it next. Two patterns cover almost every use. For automated sending where no review is needed, take the suggestion, drop it into the body of a Send Message request, and send it. For anything a human should see first, create a draft from the suggestion so it lands in the user's Drafts folder for editing.
The draft path is the safer default for AI-written mail. A generated message can get a name wrong, strike the wrong tone, or invent a detail, and a draft gives a person the chance to catch that before the recipient does. The flow is: generate the suggestion, create a draft with it, and surface that draft in your UI or the user's mail client for approval. Only on approval does it send.
Because the suggestion is plain text you control, you can also post-process it before it goes anywhere: prepend a greeting, append the user's signature, or run it through your own checks. The endpoint hands you a body, not a finished email, so the last mile stays yours. That review loop is also why Smart Compose returns text rather than sending directly. The endpoint deliberately stops at generation, leaving the decision to send with you, so you can insert exactly as much human oversight as the use case calls for.
Requirements and limits
A few constraints shape where Smart Compose fits. The biggest is the account model: it runs against connected OAuth grants, the mailboxes your users authenticate through Google or Microsoft, and it is not available on Agent Accounts. If you're building on an Agent Account and want AI-written mail, generate the text with your own model and send it through the standard Messages API instead.
The other requirements are about account setup. Smart Compose works on a Google or Microsoft grant connected through an auth app and connector, the standard connected-account flow, and because each call reads the message it's replying to, it needs read scopes like gmail.readonly or Mail.Read. Prompts are capped at 1,000 tokens, which is generous for an instruction but means you reference long source emails by message ID rather than pasting them in.
Things to keep in mind
A few practices make Smart Compose reliable in production rather than just impressive in a demo.
-
Draft, don't auto-send, for anything a person reads. Route the
suggestioninto a draft for review; AI mail gets names, tone, and facts wrong often enough to warrant a human check. -
Reference replies by
message_id, don't paste the email. The reply endpoint reads the original as context, which keeps your prompt short and under the 1,000-token cap. - Stream with SSE for user-facing generation. It shows text immediately; reserve the single-JSON response for backend automation.
- It generates, it doesn't send. The endpoint returns a body; you choose to send it or draft it.
- It's connected-grant only. Smart Compose isn't available on Agent Accounts, so plan AI mail there around your own model plus the Messages API.
- Be specific in prompts. Name the recipient, the goal, and the tone; a precise instruction needs far less editing than a vague one.
Wrapping up
Smart Compose collapses "write an email with AI" into one request: send a prompt, get a suggestion, and send or draft it. The reply endpoint adds the original message as context so your prompt stays a short instruction, and SSE streaming gives a user-facing interface the typing-out effect people expect from AI. The one habit to carry through is routing generated mail into a draft for review, because the endpoint hands you the text precisely so you can decide what happens before it reaches the recipient.
Where to go next:
- Using Smart Compose — the full guide, including streaming
- Generate a new message and respond to a message — the endpoint references
- Send email and manage drafts — where a suggestion goes next
- Nylas CLI email send — send the message the suggestion becomes
Top comments (0)