New-hire onboarding is a checklist. Send a welcome email on day one. Send the benefits-enrollment form. Get the signed I-9 back. Get the direct-deposit form back. Once both forms are in, put orientation on the calendar. That's it — a sequence of emails, a couple of attachments coming back the other way, and one calendar invite at the end.
Most "AI for HR" demos point a model at someone's personal Outlook and call it onboarding. That's fine until you want the agent to be the sender — to own a hr@yourcompany.com mailbox, send from it, watch for replies, pull the signed forms out of those replies, and book orientation under its own name. A model hovering over a human's inbox can't do that cleanly. It needs an identity.
That identity is an Agent Account: a real Nylas mailbox with a grant_id that works against every grant-scoped endpoint — Messages, Attachments, Calendars, Events. Inbound mail surfaces through webhooks, which are application-scoped: you subscribe once at the app level and filter events by the grant_id each payload carries. The whole point of this post is that you already know the data plane. An Agent Account is just a grant. Every call below is a call you'd make against any connected mailbox. There's nothing new to learn except "the sender is software."
I work on the Nylas CLI, so the nylas ... commands below are the exact ones I reach for when I'm poking at a flow by hand. Every operation gets the two-angle treatment: the curl you'd put in your service, and the CLI you'd use to test it.
What you get
One mailbox that does the whole checklist:
- Sends the welcome email and each form-request email — on a schedule if you want them staggered across the first week.
-
Receives inbound replies as standard
message.createdwebhooks, each carrying thegrant_idso you know it's the HR agent. - Reads those replies and pulls the attachments — the signed I-9, the direct-deposit form — straight off the message.
- Books orientation as a real event on its own calendar and emails the invite to the new hire.
The onboarding state — which hire is at which step, which forms are in, whether it's time to book orientation — lives in your app or database. Agent Accounts don't carry custom metadata you can hang a checklist off, so the state machine is yours. Nylas moves the mail and the calendar; your code decides what step comes next.
Why this beats a shared HR inbox + a rules engine
A shared hr@ inbox with mail rules gets you partway and then stalls:
-
Rules can't read attachments. Inbound Nylas Rules match on
from.*only — sender address, domain, TLD. They can't see the subject, the body, or whether a signed PDF is attached. Form-receipt detection is application logic: you fetch the message and inspect its attachments. No rule does that for you. - A shared inbox has no calendar identity. When orientation goes out, you want it to come from the HR agent, on the agent's calendar, so reschedules and RSVPs route back to one place. A distribution list can't host an event.
-
You want one audit trail. Every send, every form received, every booking flows through one
grant_id. That's the thing you log and replay.
Before you begin
You need:
- A Nylas application and API key. Examples use the US data region (
https://api.us.nylas.com); swap the host if you're on EU. - A registered sending domain — a custom domain you've verified, or a Nylas
*.nylas.emailtrial subdomain to kick the tires. New domains warm over roughly four weeks, so don't bulk-onboard a hundred hires on day one. - A publicly reachable HTTPS endpoint for webhooks.
Provision the HR Agent Account
The agent is created with POST /v3/connect/custom, provider nylas, and an email on your registered domain. The optional top-level name sets the display name recipients see. No OAuth, no refresh token.
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": "Acme People Ops",
"settings": { "email": "hr@yourcompany.com" }
}'
nylas agent account create hr@yourcompany.com --name "Acme People Ops"
That hands you a grant_id. Everything from here is /v3/grants/{grant_id}/.... The API auto-creates a default workspace and policy for the account; you only touch those if you want a custom inbound policy later (nylas workspace update <workspace-id> --policy-id <policy-id>).
Provision the forms
The two forms the new hire signs and sends back — the I-9 and the direct-deposit authorization — are PDFs your app hosts. Generate or template them per hire, link them in the form-request emails, and store the expected filenames (or a per-hire token) so your receipt check knows what it's waiting on. The agent doesn't host the blank forms; it sends links out and reads the signed copies back in.
Send the welcome and form-request sequence
Day one: a welcome email. Then a form-request email for each document. You can fire them immediately, or stagger them across the first week with a schedule — --schedule "tomorrow 9am" on the CLI, or a server-side job that calls the send endpoint later. (The send API itself sends now; the CLI's --schedule is the convenient way to defer a single message while you're testing.)
Here's the welcome email — the first touch — via the Messages send endpoint:
curl --request POST \
--url 'https://api.us.nylas.com/v3/grants/<GRANT_ID>/messages/send' \
--header 'Authorization: Bearer <NYLAS_API_KEY>' \
--header 'Content-Type: application/json' \
--data '{
"subject": "Welcome to Acme — your first-week checklist",
"to": [{ "name": "Priya Nair", "email": "priya.nair@example.com" }],
"body": "<p>Welcome aboard, Priya. Over the next few days we'\''ll send you two forms to sign and return: your I-9 and your direct-deposit authorization. Reply to this email with each signed PDF attached. Once both are in, we'\''ll book your orientation.</p>"
}'
nylas email send hr@yourcompany.com \
--to priya.nair@example.com \
--subject "Welcome to Acme — your first-week checklist" \
--body "<p>Welcome aboard, Priya. Over the next few days we'll send you two forms to sign and return.</p>"
The form-request emails are the same call with different copy and a link to each form. To stagger the I-9 request to the next morning instead of sending it immediately:
nylas email send hr@yourcompany.com \
--to priya.nair@example.com \
--subject "Action needed: sign and return your I-9" \
--body "<p>Please review, sign, and reply to this email with your I-9 attached: <a href=\"https://app.acme.com/forms/i9/priya-nair\">Open your I-9</a></p>" \
--schedule "tomorrow 9am"
nylas email send has no attachment flag — it sends links and bodies, which is exactly what you want here: the new hire attaches the signed PDFs on the way back, not you. (If you ever did need to attach a file going out, that's nylas email drafts create --attach, or Base64/multipart on the Drafts API where the file form field is named attachment.)
In your app, record that the sequence started for this hire and mark both forms as pending. That pending/received map is the checklist.
Receive the replies
When Priya replies with a signed form, an inbound message lands in the agent's mailbox and fires a standard message.created webhook. Subscribe once, at the application level — webhooks are app-scoped, not grant-scoped:
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"],
"description": "HR onboarding inbound",
"webhook_url": "https://your-app.example.com/webhooks/nylas",
"notification_email_addresses": ["people-ops@yourcompany.com"]
}'
nylas webhook create \
--url https://your-app.example.com/webhooks/nylas \
--triggers message.created \
--description "HR onboarding inbound"
Every grant in your app delivers to that one URL, and each payload carries a grant_id. Filter for the HR agent's grant_id and ignore the rest.
Two things to get right on the handler:
Dedupe on the notification id. Nylas guarantees at-least-once delivery and retries an event up to three times. The top-level notification id is constant across retries — that's your dedup key. The inner data.object.id is the message id; you can additionally guard on it so you never process the same reply twice.
Don't trust the payload for the body. The Nylas docs differ on whether the full body rides inline in message.created. The safe move is the same either way: don't rely on the payload body — fetch the full message by id when you need it, and branch on message.created.truncated (the type Nylas uses when a large message is delivered without its body). Treat the webhook as a pointer to a message, then go read it.
Read the reply and find the forms
Fetch the inbound message by id to inspect its attachments:
curl --request GET \
--url 'https://api.us.nylas.com/v3/grants/<GRANT_ID>/messages/<MESSAGE_ID>' \
--header 'Authorization: Bearer <NYLAS_API_KEY>'
nylas email read <MESSAGE_ID> hr@yourcompany.com
The message object includes an attachments array — each entry has an id, filename, content_type, and size. This is where form-receipt detection happens, and it's your code, not a rule: does this reply carry a PDF whose filename (or per-hire token) matches the I-9 you're waiting on? If yes, you've got a form. If it's just "thanks, on it!", you haven't — leave the form pending.
Marking the reply read, if you want to, is a separate PUT — it isn't a side effect of the GET:
curl --request PUT \
--url 'https://api.us.nylas.com/v3/grants/<GRANT_ID>/messages/<MESSAGE_ID>' \
--header 'Authorization: Bearer <NYLAS_API_KEY>' \
--header 'Content-Type: application/json' \
--data '{ "unread": false }'
On the CLI, pass --mark-read to nylas email read to do the read-and-mark in one shot. If you'd rather reply in-thread to acknowledge receipt, nylas email reply <message-id> --body "..." preserves the thread.
Download the signed forms
Once a matching attachment is identified, pull it down and hand it to your document store or e-signature archive. The download endpoint requires the message_id as a query parameter — the attachment id alone isn't enough to locate it:
curl --request GET \
--url 'https://api.us.nylas.com/v3/grants/<GRANT_ID>/attachments/<ATTACHMENT_ID>/download?message_id=<MESSAGE_ID>' \
--header 'Authorization: Bearer <NYLAS_API_KEY>' \
--output i9-priya-nair.pdf
nylas email attachments download <ATTACHMENT_ID> <MESSAGE_ID> hr@yourcompany.com -o i9-priya-nair.pdf
Note the CLI argument order: attachment id first, then message id, then the optional grant. Once the file is down, flip that form from pending to received in your checklist. When both the I-9 and the direct-deposit form are received, the hire is ready for orientation — and that's the transition that triggers the booking.
Book orientation on the agent's calendar
Every Agent Account ships with a real calendar. When both forms are in, the agent creates the orientation event on its own calendar and emails the invite to the new hire by passing notify_participants=true. The new hire gets a normal calendar invite from hr@yourcompany.com, accepts in Google Calendar or Outlook, and the RSVP flows back to the agent's mailbox like any other attendee response.
curl --request POST \
--url 'https://api.us.nylas.com/v3/grants/<GRANT_ID>/events?calendar_id=primary¬ify_participants=true' \
--header 'Authorization: Bearer <NYLAS_API_KEY>' \
--header 'Content-Type: application/json' \
--data '{
"title": "New-hire orientation — Priya Nair",
"description": "Welcome session: benefits walkthrough, systems setup, team intros.",
"when": { "start_time": 1750932000, "end_time": 1750939200 },
"participants": [{ "name": "Priya Nair", "email": "priya.nair@example.com" }]
}'
nylas calendar events create hr@yourcompany.com \
--calendar primary \
--title "New-hire orientation — Priya Nair" \
--description "Welcome session: benefits walkthrough, systems setup, team intros." \
--start "2026-06-26 09:00" \
--end "2026-06-26 11:00" \
--participant priya.nair@example.com
Two things worth knowing. First, there's no nylas calendar events send — you create the event and let notify_participants=true (server-side) carry the invite out. The CLI verbs are create, update, delete, list, rsvp, show; emailing participants is the API's job, not a separate calendar command. Second, this is the agent's own Events API, not Scheduler. Scheduler — the round-trip "pick a slot" flow — isn't available to Agent Accounts. For onboarding you don't need it: HR sets the orientation time, the agent books it, done. If you later want hires to self-select a slot, that's a different pattern, but for a fixed orientation the Events API is the whole story.
Guardrails and gotchas
A few things that bite if you don't plan for them:
-
The checklist is yours. Agent Accounts don't carry arbitrary custom metadata you can pin onboarding state to, so don't try to make the mailbox the database. Keep the per-hire
pending/receivedmap and the current step in your own store, keyed on the hire and thegrant_id. -
Form detection is code, not a rule. Inbound Rules match
from.*only — they can't tell a signed I-9 from a "looks good!" reply. Fetch the message, inspectattachments, match on filename or a per-hire token. A rule is also inert until it's attached to a workspace viarule_ids, so even thefrom.*filtering you can do needs wiring up first. -
Fetch the body; don't trust the payload. Branch on
message.created.truncatedand re-fetch by id rather than assuming the webhook carries the full message. -
Send quotas are real. The free plan caps you at 200 messages per account per day, and each orientation invite with
notify_participants=truecounts toward the agent's daily send quota. If the agent is over quota when you create the event, the event still saves but the invitation is skipped silently — nobody gets emailed. Watch your volume during a big intake week. -
Deliverability webhooks exist. Agent Accounts also emit
message.delivered,message.bounced, andmessage.complaint. Subscribe to them so a bounced form-request email doesn't leave a hire silently stuck at step one. -
Set times explicitly. An Agent Account has no default time zone the way a person's calendar does. Pass
timezoneon create, or use epochstart_time/end_time, so orientation lands at the hour you meant.
What's next
You've got the spine: send the sequence, receive replies, pull the signed forms, book orientation — all from one mailbox, all on one grant_id, all on endpoints you already use for ordinary mailboxes. The checklist that ties the steps together is the part you own; Nylas just moves the mail and the calendar.
- Agent Accounts overview — what a grant-as-agent can and can't do
-
How Agent Account calendars work — hosting events, RSVPs, and
notify_participants - Automate customer onboarding — the same send/receive/track pattern for the customer side
-
Nylas CLI command reference — every
nylas ...command used above
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/employee-onboarding-agent-account.md
- Industry playbooks hub: https://cli.nylas.com/ai-answers/agent-account-industry-playbooks.md
Top comments (0)