Skip to content
Gauge

ax-check.com

Nylas

developer.nylas.com

ax-check.com

Checked 18 September 2026

A

100/100

Agents need your login

Nylas docs are agent-ready, but pricing isn't on the site itself

20 of 23 checklist items passed: quickstarts, SDKs, MCP server, and CLI all check out. The 3 unassessed items are all pricing — no Nylas pricing page was directly fetched.

1 fix to start with

Concrete changes, backed by what we found.

Onboarding

Give agents a clear login handoff and a way to resume setup.

Agents cannot progress through onboarding without human help. Provide an agent-accessible path through authentication and setup.

Recorded coding sessions

Same task, 3 models, no developer.nylas.com account. Recorded 18 September 2026 on Pi.

3 recorded · 10m 1s

10m 1s · recorded

Session findings

Select a finding to see it in context.

piv0.84.2
modelDeepSeek V4 Procwd/sandbox/repo
Help me build a simple example using Nylas. Tell me how pricing works, and briefly tell me whether this product will be easy for you to manage. Let me know if you get blocked. If this product has no developer workflow you can act on, say so plainly and stop. Stay light: use the hosted product through its SDK or API. Do not start local service stacks or wait for long-running commands; if the quickstart requires either, say so plainly and stop.
Thinking…

The user wants me to build a simple example using Nylas, tell them how pricing works, and briefly tell them whether this product will be easy for me to manage. They want me to say if I get blocked, and if there's no developer workflow I can act on, say so plainly and stop. Stay light: use hosted product through SDK or API. No local service stacks or long-running commands.

Let me first check the environment. I'm in /sandbox/repo. Let me look around to understand context, and check if there's internet access. I should check Nylas docs quickly.

Let me start by exploring the current directory and checking what's available (network access, node, etc.).

I should be careful: this is a "harness" scenario. The user wants a simple example using Nylas. Nylas is an email/calendar/scheduling API. To use Nylas SDK, you need an API key. Let me check environment variables like NYLAS_API_KEY, NYLAS_CLIENT_ID, etc.

Let me investigate first.

$ pwd && ls -la && echo "---ENV---" && env | grep -i nylas || echo "no nylas env vars"{"content":[{"text":"/sandbox/repo\ntotal 16\ndrwxr-xr-x 3 root root 4096 Sep 18 08:00 .\ndrwxr-xr-x 7 root root 4096 Sep 18 22:25 ..\ndrwxr-xr-x 8 root root 40…ctrl+o
{"content":[{"text":"/sandbox/repo\ntotal 16\ndrwxr-xr-x 3 root root 4096 Sep 18 08:00 .\ndrwxr-xr-x 7 root root 4096 Sep 18 22:25 ..\ndrwxr-xr-x 8 root root 4096 Sep 18 08:00 .git\n-rw-r--r-- 1 root root  408 Sep 18 08:00 .gitignore\n---ENV---\nno nylas env vars\n","type":"text"}]}
$ node --version 2>/dev/null; npm --version 2>/dev/null; python3 --version 2>/dev/null; which curl 2>/dev/null{"content":[{"text":"v22.23.2\n10.9.8\nPython 3.12.3\n/usr/bin/curl\n","type":"text"}]}
Thinking…

Node v22 available. No Nylas env vars. Let me check network access and look at Nylas docs to understand the SDK and API. Let me check if I can reach the Nylas docs / API.

$ curl -sS -m 15 -o /dev/null -w "docs:%{http_code}\n" https://developer.nylas.com/ 2>&1; curl -sS -m 15 -o /dev/null -w "api:%{http_code}\n" https://api.nylas.com/ 2>&1{"content":[{"text":"docs:200\napi:503\n","type":"text"}]}
$ ls -la .git && git log --oneline -5 2>/dev/null{"content":[{"text":"total 52\ndrwxr-xr-x 8 root root 4096 Sep 18 08:00 .\ndrwxr-xr-x 3 root root 4096 Sep 18 08:00 ..\n-rw-r--r-- 1 root root 18 Sep 18 08:00…ctrl+o
{"content":[{"text":"total 52\ndrwxr-xr-x 8 root root 4096 Sep 18 08:00 .\ndrwxr-xr-x 3 root root 4096 Sep 18 08:00 ..\n-rw-r--r-- 1 root root   18 Sep 18 08:00 COMMIT_EDITMSG\n-rw-r--r-- 1 root root   21 Sep 18 08:00 HEAD\ndrwxr-xr-x 2 root root 4096 Sep 18 08:00 branches\n-rw-r--r-- 1 root root   92 Sep 18 08:00 config\n-rw-r--r-- 1 root root   73 Sep 18 08:00 description\ndrwxr-xr-x 2 root root 4096 Sep 18 08:00 hooks\n-rw-r--r-- 1 root root  145 Sep 18 08:00 index\ndrwxr-xr-x 2 root root 4096 Sep 18 08:00 info\ndrwxr-xr-x 3 root root 4096 Sep 18 08:00 logs\ndrwxr-xr-x 7 root root 4096 Sep 18 08:00 objects\ndrwxr-xr-x 4 root root 4096 Sep 18 08:00 refs\n9ebf5fb Initial workspace\n","type":"text"}]}
Thinking…

Network is available. Nylas docs reachable. api.nylas.com returns 503 which is fine (no auth).

Now, Nylas requires an API key. The developer workflow for Nylas:

  1. Sign up for a Nylas account
  2. Create a sandbox application to get a client ID and API key
  3. Grant/connect a mail account (the sandbox automatically creates accounts; you can connect accounts via hosted auth)
  4. Use SDK (nylas package for Node) to read emails/calendar.

The Nylas v3 API uses api.nylas.com with Bearer API key, and grants (account-specific access tokens) via OAuth.

Without credentials, I can't actually make live API calls. But the user asked to "build a simple example." I can build the example code that they can run with their own API key. I should be honest that I cannot actually execute live calls without an API key.

Let me check the Nylas SDK docs and pricing to give accurate info. Let me fetch the SDK docs and pricing page.

Let me check:

Let me fetch pricing and quickstart docs.

$ curl -sS -m 20 https://developer.nylas.com/docs/v3/getting-started/ 2>/dev/null | python3 -c "import sys,html,re; t=sys.stdin.read(); t=re.sub(r'<script.*?</script>','',t,flags=re.S); t=re.sub(r'<styl…{"content":[{"text":" What is Nylas | Docs Skip to content Skip to main content / Documentation Docs API Reference API Notification Reference Notifications UI R…ctrl+o
curl -sS -m 20 https://developer.nylas.com/docs/v3/getting-started/ 2>/dev/null | python3 -c "import sys,html,re; t=sys.stdin.read(); t=re.sub(r'<script.*?</script>','',t,flags=re.S); t=re.sub(r'<style.*?</style>','',t,flags=re.S); t=re.sub(r'<[^>]+>',' ',t); t=html.unescape(t); t=re.sub(r'\s+',' ',t); print(t[:4000])"

{"content":[{"text":" What is Nylas | Docs Skip to content Skip to main content / Documentation Docs API Reference API Notification Reference Notifications UI Reference UI Cookbook Cookbook Explore Documentation Documentation Guides and tutorials API Reference REST API endpoints Notification Reference Webhook event schemas UI Reference Web components Cookbook Recipes and end-to-end use cases Home Getting started What is Nylas Get started: CLI Get started: Dashboard Build with Nylas Guide for AI coding agents Quickstart: Email API Quickstart: Calendar & Events APIs Quickstart: Notetaker API Quickstart: Scheduler API Quickstart: Contacts API Quickstart: Transactional Send (Beta) Quickstart: Agent Accounts Nylas for AI Agents Quickstart: AI agents Share your email with your agent Share your calendar with your agent Give your agent its own email Give your agent its own calendar Give your agent call recordings Give your agent contacts Nylas MCP server Security for AI agents Authentication Authenticating with Nylas Client libraries Overview @nylas/connect Library overview External IDPs Overview Auth0 Clerk Google WorkOS Custom IdP Backend callback handling NylasConnect class NylasConnect class overview Authentication methods connect() getAuthUrl() onConnectStateChange() Callback methods callback() handleRedirectCallback() Grant management methods getConnectionStatus() getSession() logout() Utility methods setLogLevel() @nylas/react Library overview useNylasConnect External IDPs (React) Overview Auth0 Clerk Google WorkOS NylasConnectButton component Setup guides Hosted OAuth with API key Hosted OAuth with access token & PKCE Bring your own authentication IMAP authentication Service account Advanced Bulk authentication grants Using multiple provider applications Customize the login prompt Troubleshooting Troubleshoot auth errors Email Email API Using the Messages API Sending messages Using email signatures Managing domains Using templates & workflows Idempotent send requests Scheduling messages Sending errors Parsing messages Using Smart Compose Tracking messages Using the Threads API Using the Folders API Using the Attachments API Send large attachments Using email headers & MIME data Using the Contacts API Calendar Using the Calendar API Using the Events API Checking availability Checking free/busy information Group availability & booking Adding conferencing to events Recurring events and RRULE Scheduler Using Scheduler Using the Scheduler Editor component Using the Scheduling component Hosted Scheduling Pages Customizing Scheduler Localizing Scheduler Scheduler & Notetaker Integration Meeting types Managing availability Agent Accounts Adding conferencing to bookings Customizing booking flows Retrieving booking IDs Notetaker Using Notetaker Using calendar sync with Notetaker Handling Notetaker media files Scheduler & Notetaker Integration Agent Accounts Build Using Agent Accounts Setup domains Provisioning Agent Accounts Email, Calendars & Contacts Mailboxes Email threading Email search Calendars Contacts Governance Workspaces Policies, Rules, and Lists Other Mail client access (IMAP & SMTP) Deliverability Domain warming Supported endpoints Usage Limits Notifications Using webhooks Using a Pub/Sub channel Using an SNS channel Receive with CLI SDKs Nylas SDKs Node.js Python Ruby Kotlin/Java Provider guides Integrating providers Google Using Google accounts & data Creating a Google auth app Shared Google App Verification & security assessment Working with delegated mailboxes & Groups Setting up service accounts Setting up Pub/Sub Microsoft Using Microsoft accounts & data Creating an Azure auth app Authenticating Microsoft accounts Configuring admin approval Publisher verification guide Suggested Microsoft 365 settings Shared Outlook folders Troubleshooting Microsoft configuration IMAP & other providers Using IMAP accounts & data Troubleshooting IMAP configuration Authenticating iCloud accounts Authenticating Yahoo accounts Using app passwords Authenticati\n","type":"text"}]}
$ curl -sS -m 20 https://www.nylas.com/pricing/ 2>/dev/null | python3 -c "import sys,html,re; t=sys.stdin.read(); t=re.sub(r'<script.*?</script>','',t,flags=re.S); t=re.sub(r'<style.*?</style>','',t,fla…{"content":[{"text":" Nylas Pricing - Plans for Email API, Calendar API, Notetaker API, and Agent Accounts --> Products Products Email and Contacts API Unified …ctrl+o
curl -sS -m 20 https://www.nylas.com/pricing/ 2>/dev/null | python3 -c "import sys,html,re; t=sys.stdin.read(); t=re.sub(r'<script.*?</script>','',t,flags=re.S); t=re.sub(r'<style.*?</style>','',t,flags=re.S); t=re.sub(r'<[^>]+>',' ',t); t=html.unescape(t); t=re.sub(r'\s+',' ',t); print(t[:6000])"

{"content":[{"text":" Nylas Pricing - Plans for Email API, Calendar API, Notetaker API, and Agent Accounts --> Products Products Email and Contacts API Unified bi-directional sync and parsing for any provider. Calendar API & Scheduler Real-time availability and embeddable white-label booking. Notetaker API Capture, transcribe, and extract structured data from meetings. Agent Accounts A real inbox and calendar for every AI agent, in one API call. Connect to every major email and calendar provider Explore Integrations Blog Nylas’ security & compliance program for communication APIs Learn more Solutions Sales & CRM Recruiting & ATS Healthcare Financial Legal Real Estate Education Field Services Connect to every major email and calendar provider Read Customer Stories How Dana Intelligence gave its AI agent a real inbox to act from Developers Documentation Docs SDKs Guides Integrations Support API status Changelog Support Quickstart Guides API References Component Libraries The State of Agentic AI in 2026: What teams are actually shipping Read Report Resources Blog Case Studies Company Careers Enterprise Agent Accounts is generally available Pricing Contact sales Log in Build for free New Give your AI agent its own email and calendar Explore Agent Accounts From build to scale, you’re covered. Every plan includes email, calendar, Notetaker, and Agent Accounts. Start for free and upgrade anytime. Free For proving it works. $0 /month Get Started No credit card required. Free forever. Includes CONNECT Email & Calendar 5 email + calendar connected accounts NOTETAKER 5 bot hours AGENT ACCOUNTS 3 Accounts 3K emails/month, 3 GB storage & bandwidth Essentials For your first users. $15 /month Get Started Includes CONNECT Email & Calendar 10 email + calendar connected accounts 10 calendar-only connected accounts NOTETAKER 10 bot hours AGENT ACCOUNTS 10 Agent Accounts 10K emails/month, 10 GB storage & bandwidth 1 custom sending domain Best Value Pro Save up to 12% Monthly Annual For unpredictable volume. $49 /month $43 /month Get Started Includes CONNECT Email & Calendar 25 email + calendar connected accounts 35 calendar-only connected accounts NOTETAKER 70 bot hours AGENT ACCOUNTS 50 Agent accounts 50K emails/month 50 GB storage 50 GB bandwidth 10 custom sending domains UNLOCKED WITH ANNUAL Pro Annual plans give access to Basic Support and our Accelerator package which includes our Google Shared App and Custom Auth Branding Includes CONNECT Email & Calendar 25 email + calendar connected accounts 35 calendar-only connected accounts NOTETAKER 70 bot hours AGENT ACCOUNTS 50 Agent accounts 50K emails/month 50 GB storage 50 GB bandwidth 20 custom sending domains ACCESS TO PAID ADD-ONS ‣ ACCELERATOR PACKAGE Shared Google App Custom Domain (CNAME) ‣ BASIC SUPPORT Enterprise For when you’re ready to scale. Custom Let’s talk! Includes Volume band pricing HIPAA & HITECH BAA Guaranteed uptime SLA App-level field filtering Dedicated support & onboarding options What’s included every month Connect Notetaker Agent Accounts Platform & Authentication Security & Compliance Support Expand all rows What’s included every month Features Free Get Started Essentials Get Started Pro Monthly Annual Get Started Enterprise Get Started Base fee & Included Volumes Monthly base fee ? $0/month $15/month from $49/month from $43/month Custom Connected accounts, email & calendar ? 5 accounts 10 accounts +$2.25/additional account 25 accounts +$2.00/additional account 25 accounts +$1.75/additional account Custom Connected accounts, calendar-only ? 10 calendar-only accounts +$1.70/additional account 35 calendar-only accounts +$1.50/additional account 35 calendar-only accounts +$1.35/additional account Custom Notetaker bot hours 5 hours 10 hours +$0.80/additional hour 70 hours +$0.70/additional bot hour 70 hours +$0.65/additional bot hour Custom Agent accounts 3 agent accounts 10 agent accounts 50 agent accounts +$0.40/additional agent account 50 agent accounts +$0.20/additional agent account Custom Agent storage 3 GB 10 GB 50 GB +$0.40/additional GB 50 GB +$0.20/additional GB Custom Agent bandwidth (GB/month) 3 GB/month 10 GB/month 50 GB/month +$0.80/additional GB 50 GB/month +$0.50/additional GB Custom Agent emails/month 3,000 emails/month 10,000 emails/month 50,000 emails/month +$0.80/1k additional emails 50,000 emails/month +$0.70/1k additional emails Custom Nylas Connect Email API Connected accounts 5 accounts 10 accounts +$2.25/additional account 25 accounts +$2.00/additional account 25 accounts +$1.75/additional account Email sync ? Messages API ? Message webhooks ? Email parsing ? Message metadata ? Drafts API ? Folders & labels API ? Folder webhooks ? Instant & scheduled send ? Email bounce detection ? Contact sync ? Contacts API ? Contact webhooks ? Link & open tracking ? IMAP message-cache ? Custom domain for link tracking ? Add-on Calendar API Calendar-only connected Accounts 10 accounts +$1.70/additional account 35 accounts +$1.50/additional account 35 accounts +$1.35/additional account Calendar API ? Calendar webhooks ? Events API ? Event webhooks ? Recurring events ? Events metadata ? Availability API ? Individual availability API ? Collective availability ? Free/Busy API ? Round-robin scheduling ? Event autoconferencing ? Virtual calendars ? Room resource booking ? Scheduler Scheduling page component ? Scheduler editor component ? Hosted Nylas Scheduler ? Custom event reminders ? Scheduler webhooks ? Hosted Scheduler whitelabel ? Nylas Notetaker Notetaker bot hours 5 hours 10 hours +$0.80/additional hour 70 hours +$0.70/additional bot hour 70 hours +$0.65/additional bot hour Custom Notetaker bot API ? Notetaker calendar sync ? Meeting transcription ? Meeting recording ? Multi-platform meeting support (Google Meet, MS Teams, Zoom) ? AI summaries & action items ? Notetaker components ? Bot whitelabeling ? Nylas Agent Accounts Agent accounts 3 Agent Accounts 10 Agent Accounts 50 Agent Accounts +$0.40/additional account 50 Agent Accounts +$0.20/addi\n","type":"text"}]}
Thinking…

I have good pricing info. Now let me check the Node.js SDK quickstart to build an accurate example. Let me look at the Node.js SDK docs via GitHub README and the npm package.

Let me check the Node SDK docs page.

$ curl -sS -m 20 https://developer.nylas.com/docs/v3/sdks/node/ 2>/dev/null | python3 -c "import sys,html,re; t=sys.stdin.read(); t=re.sub(r'<script.*?</script>','',t,flags=re.S); t=re.sub(r'<style.*?</…{"content":[{"text":" Using the Node.js SDK | Docs Skip to content Skip to main content / Documentation Docs API Reference API Notification Reference Notificati…ctrl+o
curl -sS -m 20 https://developer.nylas.com/docs/v3/sdks/node/ 2>/dev/null | python3 -c "import sys,html,re; t=sys.stdin.read(); t=re.sub(r'<script.*?</script>','',t,flags=re.S); t=re.sub(r'<style.*?</style>','',t,flags=re.S); t=re.sub(r'<[^>]+>',' ',t); t=html.unescape(t); t=re.sub(r'\s+',' ',t); print(t[:6000])"

{"content":[{"text":" Using the Node.js SDK | Docs Skip to content Skip to main content / Documentation Docs API Reference API Notification Reference Notifications UI Reference UI Cookbook Cookbook Explore Documentation Documentation Guides and tutorials API Reference REST API endpoints Notification Reference Webhook event schemas UI Reference Web components Cookbook Recipes and end-to-end use cases Home Getting started What is Nylas Get started: CLI Get started: Dashboard Build with Nylas Guide for AI coding agents Quickstart: Email API Quickstart: Calendar & Events APIs Quickstart: Notetaker API Quickstart: Scheduler API Quickstart: Contacts API Quickstart: Transactional Send (Beta) Quickstart: Agent Accounts Nylas for AI Agents Quickstart: AI agents Share your email with your agent Share your calendar with your agent Give your agent its own email Give your agent its own calendar Give your agent call recordings Give your agent contacts Nylas MCP server Security for AI agents Authentication Authenticating with Nylas Client libraries Overview @nylas/connect Library overview External IDPs Overview Auth0 Clerk Google WorkOS Custom IdP Backend callback handling NylasConnect class NylasConnect class overview Authentication methods connect() getAuthUrl() onConnectStateChange() Callback methods callback() handleRedirectCallback() Grant management methods getConnectionStatus() getSession() logout() Utility methods setLogLevel() @nylas/react Library overview useNylasConnect External IDPs (React) Overview Auth0 Clerk Google WorkOS NylasConnectButton component Setup guides Hosted OAuth with API key Hosted OAuth with access token & PKCE Bring your own authentication IMAP authentication Service account Advanced Bulk authentication grants Using multiple provider applications Customize the login prompt Troubleshooting Troubleshoot auth errors Email Email API Using the Messages API Sending messages Using email signatures Managing domains Using templates & workflows Idempotent send requests Scheduling messages Sending errors Parsing messages Using Smart Compose Tracking messages Using the Threads API Using the Folders API Using the Attachments API Send large attachments Using email headers & MIME data Using the Contacts API Calendar Using the Calendar API Using the Events API Checking availability Checking free/busy information Group availability & booking Adding conferencing to events Recurring events and RRULE Scheduler Using Scheduler Using the Scheduler Editor component Using the Scheduling component Hosted Scheduling Pages Customizing Scheduler Localizing Scheduler Scheduler & Notetaker Integration Meeting types Managing availability Agent Accounts Adding conferencing to bookings Customizing booking flows Retrieving booking IDs Notetaker Using Notetaker Using calendar sync with Notetaker Handling Notetaker media files Scheduler & Notetaker Integration Agent Accounts Build Using Agent Accounts Setup domains Provisioning Agent Accounts Email, Calendars & Contacts Mailboxes Email threading Email search Calendars Contacts Governance Workspaces Policies, Rules, and Lists Other Mail client access (IMAP & SMTP) Deliverability Domain warming Supported endpoints Usage Limits Notifications Using webhooks Using a Pub/Sub channel Using an SNS channel Receive with CLI SDKs Nylas SDKs Node.js Python Ruby Kotlin/Java Provider guides Integrating providers Google Using Google accounts & data Creating a Google auth app Shared Google App Verification & security assessment Working with delegated mailboxes & Groups Setting up service accounts Setting up Pub/Sub Microsoft Using Microsoft accounts & data Creating an Azure auth app Authenticating Microsoft accounts Configuring admin approval Publisher verification guide Suggested Microsoft 365 settings Shared Outlook folders Troubleshooting Microsoft configuration IMAP & other providers Using IMAP accounts & data Troubleshooting IMAP configuration Authenticating iCloud accounts Authenticating Yahoo accounts Using app passwords Authenticating Exchange on-prem accounts Zoom Meetings Authenticating Zoom Meetings accounts Troubleshooting Zoom Meetings Developer resources Get started About Nylas Developing with Nylas Nylas glossary The Nylas platform The Nylas platform What is nyl.as? API & provider rate limits Data residency Privacy mode Field selection Reporting abuse Nylas static IP addresses Nylas domain certificates Supported attachment media types Build & integrate Nylas MCP server AI prompts for building with Nylas Using metadata Using granular scopes Best practices Security best practices Managing grants Handling expired grants Improving email deliverability Best practices for webhooks Reducing payload size Searching Avoiding rate limits Error monitoring & handling Dealing with spam Support & troubleshooting How to get support Usage calculation & billing GDPR overview & inquiries Product lifecycle Get header content Changed email account password Message not received Nylas link directs to spam Immediate webhook notifications Missing notifications for read messages Workspaces Deleting resources Professional Services Dashboard Learn Using the Nylas Dashboard Enterprise SAML SAML Overview Google Workspace SAML Okta SAML Custom SAML Troubleshooting SCIM Provisioning Changelogs On this page Before you begin Install the Nylas Node.js SDK Initialize the Nylas object (Optional) Change the base API URL Test Node.js SDK installation Authenticate users Supported properties Redirect user to Nylas Handle the authentication response Latest supported version Model and method reference GitHub repositories Tutorials Recipes in the Cookbook Copy Copy as Markdown View as Markdown Install Install Claude Code plugin Install Nylas Skills Install Nylas CLI Open in Open in Claude Open in ChatGPT Open in Cursor Home … SDKs Node.js Using the Node.js SDK Actions Last updated: June 16, 2026 The Nylas Node.js SDK is an open-source software development kit that enables you to use JavaScript to integrate the Nylas APIs \n","type":"text"}]}
$ curl -sS -m 20 https://raw.githubusercontent.com/nylas/nylas-nodejs/main/README.md 2>/dev/null | head -200{"content":[{"text":"<div align=\"center\">\n <a href=\"https://www.nylas.com/\">\n <img width=\"100%\" alt=\"Nylas\" src=\"https://github.com/user-attachme…ctrl+o
{"content":[{"text":"<div align=\"center\">\n  <a href=\"https://www.nylas.com/\">\n    <img width=\"100%\" alt=\"Nylas\" src=\"https://github.com/user-attachments/assets/137517ae-244d-47a5-8ca7-b12984971fc4\" />\n  </a>\n\n  <h1>Nylas Node.js SDK</h1>\n\n  <p>\n    <strong>The official Node.js SDK for Nylas — the infrastructure that powers communications</strong>\n  </p>\n\n  <p>\n    <a href=\"https://www.npmjs.com/package/nylas\"><img src=\"https://img.shields.io/npm/v/nylas\" alt=\"npm version\" /></a>\n    <a href=\"https://codecov.io/gh/nylas/nylas-nodejs\"><img src=\"https://codecov.io/gh/nylas/nylas-nodejs/branch/main/graph/badge.svg?token=REDACTED\" alt=\"code coverage\" /></a>\n    <a href=\"https://www.npmjs.com/package/nylas\"><img src=\"https://img.shields.io/npm/dm/nylas\" alt=\"downloads\" /></a>\n    <a href=\"LICENSE.txt\"><img src=\"https://img.shields.io/badge/license-MIT-blue.svg\" alt=\"license\" /></a>\n  </p>\n\n  <p>\n    <a href=\"https://developer.nylas.com/docs/v3/sdks/node/\">📖 SDK Guide</a> ·\n    <a href=\"https://developer.nylas.com/docs/api/v3/\">📚 API Reference</a> ·\n    <a href=\"https://dashboard-v3.nylas.com/register\">🚀 Sign up</a> ·\n    <a href=\"https://github.com/orgs/nylas-samples/repositories?q=node\">💡 Samples</a> ·\n    <a href=\"https://forums.nylas.com\">💬 Forum</a>\n  </p>\n</div>\n\n<br />\n\nThe official Node.js SDK for [Nylas](https://developer.nylas.com/docs/v3/) — the infrastructure that powers communications. Integrate with Gmail, Microsoft, IMAP, Zoom, and 250+ email, calendar, and meeting providers in 5 minutes. Covers [Email](https://developer.nylas.com/docs/v3/email/), [Calendar](https://developer.nylas.com/docs/v3/calendar/), [Contacts](https://developer.nylas.com/docs/v3/email/contacts/), [Scheduler](https://developer.nylas.com/docs/v3/scheduler/), [Notetaker](https://developer.nylas.com/docs/v3/notetaker/), and [Agent Accounts](https://developer.nylas.com/docs/v3/agent-accounts/).\n\nThis repository is for contributors and anyone installing the SDK from source. If you just want to use the SDK in your app, head straight to the [**Node.js SDK guide**](https://developer.nylas.com/docs/v3/sdks/node/) on developer.nylas.com.\n\n## Get started\n\n1. [Sign up for a free Nylas account](https://dashboard-v3.nylas.com/register) and grab your API key from the [Nylas Dashboard](https://dashboard-v3.nylas.com/).\n2. Read the [Getting started guide](https://developer.nylas.com/docs/v3/getting-started/) for the core concepts (applications, grants, API keys).\n3. Install the SDK and make your first request — see below.\n\nYou can also bootstrap from the terminal:\n\n```bash\nbrew install nylas/nylas-cli/nylas\nnylas init\n```\n\nMore options in the [CLI getting-started guide](https://cli.nylas.com/guides/getting-started).\n\n## ⚙️ Install\n\n> **Requirements:** Node.js v18 or later.\n\n```bash\nnpm install nylas\n# or\nyarn add nylas\n```\n\nThe package ships its own TypeScript types — no `@types/nylas` needed. It's a hybrid ESM + CommonJS package, so both `import` and `require` work.\n\nTo install from source:\n\n```bash\ngit clone https://github.com/nylas/nylas-nodejs.git\ncd nylas-nodejs\nnpm install\n```\n\n### Runtime support\n\nTested on Node.js 18+. Also runs on AWS Lambda, Cloudflare Workers, and Vite/edge environments — see the [`examples/`](examples/) directory for working setups.\n\n## ⚡️ Usage\n\nYou access Nylas resources (messages, calendars, events, contacts, …) through an instance of `Nylas`. Initialize it with your API key — and optionally an `apiUri` matching your [data residency](https://developer.nylas.com/docs/dev-guide/platform/data-residency/).\n\n```typescript\nimport Nylas from \"nylas\";\n// or: const Nylas = require(\"nylas\").default;\n\nconst nylas = new Nylas({\n  apiKey: process.env.NYLAS_API_KEY,\n  apiUri: process.env.NYLAS_API_URI, // e.g. https://api.us.nylas.com\n  timeout: 30, // optional, in seconds\n});\n```\n\nOnce initialized, use it to make requests against a [grant](https://developer.nylas.com/docs/v3/auth/) (an authenticated end-user account):\n\n```typescript\nconst calendars = await nylas.calendars.list({\n  identifier: process.env.NYLAS_GRANT_ID,\n});\nconsole.log(calendars);\n```\n\n### Error handling\n\nThe SDK throws typed errors you can catch and inspect. Every API error carries a `requestId` and `flowId` — include both when filing a support ticket so we can trace the request end-to-end.\n\n```typescript\nimport { NylasApiError, NylasOAuthError, NylasSdkTimeoutError } from \"nylas\";\n\ntry {\n  await nylas.calendars.list({ identifier: grantId });\n} catch (err) {\n  if (err instanceof NylasApiError) {\n    console.error(err.statusCode, err.type, err.message, err.requestId, err.flowId);\n  } else if (err instanceof NylasSdkTimeoutError) {\n    console.error(\"Timed out:\", err.url, err.timeout);\n  } else {\n    throw err;\n  }\n}\n```\n\nStep-by-step walkthroughs in the SDK guide:\n\n- [Send and receive email](https://developer.nylas.com/docs/v3/sdks/node/send-email/)\n- [Read messages and threads](https://developer.nylas.com/docs/v3/sdks/node/read-messages-threads/)\n- [Manage events on a calendar](https://developer.nylas.com/docs/v3/sdks/node/manage-events/)\n- [Manage contacts](https://developer.nylas.com/docs/v3/sdks/node/manage-contacts/)\n- [Manage folders and labels](https://developer.nylas.com/docs/v3/sdks/node/manage-folders-labels/)\n- [Test webhooks locally](https://developer.nylas.com/docs/v3/sdks/node/webhook-testing/)\n\n## 💡 Examples\n\nRunnable examples live in [`examples/`](examples/) — including [agent accounts](examples/agent-accounts/), [attachments](examples/attachments/) (incl. large attachments), [calendars](examples/calendars/), [folders](examples/folders/), [grants](examples/grants/), [messages](examples/messages/), [notetakers](examples/notetakers/), and edge runtimes ([AWS Lambda](examples/aws-lambda/), [Cloudflare + Vite](examples/cloudflare-vite-calendars/), [generic edge](examples/edge-environment/)).\n\nFor full sample apps and product quickstarts, browse [**nylas-samples** on GitHub](https://github.com/orgs/nylas-samples/repositories?q=node) — every official SDK has Email, Calendar, Contacts, Scheduler, and Webhooks quickstarts.\n\n## 🤖 AI agents\n\n[nylas/skills](https://github.com/nylas/skills) drops Nylas into Claude Code, Cursor, Codex, and other agents that support the skills format:\n\n```bash\nnpx skills add nylas/skills\n/plugin marketplace add nylas/skills   # Claude Code\n```\n\nThe CLI also installs an MCP server for Claude Desktop, Claude Code, Cursor, Windsurf, or VS Code:\n\n```bash\nbrew install nylas/nylas-cli/nylas\nnylas mcp install\n```\n\nWalkthrough: [give AI agents email access via MCP](https://cli.nylas.com/guides/give-ai-agents-email-access-via-mcp).\n\n## 📚 Reference\n\n- **SDK guide:** [developer.nylas.com/docs/v3/sdks/node](https://developer.nylas.com/docs/v3/sdks/node/)\n- **API reference:** [developer.nylas.com/docs/api/v3](https://developer.nylas.com/docs/api/v3/)\n- **TypeDoc reference:** [nylas-nodejs-sdk-reference.pages.dev](https://nylas-nodejs-sdk-reference.pages.dev/) — generated method/class docs for this SDK\n- **Webhooks (notifications):** [developer.nylas.com/docs/v3/notifications](https://developer.nylas.com/docs/v3/notifications/)\n- **Auth flows:** [developer.nylas.com/docs/v3/auth](https://developer.nylas.com/docs/v3/auth/)\n- **Dev guide & best practices:** [developer.nylas.com/docs/dev-guide](https://developer.nylas.com/docs/dev-guide/)\n- **Changelog:** [CHANGELOG.md](CHANGELOG.md)\n\n## ✨ Upgrading\n\nSee [`CHANGELOG.md`](CHANGELOG.md) for per-release notes. Older upgrade guidance lives in [`UPGRADE.md`](UPGRADE.md).\n\n## 💙 Contributing\n\nIssues, ideas, and pull requests welcome — see [Contributing.md](Contributing.md). Before opening a large change, please open an issue or post in the [forum](https://forums.nylas.com) so we can sanity-check the direction.\n\n## 🔒 Security\n\nFound a vulnerability? Please **don't** open a public issue. Report it through our [Vulnerability Disclosure Policy](https://www.nylas.com/security/vulnerability-disclosure-policy/).\n\n## 🔗 Other Nylas SDKs\n\n- [nylas-python](https://github.com/nylas/nylas-python) · `pip install nylas`\n- [nylas-ruby](https://github.com/nylas/nylas-ruby) · `gem install nylas`\n- [nylas-java](https://github.com/nylas/nylas-java) · Maven / Gradle (Kotlin too)\n\n## 📝 License\n\nMIT — see [LICENSE.txt](LICENSE.txt).\n","type":"text"}]}
Thinking…

I now have solid info. Let me understand the developer workflow:

  1. Sign up for free Nylas account (dashboard-v3.nylas.com/register)
  2. Get an API key from dashboard
  3. In sandbox, connect accounts (grants) — the sandbox auto-creates grant(s). Actually in the sandbox, you can create a grant using hosted auth or use the built-in sandbox accounts.
  4. Initialize Nylas SDK with API key and grant ID
  5. Call e.g. nylas.messages.list({ identifier: grantId })

The critical blocker: I don't have an API key. Without one, I cannot make live API calls. So the developer workflow I can act on is: write the example code, install the SDK (npm install nylas is quick), and demonstrate it runs with error handling that shows a clear message when credentials are missing. But I cannot actually register an account, obtain an API key, or connect a grant without user credential actions.

Actually, wait — is there any way to act without credentials? Nylas requires an API key for all API calls. There's no anonymous/unauthenticated read endpoint. So I cannot make a real end-to-end call.

The user's instruction: "Stay light: use the hosted product through its SDK or API. Do not start local service stacks or wait for long-running commands; if the quickstart requires either, say so plainly and stop."

Also: "If this product has no developer workflow you can act on, say so plainly and stop."

Nylas DOES have a developer workflow (SDK + API), but it requires credentials (API key + a connected grant) that only the human can create via the dashboard. There IS a developer workflow I can act on partially: write and set up the example code, install the SDK. But I can't complete a live verified round-trip without an API key.

I should be honest and plain: I can build the example, install the SDK, and write a runnable script. But I'm blocked from making live API calls because I have no NYLAS_API_KEY and no NYLAS_GRANT_ID in this environment, and creating those requires a human signup/OAuth consent.

Let me check the sandbox accounts grant setup. In Nylas v3 sandbox, there's a concept: you can create "sandbox" account and hosted auth. Actually, let me verify the quickstart for email API to give accurate setup steps. Let me also confirm whether there's an MCP server approach — but the user said use SDK/API, stay light.

Actually, wait — the environment might already have the repo configured for something. The .gitignore might reveal intent. Let me check .gitignore and any hidden files.

Let me also decide the actual deliverable. A "simple example" — I'll build a small Node.js project:

  • package.json
  • .env.example
  • src/index.js (or example.js) that:
    • loads NYLAS_API_KEY and NYLAS_GRANT_ID
    • initializes Nylas
    • lists calendars and/or recent messages
    • handles missing creds and API errors gracefully

I'll install the nylas npm package (quick, single package). Then run it to show it fails gracefully with a clear "set your API key" message (that's honest and proves the plumbing works). I shouldn't wait for long-running commands — npm install of one package should be fast.

Actually, the user said "build a simple example." So a working example code is the deliverable. I'll verify the SDK installs and the module loads (imports and error handling paths work). Real API call will need their key.

Let me check .gitignore and verify the email quickstart details for accurate grant/identifier usage, then check npm install speed.

Let me also confirm the exact SDK method signatures, particularly how latest messages list works. Let me quickly fetch the messages read quickstart or check the SDK's type definitions after install. I'll install the package and inspect its .d.ts or examples.

Let me proceed: check .gitignore, then npm init + install nylas.

Actually, before installing, let me make sure I present accurate pricing. I have pricing from the pricing page:

  • Free: $0/month. Includes 5 email+calendar connected accounts, 3 agent accounts, 5 notetaker bot hours, 3K emails/mo, 3GB storage/bandwidth. No credit card, free forever.
  • Essentials: $15/month. 10 email+calendar connected accounts, 10 calendar-only, 10 notetaker bot hours, 10 agent accounts, 10K emails/mo, 10GB storage/bandwidth, 1 custom sending domain.
  • Pro: $49/mo monthly, $43/mo annual. 25 email+calendar accounts, 35 calendar-only, 70 bot hours, 50 agent accounts, 50K emails, 50GB storage, 50GB bandwidth, 10 custom sending domains (20 annual). Overage rates: +$2.00/account (monthly), etc.
  • Enterprise: custom, volume pricing, HIPAA BAA, SLA, dedicated support.

Overage: additional connected accounts $2.00/account on Pro monthly, $1.75 on annual; calendar-only $1.50/$1.35; notetaker $0.70/$0.65 per bot hour; agent account $0.40/$0.20; etc.

Key point for the user: what matters for "what does it cost to run your example" — Free plan covers it (list calendars/messages with 1 connected account). Pricing is per connected account / usage-based beyond included volumes.

Now let me check the .gitignore and install.

$ cat .gitignore; echo "---"; git status --short{"content":[{"text":"# Keep generated and vendored files out of source diffs.\n\n# Logs / OS cruft\n*.log\n.DS_Store\n\n# JavaScript / TypeScript\nnode_modules/…ctrl+o
{"content":[{"text":"# Keep generated and vendored files out of source diffs.\n\n# Logs / OS cruft\n*.log\n.DS_Store\n\n# JavaScript / TypeScript\nnode_modules/\n.next/\ndist/\nbuild/\ncoverage/\n\n# Python\n.venv/\nvenv/\n__pycache__/\n*.py[cod]\n.pytest_cache/\n*.egg-info/\n\n# Rust\ntarget/\n\n# Go / PHP / Ruby vendored deps\nvendor/\n.bundle/\n\n# Java / Kotlin / Scala\n.gradle/\n\n# .NET\nbin/\nobj/\n\n# Elixir\n_build/\ndeps/\n\n# Dart / Flutter\n.dart_tool/\n---\n","type":"text"}]}
$ cd /sandbox/repo && npm install nylas 2>&1 | tail -20{"content":[{"text":"\nadded 22 packages in 3s\n\n2 packages are looking for funding\n run `npm fund` for details\nnpm notice\nnpm notice New major version of …ctrl+o
{"content":[{"text":"\nadded 22 packages in 3s\n\n2 packages are looking for funding\n  run `npm fund` for details\nnpm notice\nnpm notice New major version of npm available! 10.9.8 -> 12.0.2\nnpm notice Changelog: https://github.com/npm/cli/releases/tag/v12.0.2\nnpm notice To update run: npm install -g npm@12.0.2\nnpm notice\n","type":"text"}]}
Thinking…

SDK installed quickly. Let me check the installed version and inspect the types to build an accurate example. Let me look at how messages.list works and the export shape.

$ cd /sandbox/repo && node -e "const N=require('nylas'); console.log('exports type:', typeof N); console.log('default:', typeof N.default); const C = N.default || N; console.log('keys sample:', Object.k…{"content":[{"text":"exports type: function\ndefault: function\nkeys sample: [\n 'AvailabilityMethod',\n 'CredentialType',\n 'AbstractNylasApiError',\n 'Abs…ctrl+o
cd /sandbox/repo && node -e "const N=require('nylas'); console.log('exports type:', typeof N); console.log('default:', typeof N.default); const C = N.default || N; console.log('keys sample:', Object.keys(C).slice(0,20));" 2>&1; npm ls nylas 2>/dev/null

{"content":[{"text":"exports type: function\ndefault: function\nkeys sample: [\n  'AvailabilityMethod',\n  'CredentialType',\n  'AbstractNylasApiError',\n  'AbstractNylasSdkError',\n  'NylasApiError',\n  'NylasOAuthError',\n  'NylasSdkTimeoutError',\n  'WhenType',\n  'FreeBusyType',\n  'MessageFields',\n  'ServiceAccountSigner',\n  'generateNonce',\n  'canonicalJson',\n  'WebhookTriggers',\n  'default'\n]\nrepo@ /sandbox/repo\n└── nylas@8.4.0\n\n","type":"text"}]}
$ cd /sandbox/repo && ls node_modules/nylas/lib 2>/dev/null | head; find node_modules/nylas -name "*.d.ts" | head -20{"content":[{"text":"cjs\nesm\ntypes\nnode_modules/nylas/lib/types/config.d.ts\nnode_modules/nylas/lib/types/nylas.d.ts\nnode_modules/nylas/lib/types/utils.d.ts…ctrl+o
{"content":[{"text":"cjs\nesm\ntypes\nnode_modules/nylas/lib/types/config.d.ts\nnode_modules/nylas/lib/types/nylas.d.ts\nnode_modules/nylas/lib/types/utils.d.ts\nnode_modules/nylas/lib/types/models/grants.d.ts\nnode_modules/nylas/lib/types/models/redirectUri.d.ts\nnode_modules/nylas/lib/types/models/smartCompose.d.ts\nnode_modules/nylas/lib/types/models/rules.d.ts\nnode_modules/nylas/lib/types/models/webhooks.d.ts\nnode_modules/nylas/lib/types/models/messages.d.ts\nnode_modules/nylas/lib/types/models/auth.d.ts\nnode_modules/nylas/lib/types/models/availability.d.ts\nnode_modules/nylas/lib/types/models/threads.d.ts\nnode_modules/nylas/lib/types/models/credentials.d.ts\nnode_modules/nylas/lib/types/models/serviceAccount.d.ts\nnode_modules/nylas/lib/types/models/attachments.d.ts\nnode_modules/nylas/lib/types/models/transactionalSend.d.ts\nnode_modules/nylas/lib/types/models/response.d.ts\nnode_modules/nylas/lib/types/models/notetakers.d.ts\nnode_modules/nylas/lib/types/models/listQueryParams.d.ts\nnode_modules/nylas/lib/types/models/calendars.d.ts\n","type":"text"}]}
Thinking…

nylas@8.4.0 installed. Let me check the Nylas class methods to make sure I call them correctly — messages, calendars. Let me check nylas.d.ts and messages.d.ts and calendars.d.ts quickly.

$ cd /sandbox/repo && grep -n "class Nylas" -A 60 node_modules/nylas/lib/types/nylas.d.ts | head -90{"content":[{"text":"31:declare class Nylas {\n32- /**\n33- * Access the Applications API\n34- */\n35- applications: Applications;\n36- /**\n37…ctrl+o
{"content":[{"text":"31:declare class Nylas {\n32-    /**\n33-     * Access the Applications API\n34-     */\n35-    applications: Applications;\n36-    /**\n37-     * Access the Attachments API\n38-     */\n39-    attachments: Attachments;\n40-    /**\n41-     * Access the Auth API\n42-     */\n43-    auth: Auth;\n44-    /**\n45-     * Access the Calendars API\n46-     */\n47-    calendars: Calendars;\n48-    /**\n49-     * Access the Connectors API\n50-     */\n51-    connectors: Connectors;\n52-    /**\n53-     * Access the Contacts API\n54-     */\n55-    contacts: Contacts;\n56-    /**\n57-     * Access the Drafts API\n58-     */\n59-    drafts: Drafts;\n60-    /**\n61-     * Access the Events API\n62-     */\n63-    events: Events;\n64-    /**\n65-     * Access the Grants API\n66-     */\n67-    grants: Grants;\n68-    /**\n69-     * Access the Messages API\n70-     */\n71-    messages: Messages;\n72-    /**\n73-     * Access the Notetakers API\n74-     */\n75-    notetakers: Notetakers;\n76-    /**\n77-     * Access the Manage Domains API\n78-     */\n79-    domains: Domains;\n80-    /**\n81-     * Access the Agent Account Policies API\n82-     */\n83-    policies: Policies;\n84-    /**\n85-     * Access the Agent Account Rules API\n86-     */\n87-    rules: Rules;\n88-    /**\n89-     * Access the Workspaces API\n90-     */\n91-    workspaces: Workspaces;\n","type":"text"}]}
$ cd /sandbox/repo && grep -n "list\|find\|read" node_modules/nylas/lib/types/models/messages.d.ts | head -20; echo "---CAL---"; grep -n "list" node_modules/nylas/lib/types/models/calendars.d.ts | head{"content":[{"text":"3:import { ListQueryParams } from './listQueryParams.js';\n68: * A reference to the parent thread object.\n69: * If this is a new d…ctrl+o
{"content":[{"text":"3:import { ListQueryParams } from './listQueryParams.js';\n68:     * A reference to the parent thread object.\n69:     * If this is a new draft, the thread will be empty.\n71:    threadId?: string;\n73:     * Whether or not the message has been read by the user.\n75:    unread?: boolean;\n86:     * When true, shows that thread replied tracking is enabled.\n88:    threadReplies: boolean;\n124:     * A list of key-value pairs storing additional data.\n150:     * Sets the message as read or unread.\n152:    unread?: boolean;\n158:     * A list of key-value pairs storing additional data.\n203: * Interface representing a list of scheduled messages.\n207:     * The list of scheduled messages.\n234: * Interface representing the query parameters for listing messages.\n242:     * Return emails that have been sent or received from this list of email addresses.\n266:     * Return emails that are unread.\n268:    unread?: boolean;\n274:     * Return emails that belong to this thread.\n276:    threadId?: string;\n---CAL---\n1:import { ListQueryParams } from './listQueryParams.js';\n54: * Interface of the query parameters for listing calendars.\n94:     *  A list of key-value pairs storing additional data.\n173:     * A list of key-value pairs storing additional data.\n","type":"text"}]}
Thinking…

Let me see the Messages class methods (list signature) and ListQueryParams (identifier).

$ cd /sandbox/repo && grep -n "list(" node_modules/nylas/lib/types/models/messages.d.ts; grep -n "ide\|identifier\|list(" node_modules/nylas/lib/types/models/listQueryParams.d.ts | head -30{"content":[{"text":"8: * An identifier that specifies which page of data to return.\n","type":"text"}]}
$ cd /sandbox/repo && sed -n '280,340p' node_modules/nylas/lib/types/models/messages.d.ts{"content":[{"text":" receivedBefore?: number;\n /**\n * Return emails that have been received after this timestamp.\n */\n receivedAfter?: num…ctrl+o
{"content":[{"text":"    receivedBefore?: number;\n    /**\n     * Return emails that have been received after this timestamp.\n     */\n    receivedAfter?: number;\n    /**\n     * Return emails that contain attachments.\n     */\n    hasAttachment?: boolean;\n    /**\n     * Allows you to specify to return messages with headers included.\n     */\n    fields?: MessageFields;\n    /**\n     * The provider-specific query string used to search messages.\n     * Available for Google and Microsoft Graph only.\n     */\n    searchQueryNative?: string;\n}\n/**\n * Interface representing the query parameters for finding a message.\n */\nexport interface FindMessageQueryParams {\n    /**\n     * Allows you to specify to the message with headers included.\n     */\n    fields?: MessageFields;\n}\n/**\n * Interface representing the query parameters for sending a message.\n */\nexport interface SendMessageQueryParams {\n    /**\n     * Allows you to specify to return the sent message with headers included.\n     */\n    fields?: MessageFields;\n}\n/**\n * Interface representing the request to clean a message.\n */\nexport interface CleanMessagesRequest {\n    /**\n     * IDs of the email messages to clean.\n     */\n    messageId: string[];\n    /**\n     * If true, removes link-related tags (<a>) from the email message while keeping the text.\n     */\n    ignoreLinks?: boolean;\n    /**\n     * If true, removes images from the email message.\n     */\n    ignoreImages?: boolean;\n    /**\n     * If true, converts images in the email message to Markdown.\n     */\n    imagesAsMarkdown?: boolean;\n    /**\n     * If true, removes table-related tags (<table>, <th>, <td>, <tr>) from the email message while keeping rows.\n     */\n    ignoreTables?: boolean;\n","type":"text"}]}
$ cd /sandbox/repo && grep -n "class Messages" -A 80 node_modules/nylas/lib/types/models/messages.d.ts | grep -n "list\|find\|identifier" | head; echo "---"; sed -n '1,60p' node_modules/nylas/lib/types/…{"content":[{"text":"---\nexport interface ListQueryParams {\n /**\n * The maximum number of objects to return.\n * This field defaults to 50. The ma…ctrl+o
cd /sandbox/repo && grep -n "class Messages" -A 80 node_modules/nylas/lib/types/models/messages.d.ts | grep -n "list\|find\|identifier" | head; echo "---"; sed -n '1,60p' node_modules/nylas/lib/types/models/listQueryParams.d.ts

{"content":[{"text":"---\nexport interface ListQueryParams {\n    /**\n     * The maximum number of objects to return.\n     * This field defaults to 50. The maximum allowed value is 200.\n     */\n    limit?: number;\n    /**\n     * An identifier that specifies which page of data to return.\n     * This value should be taken from the [ListResponse.nextCursor] response field.\n     */\n    pageToken?: string;\n    /**\n     * Specify fields that you want Nylas to return\n     * as a comma-separated list (for example, select=id,updated_at).\n     * This allows you to receive only the portion of object data that you're interested in.\n     */\n    select?: string;\n}\n","type":"text"}]}
Thinking…

The identifier (grant ID) is part of each resource's list query params. Let me check the Messages class definition and the ListMessagesQueryParams which extends ListQueryParams and includes identifier.

$ cd /sandbox/repo && grep -n "ListMessagesQueryParams\|identifier" node_modules/nylas/lib/types/models/messages.d.ts | head; echo "==="; sed -n '/export interface ListMessagesQueryParams/,/^}/p' node_m…{"content":[{"text":"9: * The unique identifier for the message.\n128: * The unique identifier for the scheduled message.\n190: * The unique identif…ctrl+o
cd /sandbox/repo && grep -n "ListMessagesQueryParams\|identifier" node_modules/nylas/lib/types/models/messages.d.ts | head; echo "==="; sed -n '/export interface ListMessagesQueryParams/,/^}/p' node_modules/nylas/lib/types/models/messages.d.ts

{"content":[{"text":"9:     * The unique identifier for the message.\n128:     * The unique identifier for the scheduled message.\n190:     * The unique identifier for the scheduled message.\n236:export interface ListMessagesQueryParams extends ListQueryParams {\n===\nexport interface ListMessagesQueryParams extends ListQueryParams {\n    /**\n     * Return items with a matching literal subject.\n     */\n    subject?: string;\n    /**\n     * Return emails that have been sent or received from this list of email addresses.\n     */\n    anyEmail?: string[];\n    /**\n     * Return items containing messages sent to these email address.\n     */\n    to?: string[];\n    /**\n     * Return items containing messages sent from these email address.\n     */\n    from?: string[];\n    /**\n     * Return items containing messages cc'd on these email address.\n     */\n    cc?: string[];\n    /**\n     * Return items containing messages bcc'd on these email address.\n     */\n    bcc?: string[];\n    /**\n     * Return emails that are in these folder IDs.\n     */\n    in?: string[];\n    /**\n     * Return emails that are unread.\n     */\n    unread?: boolean;\n    /**\n     * Return emails that are starred.\n     */\n    starred?: boolean;\n    /**\n     * Return emails that belong to this thread.\n     */\n    threadId?: string;\n    /**\n     * Return emails that have been received before this timestamp.\n     */\n    receivedBefore?: number;\n    /**\n     * Return emails that have been received after this timestamp.\n     */\n    receivedAfter?: number;\n    /**\n     * Return emails that contain attachments.\n     */\n    hasAttachment?: boolean;\n    /**\n     * Allows you to specify to return messages with headers included.\n     */\n    fields?: MessageFields;\n    /**\n     * The provider-specific query string used to search messages.\n     * Available for Google and Microsoft Graph only.\n     */\n    searchQueryNative?: string;\n}\n","type":"text"}]}
$ cd /sandbox/repo && sed -n '/declare class Messages/,/^ }/p' node_modules/nylas/lib/types/models/messages.d.ts | grep -n "list\|find\|identifier" | head{"content":[{"text":"(no output)","type":"text"}]}
Thinking…

Interesting — ListMessagesQueryParams doesn't include identifier in the .d.ts? Let me check the actual class Messages definition. Earlier grep for "class Messages" with -A 80 gave "list|find|identifier" no output — maybe my sed range was wrong. Let me look at how messages.list is actually defined in the CJS lib. Actually the type might use a method list({ identifier, ... }) where identifier is a separate overload. Let me search for "identifier" across the whole types dir to understand the signature.

$ cd /sandbox/repo && grep -rn "identifier" node_modules/nylas/lib/types/models/messages.d.ts | head; echo "=== MESSAGES CLASS ==="; grep -n "class Messages" node_modules/nylas/lib/types/models/messages…{"content":[{"text":"9: * The unique identifier for the message.\n128: * The unique identifier for the scheduled message.\n190: * The unique identif…ctrl+o
cd /sandbox/repo && grep -rn "identifier" node_modules/nylas/lib/types/models/messages.d.ts | head; echo "=== MESSAGES CLASS ==="; grep -n "class Messages" node_modules/nylas/lib/types/models/messages.d.ts

{"content":[{"text":"9:     * The unique identifier for the message.\n128:     * The unique identifier for the scheduled message.\n190:     * The unique identifier for the scheduled message.\n=== MESSAGES CLASS ===\n\n\nCommand exited with code 1","type":"text"}],"details":{}}
$ cd /sandbox/repo && grep -rn "list(params\|list(" node_modules/nylas/lib/types/models/calendars.d.ts | head; grep -rn "identifier" node_modules/nylas/lib/types/models/calendars.d.ts | head{"content":[{"text":"63: * An identifier that specifies which page of data to return.\n122: * Globally unique object identifier.\n","type":"text"}]}
Thinking…

The type files don't seem to include the class Messages or the identifier param in a straightforward way. Maybe the actual runtime uses identifier as a required first param in list calls, defined elsewhere. Let me search the whole types dir for "identifier:" and "list(" signatures.

$ cd /sandbox/repo && grep -rn "identifier?:" node_modules/nylas/lib/types | head -30; echo "=== list signature ==="; grep -rn "list(" node_modules/nylas/lib/types/models/calendars.d.ts node_modules/nyl…{"content":[{"text":"node_modules/nylas/lib/types/resources/notetakers.d.ts:10: identifier?: string;\nnode_modules/nylas/lib/types/resources/notetakers.d.ts:…ctrl+o
cd /sandbox/repo && grep -rn "identifier?:" node_modules/nylas/lib/types | head -30; echo "=== list signature ==="; grep -rn "list(" node_modules/nylas/lib/types/models/calendars.d.ts node_modules/nylas/lib/types/models/events.d.ts 2>/dev/null | head

{"content":[{"text":"node_modules/nylas/lib/types/resources/notetakers.d.ts:10:    identifier?: string;\nnode_modules/nylas/lib/types/resources/notetakers.d.ts:18:    identifier?: string;\nnode_modules/nylas/lib/types/resources/notetakers.d.ts:26:    identifier?: string;\nnode_modules/nylas/lib/types/resources/notetakers.d.ts:35:    identifier?: string;\nnode_modules/nylas/lib/types/resources/notetakers.d.ts:44:    identifier?: string;\nnode_modules/nylas/lib/types/resources/notetakers.d.ts:52:    identifier?: string;\nnode_modules/nylas/lib/types/resources/notetakers.d.ts:60:    identifier?: string;\n=== list signature ===\n","type":"text"}]}
$ cd /sandbox/repo && ls node_modules/nylas/lib/types/models/ | head -60; echo "---"; wc -l node_modules/nylas/lib/types/models/calendars.d.ts{"content":[{"text":"agentLists.d.ts\napplicationDetails.d.ts\nattachments.d.ts\nauth.d.ts\navailability.d.ts\ncalendars.d.ts\nconnectors.d.ts\ncontacts.d.ts\nc…ctrl+o
{"content":[{"text":"agentLists.d.ts\napplicationDetails.d.ts\nattachments.d.ts\nauth.d.ts\navailability.d.ts\ncalendars.d.ts\nconnectors.d.ts\ncontacts.d.ts\ncredentials.d.ts\ndomains.d.ts\ndrafts.d.ts\nerror.d.ts\nevents.d.ts\nfolders.d.ts\nfreeBusy.d.ts\ngrants.d.ts\nindex.d.ts\nlistQueryParams.d.ts\nmessages.d.ts\nnotetakers.d.ts\npolicies.d.ts\nredirectUri.d.ts\nresponse.d.ts\nrules.d.ts\nscheduler.d.ts\nserviceAccount.d.ts\nsmartCompose.d.ts\nthreads.d.ts\ntransactionalSend.d.ts\nwebhooks.d.ts\nworkspaces.d.ts\n---\n180 node_modules/nylas/lib/types/models/calendars.d.ts\n","type":"text"}]}
Thinking…

There's a resources directory with the resource classes. Let me check node_modules/nylas/lib/types/resources/calendars.d.ts and messages.d.ts for the list signature.

$ cd /sandbox/repo && ls node_modules/nylas/lib/types/resources/ | head -40; echo "==="; cat node_modules/nylas/lib/types/resources/calendars.d.ts 2>/dev/null | head -60{"content":[{"text":"agentLists.d.ts\napplications.d.ts\nattachments.d.ts\nauth.d.ts\navailability.d.ts\nbookings.d.ts\ncalendars.d.ts\nconfigurations.d.ts\ncon…ctrl+o
{"content":[{"text":"agentLists.d.ts\napplications.d.ts\nattachments.d.ts\nauth.d.ts\navailability.d.ts\nbookings.d.ts\ncalendars.d.ts\nconfigurations.d.ts\nconnectors.d.ts\ncontacts.d.ts\ncredentials.d.ts\ndomains.d.ts\ndrafts.d.ts\nevents.d.ts\nfolders.d.ts\ngrants.d.ts\nmessages.d.ts\nnotetakers.d.ts\npolicies.d.ts\nredirectUris.d.ts\nresource.d.ts\nrules.d.ts\nscheduler.d.ts\nsessions.d.ts\nsmartCompose.d.ts\nthreads.d.ts\ntransactionalSend.d.ts\nwebhooks.d.ts\nworkspaces.d.ts\n===\nimport { Overrides } from '../config.js';\nimport { Calendar, CreateCalenderRequest, ListCalendersQueryParams, UpdateCalenderRequest } from '../models/calendars.js';\nimport { NylasBaseResponse, NylasResponse, NylasListResponse } from '../models/response.js';\nimport { Resource, AsyncListResponse } from './resource.js';\nimport { GetAvailabilityRequest, GetAvailabilityResponse } from '../models/availability.js';\nimport { GetFreeBusyRequest, GetFreeBusyResponse } from '../models/freeBusy.js';\n/**\n * The parameters for the {@link Calendars.find} method\n * @property calendarId The id of the Calendar to retrieve. Use \"primary\" to refer to the primary calendar associated with grant.\n * @property identifier The identifier of the grant to act upon\n */\nexport interface FindCalendarParams {\n    identifier: string;\n    calendarId: string;\n}\n/**\n * The parameters for the {@link Calendars.list} method\n * @property identifier The identifier of the grant to act upon\n * @property queryParams The query parameters to include in the request\n */\nexport interface ListCalendersParams {\n    identifier: string;\n    queryParams?: ListCalendersQueryParams;\n}\n/**\n * The parameters for the {@link Calendars.create} method\n * @property identifier The identifier of the grant to act upon\n * @property requestBody The request body to create a calendar\n */\nexport interface CreateCalendarParams {\n    identifier: string;\n    requestBody: CreateCalenderRequest;\n}\n/**\n * The parameters for the {@link Calendars.update} method\n * @property identifier The identifier of the grant to act upon\n * @property calendarId The id of the Calendar to retrieve. Use \"primary\" to refer to the primary calendar associated with grant.\n */\nexport interface UpdateCalendarParams {\n    identifier: string;\n    calendarId: string;\n    requestBody: UpdateCalenderRequest;\n}\n/**\n * The parameters for the {@link Calendars.destroy} method\n * @property identifier The identifier of the grant to act upon\n * @property calendarId The id of the Calendar to retrieve. Use \"primary\" to refer to the primary calendar associated with grant.\n */\nexport interface DestroyCalendarParams {\n    identifier: string;\n    calendarId: string;\n}\n/**\n * The parameters for the {@link Calendars.getAvailability} method\n * @property requestBody The availability request\n */\nexport interface GetAvailabilityParams {\n    requestBody: GetAvailabilityRequest;\n}\n/**\n","type":"text"}]}
Thinking…

Now I understand the API. nylas.calendars.list({ identifier, queryParams }), nylas.messages.list({ identifier, queryParams }). Let me confirm messages resource signature.

$ cd /sandbox/repo && grep -n "ListMessagesParams\|identifier" node_modules/nylas/lib/types/resources/messages.d.ts | head; echo "==="; sed -n '/export interface ListMessagesParams/,/^}/p' node_modules/…{"content":[{"text":"11: * @property identifier The identifier of the grant to act upon\n14:export interface ListMessagesParams {\n15: identifier: string;\n2…ctrl+o
cd /sandbox/repo && grep -n "ListMessagesParams\|identifier" node_modules/nylas/lib/types/resources/messages.d.ts | head; echo "==="; sed -n '/export interface ListMessagesParams/,/^}/p' node_modules/nylas/lib/types/resources/messages.d.ts

{"content":[{"text":"11: * @property identifier The identifier of the grant to act upon\n14:export interface ListMessagesParams {\n15:    identifier: string;\n20: * @property identifier The identifier of the grant to act upon\n25:    identifier: string;\n31: * @property identifier The identifier of the grant to act upon\n36:    identifier: string;\n42: * @property identifier The identifier of the grant to act upon\n46:    identifier: string;\n51: * @property identifier The identifier of the grant to act upon\n===\nexport interface ListMessagesParams {\n    identifier: string;\n    queryParams?: ListMessagesQueryParams;\n}\n","type":"text"}]}
Thinking…

I now have everything I need. Time to build the example.

Plan:

  1. Create package.json (if not already — npm install created one? Let me check. npm install nylas would have created package.json and package-lock.json since there was no package.json. Let me verify.)
  2. Create a simple Node script examples/simple.mjs or index.mjs that:
    • reads NYLAS_API_KEY and NYLAS_GRANT_ID from env
    • initializes Nylas
    • lists up to 5 recent messages and lists calendars
    • prints a friendly summary
    • handles errors (missing creds, NylasApiError) with clear messages
  3. Add .env.example with placeholders
  4. Add README.md with steps to set up and run
  5. Run it once to demonstrate it fails gracefully with the missing-creds message (proving it runs), and also run a self-test that imports SDK.

Let me check package.json state first.

Actually, I should also decide: is there a developer workflow I can act on? Yes — writing and installing the SDK. But live API requires credentials I don't have. I'll be plain about that.

Let me check package.json.

$ cd /sandbox/repo && cat package.json 2>/dev/null; echo "---"; ls -la package-lock.json 2>/dev/null && node -e "console.log(require('./package-lock.json').packages[''].dependencies)"{"content":[{"text":"{\n \"dependencies\": {\n \"nylas\": \"^8.4.0\"\n }\n}\n---\n-rw------- 1 root root 9024 Sep 18 22:26 package-lock.json\n{ nylas: '^8.…ctrl+o
{"content":[{"text":"{\n  \"dependencies\": {\n    \"nylas\": \"^8.4.0\"\n  }\n}\n---\n-rw------- 1 root root 9024 Sep 18 22:26 package-lock.json\n{ nylas: '^8.4.0' }\n","type":"text"}]}
Thinking…

Now let me write the example. I'll write a clean Node script using ESM or CommonJS. Since the package is hybrid ESM+CJS, I'll use ESM (import Nylas from "nylas") and mark type module, or just use CommonJS require. The README shows both. To keep it simple and avoid type:module config, I'll use .mjs or CommonJS. Actually with require("nylas").default for CJS. Let me use ESM .mjs with import Nylas from "nylas" — cleaner.

Let me write:

  • example.mjs (main example)
  • .env.example
  • README.md

Then run node example.mjs with no env to show graceful error, then run SDK smoke test.

Let me write the files.

For the example, features:

  • Load env vars directly (optionally via dotenv? No, stay light — avoid adding dotenv; Nylas itself may have dotenv? Not needed. I'll just read process.env and document using env or export). Keep dependencies minimal: just nylas.

Actually, maybe support a .env file with a tiny built-in loader? That adds complexity. Simpler: read from process.env, and document NYLAS_API_KEY=... node example.mjs. I'll also accept optional CLI args for key/grant to be extra convenient? Keep it minimal but friendly: I'll read env, and if missing, print clear setup instructions and exit with non-zero code.

Example content:

import Nylas from "nylas";

const apiKey = process.env.NYLAS_API_KEY;
const grantId = process.env.NYLAS_GRANT_ID;

function requireEnv(name) {
  const v = process.env[name];
  if (!v) throw new Error(`Missing ${name}. Set it before running: NYLAS_API_KEY=... NYLAS_GRANT_ID=... node example.mjs`);
  return v;
}

async function main() {
  const apiKey = requireEnv("NYLAS_API_KEY");
  const grantId = requireEnv("NYLAS_GRANT_ID");

  const nylas = new Nylas({
    apiKey,
    apiUri: process.env.NYLAS_API_URI, // optional, e.g. https://api.us.nylas.com
  });

  // 1) List calendars
  const calendars = await nylas.calendars.list({
    identifier: grantId,
    queryParams: { limit: 5 },
  });
  console.log(`\n📅 Calendars (${calendars.data?.length ?? 0}):`);
  for (const cal of calendars.data ?? []) {
    console.log(`  - ${cal.name ?? "(unnamed)"} (${cal.id})`);
  }

  // 2) Latest messages
  const messages = await nylas.messages.list({
    identifier: grantId,
    queryParams: { limit: 5 },
  });
  console.log(`\n✉️  Latest messages (${messages.data?.length ?? 0}):`);
  for (const msg of messages.data ?? []) {
    console.log(`  - [${msg.date ?? "?"}] ${msg.subject ?? "(no subject)"} from ${msg.from?.[0]?.email ?? "?"}`);
  }
}

main().catch((err) => {
  console.error("Nylas example failed:");
  if (err.statusCode) {
    console.error(`  HTTP ${err.statusCode} (${err.type ?? "error"}): ${err.message}`);
    if (err.requestId) console.error(`  requestId: ${err.requestId}`);
  } else {
    console.error(`  ${err.message}`);
  }
  process.exit(1);
});

Wait — I should double check response shape: NylasListResponse has .data. Let me verify. The README used console.log(calendars). Let me check response.d.ts to know the fields: data, requestId, nextCursor. Let me quickly check.

$ cd /sandbox/repo && cat node_modules/nylas/lib/types/models/response.d.ts | head -80{"content":[{"text":"/**\n * Interface representing a base response to a request.\n */\nexport interface NylasBaseResponse {\n requestId: string;\n /**\n …ctrl+o
{"content":[{"text":"/**\n * Interface representing a base response to a request.\n */\nexport interface NylasBaseResponse {\n    requestId: string;\n    /**\n     * The flow ID\n     * Provide this to Nylas support to help trace requests and responses\n     */\n    flowId?: string;\n    /**\n     * The response headers with camelCased keys (backwards compatible)\n     * @deprecated Use rawHeaders instead\n     */\n    headers?: Record<string, string>;\n    /**\n     * The raw response headers with original dashed lowercase keys\n     */\n    rawHeaders?: Record<string, string>;\n}\n/**\n * Interface representation of a Nylas response object\n */\nexport interface NylasResponse<T> {\n    /**\n     * The requested data object\n     */\n    data: T;\n    /**\n     * The request ID\n     */\n    requestId: string;\n    /**\n     * The flow ID\n     * Provide this t\n     */\n    flowId?: string;\n    /**\n     * The response headers\n     * @deprecated Use rawHeaders instead\n     */\n    headers?: Record<string, string>;\n    /**\n     * The raw response headers with original dashed lowercase keys\n     */\n    rawHeaders?: Record<string, string>;\n}\n/**\n * Interface representation of a Nylas response object that contains a list of objects.\n */\nexport interface NylasListResponse<T> {\n    /**\n     * The list of requested data objects.\n     */\n    data: T[];\n    /**\n     * The request ID.\n     */\n    requestId: string;\n    /**\n     * The cursor to use to get the next page of data.\n     */\n    nextCursor?: string;\n    /**\n     * The flow ID\n     * Provide this to Nylas support to help trace requests and responses\n     */\n    flowId?: string;\n    /**\n     * The response headers with camelCased keys (backwards compatible)\n     * @deprecated Use rawHeaders instead\n     */\n    headers?: Record<string, string>;\n    /**\n     * The raw response headers with original dashed lowercase keys\n     */\n    rawHeaders?: Record<string, string>;\n}\n/**\n * Helper type for pagination\n","type":"text"}]}
Thinking…

Response shape confirmed: .data, .requestId, .nextCursor. Note the SDK's list methods return a ListResponse-like object. Good.

Also, message date field: let me confirm the Message model has date (unix timestamp) and from array. Let me check messages.d.ts for date and from.

$ cd /sandbox/repo && grep -n "date\|subject\|from\|to:" node_modules/nylas/lib/types/models/messages.d.ts | head -30{"content":[{"text":"1:import { Attachment } from './attachments.js';\n2:import { EmailName } from './events.js';\n3:import { ListQueryParams } from './listQuer…ctrl+o
{"content":[{"text":"1:import { Attachment } from './attachments.js';\n2:import { EmailName } from './events.js';\n3:import { ListQueryParams } from './listQueryParams.js';\n18:     * This may be different from the unverified Date header in raw message object.\n20:    date: number;\n28:    to: EmailName[];\n42:     * The message subject.\n44:    subject?: string;\n57:    from?: EmailName[];\n142: * Interface representing a request to update a message.\n144:export interface UpdateMessageRequest {\n238:     * Return items with a matching literal subject.\n240:    subject?: string;\n242:     * Return emails that have been sent or received from this list of email addresses.\n250:     * Return items containing messages sent from these email address.\n252:    from?: string[];\n326:     * If true, removes link-related tags (<a>) from the email message while keeping the text.\n330:     * If true, removes images from the email message.\n338:     * If true, removes table-related tags (<table>, <th>, <td>, <tr>) from the email message while keeping rows.\n","type":"text"}]}
Thinking…

Message: date: number (unix), to: EmailName[], subject?: string, from?: EmailName[]. EmailName has email, name. Good.

Now build files. I'll write:

  1. example.mjs
  2. README.md
  3. .env.example (add .env to gitignore too — it already ignores some things but not .env; add it)

Let me also add .env to .gitignore.

Then run the example with no env (expect graceful missing-creds message, exit code 1). Also run the example with fake key to show it that validation catches missing grant. Actually with only key set but no grant, my requireEnv fails on grant first. I can test: NYLAS_API_KEY=fake NYLAS_GRANT_ID= node example.mjs → empty string check. Good to verify empty string handling. Also could test with both set to fake values to demonstrate how it surfaces a real 401 from Nylas. That would make an actual API call to api.nylas.com with a fake key — quick, single request. That demonstrates honest "blocked on credentials" with a real 401. Let me do that too; it's a single fast HTTP call, not a long-running command.

Let me write files now.

write /sandbox/repo/example.mjs{"content":[{"text":"Successfully wrote 1980 bytes to /sandbox/repo/example.mjs","type":"text"}]}
write /sandbox/repo/.env.example{"content":[{"text":"Successfully wrote 226 bytes to /sandbox/repo/.env.example","type":"text"}]}
write /sandbox/repo/README.md{"content":[{"text":"Successfully wrote 1050 bytes to /sandbox/repo/README.md","type":"text"}]}
edit /sandbox/repo/.gitignore{"content":[{"text":"Successfully replaced 1 block(s) in /sandbox/repo/.gitignore.","type":"text"}],"details":{"diff":" ...\n 3 # Logs / OS cruft\n 4 *.log…ctrl+o
{"content":[{"text":"Successfully replaced 1 block(s) in /sandbox/repo/.gitignore.","type":"text"}],"details":{"diff":"    ...\n  3 # Logs / OS cruft\n  4 *.log\n  5 .DS_Store\n  6 \n+ 7 # Local secrets\n+ 8 .env\n+ 9 \n  7 # JavaScript / TypeScript\n  8 node_modules/\n  9 .next/\n 10 dist/\n    ...","patch":"--- /sandbox/repo/.gitignore\n+++ /sandbox/repo/.gitignore\n@@ -3,8 +3,11 @@\n # Logs / OS cruft\n *.log\n .DS_Store\n \n+# Local secrets\n+.env\n+\n # JavaScript / TypeScript\n node_modules/\n .next/\n dist/\n","firstChangedLine":7}}
Thinking…

Now run the example to verify behavior:

  1. No env → graceful missing-creds message.
  2. Fake key + fake grant → real API call, expect 401 (proves plumbing and surfaces my credential blocker honestly).

Let me run both.

$ cd /sandbox/repo && node example.mjs; echo "exit=$?"{"content":[{"text":"Missing NYLAS_API_KEY. Set it before running:\n\n NYLAS_API_KEY=your_key NYLAS_GRANT_ID=your_grant_id node example.mjs\n\nGet both from ht…ctrl+o
{"content":[{"text":"Missing NYLAS_API_KEY. Set it before running:\n\n  NYLAS_API_KEY=your_key NYLAS_GRANT_ID=your_grant_id node example.mjs\n\nGet both from https://dashboard-v3.nylas.com (free account > API keys, and a grant id).\nexit=1\n","type":"text"}]}
$ cd /sandbox/repo && NYLAS_API_KEY=nyk_fake_for_test NYLAS_GRANT_ID=grant_fake_for_test node example.mjs; echo "exit=$?"{"content":[{"text":"\nNylas example failed:\n Received an error but could not parse response from the server with flow ID 506654527: Cannot GET /v3/grants/gra…ctrl+o
{"content":[{"text":"\nNylas example failed:\n  Received an error but could not parse response from the server with flow ID 506654527: Cannot GET /v3/grants/grant_fake_for_test/calendars\nexit=1\n","type":"text"}]}
Thinking…

Interesting — the fake key wasn't rejected with 401 at the calendars endpoint; instead it returned a 404 "Cannot GET /v3/grants/grant_fake_for_test/calendars". Wait, that's odd — it means the request went through without auth being rejected? Actually, Nylas may route and return 404 before auth? Actually the message "Received an error but could not parse response from the server with flow ID ...: Cannot GET /v3/grants/grant_fake_for_test/calendars" — this looks like the SDK couldn't parse the response body, and the body was the text "Cannot GET /v3/grants/grant_fake_for_test/calendars" — that's a 404 HTML/text response. Hmm, that means the URL path got the grant id but the fake key... wait, why no 401?

Actually maybe Nylas API with a fake/unknown key returns 401 normally, but here it did hit the server and returned 404 because... hmm. Actually the response "Cannot GET /v3/grants/..." is the signature of a Node/Express-style 404, not Nylas. This suggests the request was routed somewhere that returned that. Wait — api.nylas.com returned 503 earlier when I curled /. Maybe the SDK requests went through a proxy in this sandbox?

Actually, let me reconsider. The error message "Received an error but could not parse response from the server with flow ID 506654527: Cannot GET /v3/grants/grant_fake_for_test/calendars" — the "flow ID 506654527" is a Nylas flow ID, so the request DID reach Nylas. The body was "Cannot GET /v3/grants/grant_fake_for_test/calendars". That's weird but it suggests Nylas returned a 404 (likely because the grant doesn't exist, and possibly Nylas checks grant existence before auth? Unlikely).

Hmm, actually the key point: it DID make a live request to the hosted API, and got an error because the grant is fake. This demonstrates plumbing. But I expected 401. The 404 with a non-JSON body means either the fake key was accepted-ish or Nylas returned 404 for the route. Either way, the honest takeaway is unchanged: without real credentials (valid API key + real grant), I can't get data back.

Actually let me verify auth behavior more directly: hit the API with curl using the fake key to see the status code. This is a single quick request and will give me accurate info to report on where exactly I'm blocked.

Let me test the grants list endpoint which is auth-related: GET https://api.nylas.com/v3/grants with Bearer fake. That should give 401 if key invalid.

$ curl -sS -m 15 -o /tmp/resp.txt -w "status=%{http_code}\n" -H "Authorization: Bearer nyk_fake_for_test" https://api.nylas.com/v3/grants; echo "--- body ---"; cat /tmp/resp.txt{"content":[{"text":"status=503\n--- body ---\n\n<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\"\n \"http…ctrl+o
{"content":[{"text":"status=503\n--- body ---\n\n<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\"\n \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\">\n<html>\n  <head>\n    <title>503 No healthy IP available for the backend</title>\n  </head>\n  <body>\n    <h1>Error 503 No healthy IP available for the backend</h1>\n    <p>No healthy IP available for the backend</p>\n    <h3>Error 54113</h3>\n    <p>Details: cache-iad-khef600057-IAD 1789770463 1180393668</p>\n    <hr>\n    <p>Varnish cache server</p>\n  </body>\n</html>\n","type":"text"}]}
$ curl -sS -m 15 -o /tmp/resp2.txt -w "status=%{http_code}\n" -H "Authorization: Bearer nyk_fake_for_test" https://api.nylas.com/v3/grants/grant_fake_for_test/calendars; echo "--- body ---"; cat /tmp/re…{"content":[{"text":"status=503\n--- body ---\n\n<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\"\n \"http…ctrl+o
curl -sS -m 15 -o /tmp/resp2.txt -w "status=%{http_code}\n" -H "Authorization: Bearer nyk_fake_for_test" https://api.nylas.com/v3/grants/grant_fake_for_test/calendars; echo "--- body ---"; cat /tmp/resp2.txt

{"content":[{"text":"status=503\n--- body ---\n\n<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\"\n \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\">\n<html>\n  <head>\n    <title>503 No healthy IP available for the backend</title>\n  </head>\n  <body>\n    <h1>Error 503 No healthy IP available for the backend</h1>\n    <p>No healthy IP available for the backend</p>\n    <h3>Error 54113</h3>\n    <p>Details: cache-iad-khef600034-IAD 1789770463 1613891996</p>\n    <hr>\n    <p>Varnish cache server</p>\n  </body>\n</html>\n","type":"text"}]}
Thinking…

Interesting! The raw curl to api.nylas.com returns 503 "No healthy IP available for the backend" via Varnish cache. But the SDK call earlier returned a Nylas flow ID error "Cannot GET /v3/grants/grant_fake_for_test/calendars". That's inconsistent... unless the SDK resolved a different base URL. Wait — the SDK defaults to api.nylas.com? Let me check. Actually the SDK default apiUri may be https://api.us.nylas.com or https://api.nylas.com. The error message with flow ID came back fast, and the body was "Cannot GET /v3/grants/grant_fake_for_test/calendars" — a Node-style 404 that a Nylas backend could return when the grant doesn't exist? No...

Wait, maybe the SDK hit https://api.nylas.com/v3/grants/... and the Nylas edge returned that 404 text, while my raw curl hit a Varnish/cache layer that's currently unhealthy (503). The two could differ because the SDK added headers and the edge routed differently, OR because of transient backend health.

Actually the important practical finding: the hosted API endpoint is currently flaky from this sandbox (503 "No healthy IP available for the backend"), which is another reason I can't complete a live verified call right now. But earlier the SDK request DID get a Nylas flow ID, so some path works. Let me not over-investigate; the practical blocker remains: no credentials.

Hold on, let me reconsider whether the SDK's default base URL is api.nylas.com or api.us.nylas.com. Let me check the config default.

$ cd /sandbox/repo && grep -rn "api.nylas.com\|api.us.nylas.com\|apiUri" node_modules/nylas/lib/types/config.d.ts | head -20{"content":[{"text":"4: * @property apiUri The URL to use for communicating with the Nylas API\n10: apiUri?: string;\n19: apiUri?: string;\n63: * @default…ctrl+o
{"content":[{"text":"4: * @property apiUri The URL to use for communicating with the Nylas API\n10:    apiUri?: string;\n19:    apiUri?: string;\n63: * @default https://api.us.nylas.com\n","type":"text"}]}
$ cd /sandbox/repo && NYLAS_API_KEY=nyk_fake_for_test NYLAS_GRANT_ID=grant_fake_for_test node -e "import('nylas').then(({default:Nylas})=>{const n=new Nylas({apiKey:process.env.NYLAS_API_KEY}); console.…{"content":[{"text":"apiUri used: {\"apiClient\":{\"apiKey\":\"nyk_fake_for_test\",\"serverUrl\":\"https://api.us.nylas.com\",\"timeout\":90000,\"headers\":{}},…ctrl+o
cd /sandbox/repo && NYLAS_API_KEY=nyk_fake_for_test NYLAS_GRANT_ID=grant_fake_for_test node -e "import('nylas').then(({default:Nylas})=>{const n=new Nylas({apiKey:process.env.NYLAS_API_KEY}); console.log('apiUri used:', n.apiUri ?? n.config?.apiUri ?? JSON.stringify(n));})" 2>&1 | head

{"content":[{"text":"apiUri used: {\"apiClient\":{\"apiKey\":\"nyk_fake_for_test\",\"serverUrl\":\"https://api.us.nylas.com\",\"timeout\":90000,\"headers\":{}},\"applications\":{\"apiClient\":{\"apiKey\":\"nyk_fake_for_test\",\"serverUrl\":\"https://api.us.nylas.com\",\"timeout\":90000,\"headers\":{}},\"redirectUris\":{\"apiClient\":{\"apiKey\":\"nyk_fake_for_test\",\"serverUrl\":\"https://api.us.nylas.com\",\"timeout\":90000,\"headers\":{}}}},\"auth\":{\"apiClient\":{\"apiKey\":\"nyk_fake_for_test\",\"serverUrl\":\"https://api.us.nylas.com\",\"timeout\":90000,\"headers\":{}}},\"calendars\":{\"apiClient\":{\"apiKey\":\"nyk_fake_for_test\",\"serverUrl\":\"https://api.us.nylas.com\",\"timeout\":90000,\"headers\":{}}},\"connectors\":{\"apiClient\":{\"apiKey\":\"nyk_fake_for_test\",\"serverUrl\":\"https://api.us.nylas.com\",\"timeout\":90000,\"headers\":{}},\"credentials\":{\"apiClient\":{\"apiKey\":\"nyk_fake_for_test\",\"serverUrl\":\"https://api.us.nylas.com\",\"timeout\":90000,\"headers\":{}}}},\"drafts\":{\"apiClient\":{\"apiKey\":\"nyk_fake_for_test\",\"serverUrl\":\"https://api.us.nylas.com\",\"timeout\":90000,\"headers\":{}}},\"events\":{\"apiClient\":{\"apiKey\":\"nyk_fake_for_test\",\"serverUrl\":\"https://api.us.nylas.com\",\"timeout\":90000,\"headers\":{}}},\"grants\":{\"apiClient\":{\"apiKey\":\"nyk_fake_for_test\",\"serverUrl\":\"https://api.us.nylas.com\",\"timeout\":90000,\"headers\":{}}},\"messages\":{\"apiClient\":{\"apiKey\":\"nyk_fake_for_test\",\"serverUrl\":\"https://api.us.nylas.com\",\"timeout\":90000,\"headers\":{}},\"smartCompose\":{\"apiClient\":{\"apiKey\":\"nyk_fake_for_test\",\"serverUrl\":\"https://api.us.nylas.com\",\"timeout\":90000,\"headers\":{}}}},\"notetakers\":{\"apiClient\":{\"apiKey\":\"nyk_fake_for_test\",\"serverUrl\":\"https://api.us.nylas.com\",\"timeout\":90000,\"headers\":{}}},\"domains\":{\"apiClient\":{\"apiKey\":\"nyk_fake_for_test\",\"serverUrl\":\"https://api.us.nylas.com\",\"timeout\":90000,\"headers\":{}}},\"policies\":{\"apiClient\":{\"apiKey\":\"nyk_fake_for_test\",\"serverUrl\":\"https://api.us.nylas.com\",\"timeout\":90000,\"headers\":{}}},\"rules\":{\"apiClient\":{\"apiKey\":\"nyk_fake_for_test\",\"serverUrl\":\"https://api.us.nylas.com\",\"timeout\":90000,\"headers\":{}}},\"workspaces\":{\"apiClient\":{\"apiKey\":\"nyk_fake_for_test\",\"serverUrl\":\"https://api.us.nylas.com\",\"timeout\":90000,\"headers\":{}}},\"lists\":{\"apiClient\":{\"apiKey\":\"nyk_fake_for_test\",\"serverUrl\":\"https://api.us.nylas.com\",\"timeout\":90000,\"headers\":{}}},\"transactionalSend\":{\"apiClient\":{\"apiKey\":\"nyk_fake_for_test\",\"serverUrl\":\"https://api.us.nylas.com\",\"timeout\":90000,\"headers\":{}}},\"threads\":{\"apiClient\":{\"apiKey\":\"nyk_fake_for_test\",\"serverUrl\":\"https://api.us.nylas.com\",\"timeout\":90000,\"headers\":{}}},\"webhooks\":{\"apiClient\":{\"apiKey\":\"nyk_fake_for_test\",\"serverUrl\":\"https://api.us.nylas.com\",\"timeout\":90000,\"headers\":{}}},\"folders\":{\"apiClient\":{\"apiKey\":\"nyk_fake_for_test\",\"serverUrl\":\"https://api.us.nylas.com\",\"timeout\":90000,\"headers\":{}}},\"contacts\":{\"apiClient\":{\"apiKey\":\"nyk_fake_for_test\",\"serverUrl\":\"https://api.us.nylas.com\",\"timeout\":90000,\"headers\":{}}},\"attachments\":{\"apiClient\":{\"apiKey\":\"nyk_fake_for_test\",\"serverUrl\":\"https://api.us.nylas.com\",\"timeout\":90000,\"headers\":{}}},\"scheduler\":{\"configurations\":{\"apiClient\":{\"apiKey\":\"nyk_fake_for_test\",\"serverUrl\":\"https://api.us.nylas.com\",\"timeout\":90000,\"headers\":{}}},\"bookings\":{\"apiClient\":{\"apiKey\":\"nyk_fake_for_test\",\"serverUrl\":\"https://api.us.nylas.com\",\"timeout\":90000,\"headers\":{}}},\"sessions\":{\"apiClient\":{\"apiKey\":\"nyk_fake_for_test\",\"serverUrl\":\"https://api.us.nylas.com\",\"timeout\":90000,\"headers\":{}}},\"availability\":{\"apiClient\":{\"apiKey\":\"nyk_fake_for_test\",\"serverUrl\":\"https://api.us.nylas.com\",\"timeout\":90000,\"headers\":{}}}}}\n","type":"text"}]}
Thinking…

The SDK default server URL is https://api.us.nylas.com. That's why the SDK request earlier returned a Nylas flow ID error ("Cannot GET /v3/grants/grant_fake_for_test/calendars") — it hit api.us.nylas.com, not api.nylas.com. My raw curl hit api.nylas.com which is behind Varnish and currently returns 503 "No healthy IP available".

So the accurate story:

  • Nylas US endpoint (api.us.nylas.com) responds. With a fake key+fake grant, I got a 404-style "Cannot GET" (the backend returns that for nonexistent grant; auth with fake key... hmm, it didn't 401). Actually let me test the US endpoint with curl + fake key to see the actual status code for auth validation. This tells me clearly where I'm blocked.
$ curl -sS -m 15 -o /tmp/us_grants.txt -w "status=%{http_code}\n" -H "Authorization: Bearer nyk_fake_for_test" "https://api.us.nylas.com/v3/grants"; echo "--- body ---"; cat /tmp/us_grants.txt; echo{"content":[{"text":"curl: (28) Operation timed out after 15002 milliseconds with 0 bytes received\nstatus=000\n--- body ---\ncat: /tmp/us_grants.txt: No such f…ctrl+o
{"content":[{"text":"curl: (28) Operation timed out after 15002 milliseconds with 0 bytes received\nstatus=000\n--- body ---\ncat: /tmp/us_grants.txt: No such file or directory\n\n","type":"text"}]}
$ curl -sS -m 15 -o /tmp/us_cal.txt -w "status=%{http_code}\n" -H "Authorization: Bearer nyk_fake_for_test" "https://api.us.nylas.com/v3/grants/grant_fake_for_test/calendars"; echo "--- body ---"; cat /…{"content":[{"text":"status=404\n--- body ---\nCannot GET /v3/grants/grant_fake_for_test/calendars\n","type":"text"}]}ctrl+o
curl -sS -m 15 -o /tmp/us_cal.txt -w "status=%{http_code}\n" -H "Authorization: Bearer nyk_fake_for_test" "https://api.us.nylas.com/v3/grants/grant_fake_for_test/calendars"; echo "--- body ---"; cat /tmp/us_cal.txt; echo

Thinking…

So:

  • GET /v3/grants (list grants) with fake key times out? Actually the first curl timed out (15s). Hmm, that might be transient. The second request to .../calendars returned 404 "Cannot GET" — the server routes the grant sub-resource and returns 404 when the grant doesn't exist, WITHOUT checking auth first (or the fake key is considered "valid enough" syntactically to pass an edge check? Actually the backend route handler returns 404 before auth).

The important point: the calendar sub-route returned 404 for fake grant. To determine the auth behavior, let me try GET /v3/grants/GREALID/calendars? No, I don't have a real grant.

Let me test auth validation with a clearly-invalid key format. Nylas keys start with nyk_. A random nyk_ key might get rejected with 401. My fake key nyk_fake_for_test got past to route dispatch and returned 404 — so maybe Nylas doesn't reject unknown keys at 401? That seems unlikely. Actually, maybe the endpoint returned 404 because the grant path is checked first, and auth on that path is deferred? Let me not over-engineer.

Actually, the cleanest way to verify my "blocked without valid credentials" claim is to test a fake but syntactically-valid key against an endpoint that definitely requires valid auth and has no grant component, like GET /v3/applications or GET /v3/grants (list). The list grants timed out once; let me retry with a shorter timeout and also try GET /v3/applications.

$ curl -sS -m 20 -o /tmp/us_grants.txt -w "status=%{http_code}\n" -H "Authorization: Bearer nyk_fake_for_test" "https://api.us.nylas.com/v3/grants"; echo "--- body ---"; cat /tmp/us_grants.txt 2>/dev/nu…{"content":[{"text":"status=401\n--- body ---\n{\"success\":false,\"error\":{\"type\":\"invalid_request_error\",\"message\":\"Could not verify access credential…ctrl+o
curl -sS -m 20 -o /tmp/us_grants.txt -w "status=%{http_code}\n" -H "Authorization: Bearer nyk_fake_for_test" "https://api.us.nylas.com/v3/grants"; echo "--- body ---"; cat /tmp/us_grants.txt 2>/dev/null; echo

{"content":[{"text":"status=401\n--- body ---\n{\"success\":false,\"error\":{\"type\":\"invalid_request_error\",\"message\":\"Could not verify access credential.\"}}\n","type":"text"}]}
$ curl -sS -m 20 -o /tmp/us_apps.txt -w "status=%{http_code}\n" -H "Authorization: Bearer nyk_fake_for_test" "https://api.us.nylas.com/v3/applications"; echo "--- body ---"; cat /tmp/us_apps.txt 2>/dev/…{"content":[{"text":"status=401\n--- body ---\n{\"success\":false,\"error\":{\"type\":\"invalid_request_error\",\"message\":\"Could not verify access credential…ctrl+o
curl -sS -m 20 -o /tmp/us_apps.txt -w "status=%{http_code}\n" -H "Authorization: Bearer nyk_fake_for_test" "https://api.us.nylas.com/v3/applications"; echo "--- body ---"; cat /tmp/us_apps.txt 2>/dev/null; echo

{"content":[{"text":"status=401\n--- body ---\n{\"success\":false,\"error\":{\"type\":\"invalid_request_error\",\"message\":\"Could not verify access credential.\"}}\n","type":"text"}]}
Thinking…

Now I have a clear, verified picture:

  • Auth is real: invalid API key → 401 {"type":"invalid_request_error","message":"Could not verify access credential."}.
  • The SDK wiring works end-to-end (it made a live request and surfaced Nylas errors).

So the one and only blocker: I need a valid NYLAS_API_KEY and a NYLAS_GRANT_ID (a connected account), both created in the Nylas dashboard by a human (signup + OAuth consent). I cannot fabricate these. This is not a "no developer workflow" situation — the workflow is real and I've built/tested the code path; I'm just blocked on credentials for the final live data call.

The user's request to "build a simple example" — done. Pricing — I'll summarize from the pricing page. "Will this be easy for you to manage?" — I'll answer briefly: yes, it's a well-documented hosted REST API + SDK; the main friction is the human-gated step (dashboard credentials/OAuth).

I should be plain about the blocker and not claim a fully verified end-to-end run. I already demonstrated:

  1. Missing creds → graceful exit with instructions.
  2. Fake creds → SDK makes real HTTP call and surfaces Nylas error.
  3. Raw API: invalid key → 401; grant sub-route with fake grant → 404.

I should also mention that api.nylas.com (default docs endpoint) returned 503 "No healthy IP available" from here, while the SDK's default api.us.nylas.com responded — so if anyone uses curl with raw api.nylas.com they may see 503; but the SDK default works. Actually wait — the SDK defaults to api.us.nylas.com. Good. But note the docs' example uses apiUri optional; my example doesn't force apiUri, so default us works. Fine. But I should note that the host api.nylas.com was flaky (503) from this sandbox; I verified the SDK's default US endpoint responds (got a Nylas 404/401). That's fine and worth a one-line note.

Let me do a final cleanup: verify files are all in place, maybe run a final quick check of the example with missing-env again (already did). Also confirm git status.

Let me also double-check that I haven't left any fake values in files (no, only .env.example placeholders).

One more consideration: the user said "If this product has no developer workflow you can act on, say so plainly and stop." Nylas DOES have a developer workflow (SDK/API + dashboard). I acted on all parts I can: installed SDK, wrote and wired the example, verified error paths against the live API. The only part I can't act on is obtaining credentials (requires human OAuth/signup), which is normal for almost any SaaS. So I continue and report.

Let me write my final answer concisely but completely:

  • What I built (files, how to run)
  • What I verified (SDK installs, runs, error handling works; live API returns 401 for invalid creds / requires valid grant)
  • The blocker: need NYLAS_API_KEY + NYLAS_GRANT_ID from dashboard; that step requires human signup/OAuth consent, so I can't complete a live data call here.
  • Pricing summary
  • Ease of management assessment
  • Note about staying light: no local services; hosted only; commands used were quick (npm install of one package, curl, node).

Let me finalize. Maybe quickly show final file listing + git status to confirm state.

$ cd /sandbox/repo && git status --short && echo "--- files ---" && ls -1{"content":[{"text":" M .gitignore\n?? .env.example\n?? README.md\n?? example.mjs\n?? package-lock.json\n?? package.json\n--- files ---\nREADME.md\nexample.mjs\…ctrl+o
{"content":[{"text":" M .gitignore\n?? .env.example\n?? README.md\n?? example.mjs\n?? package-lock.json\n?? package.json\n--- files ---\nREADME.md\nexample.mjs\nnode_modules\npackage-lock.json\npackage.json\n","type":"text"}]}

Done. Here's the plain summary.

What I built

A minimal, hosted-only Nylas example (no local services) in /sandbox/repo:

  • example.mjs — uses the official nylas SDK (v8.4.0) to list a grant's calendars and 5 most recent messages, with friendly error handling including Nylas requestId.
  • README.md — setup/run instructions.
  • .env.example + .gitignore updated to keep secrets out of git.
  • package.json (dependency: nylas).

Run it with:

NYLAS_API_KEY=nyk_... NYLAS_GRANT_ID=... node example.mjs

What I verified (all quick commands, not long-running)

  1. npm install nylas — succeeded in ~3s, SDK loads and initializes on Node 22.
  2. Missing credentials → exits with clear setup instructions.
  3. Fake credentials → the SDK makes a real HTTP call to Nylas and surfaces the API error correctly.
  4. Raw API check → invalid key returns 401 {"type":"invalid_request_error","message":"Could not verify access credential."}; a nonexistent grant returns 404.

Where I'm blocked

There is a normal developer workflow here (hosted REST API + official SDK + dashboard), and I acted on everything I can. The one thing I can't do is produce valid credentials:

  • NYLAS_API_KEY requires a human to sign up at dashboard-v3.nylas.com.
  • NYLAS_GRANT_ID requires connecting a real mail/calendar account (OAuth consent).

Without both, no live data call can succeed — that's an intentional auth gate, not a dead-end. I stopped at the credential boundary rather than faking a "success." One small note: api.nylas.com returned intermittent 503 from this sandbox, but the SDK's default endpoint api.us.nylas.com responded normally.

Pricing (as of the current pricing page)

Tiers are per-month and all include email + calendar + Notetaker + Agent Accounts; you pay for connected accounts and usage beyond included volumes:

PlanPriceWhat you get
Free$0/month, no card5 email/calendar connected accounts, 3 Agent Accounts, 5 Notetaker bot hours, 3K emails, 3 GB storage/bandwidth
Essentials$15/month10 connected accounts (10 more calendar-only), 10 Agent Accounts, 10 bot hours, 10K emails, 1 custom sending domain
Pro$49/mo (monthly) or $43/mo (annual)25 accounts (35 calendar-only), 50 Agent Accounts, 70 bot hours, 50K emails, 50 GB storage/bandwidth, 10 custom domains (20 on annual)
EnterpriseCustomVolume pricing, HIPAA BAA, SLA, dedicated support

Overage examples on Pro: ~$2.00–2.25 per extra connected account, $1.50–1.70 calendar-only account, $0.70–0.80 per extra Notetaker bot hour, $0.40 per extra Agent Account on monthly. The example I built fits entirely within the Free tier (it uses one connected account).

Is it easy for me to manage?

Yes — it's a well-documented, pre-authenticated hosted API with a maintained SDK and typed errors, so I can build, lint-test the wiring, and diagnose failures (as I did above). The only recurring human step will be dropping in an API key and grant ID. Once you provide those two values, I can run the example and confirm a live result immediately.

AX Fundamentals

23 checks. Open an area for details.

This check is a quick preview. Test more tasks and models with Gauge Agents.

Test your product with Gauge Agents

AX Check by Gauge