This is page 1 of 2. Use http://codebase.md/nylas-samples/nylas-api-mcp?page={x} to view the full context. # Directory Structure ``` ├── .gitignore ├── CODE_OF_CONDUCT.md ├── CONTRIBUTION.md ├── nylas-code-samples │ ├── Auth │ │ ├── AdminConsent-Bulk │ │ │ └── index.md │ │ ├── AppPermission-Bulk │ │ │ └── index.md │ │ ├── auth.md │ │ ├── connected-accounts.md │ │ ├── Connectors │ │ │ ├── ews.md │ │ │ ├── google.md │ │ │ ├── icloud.md │ │ │ ├── imap.md │ │ │ ├── index.md │ │ │ ├── ms.md │ │ │ ├── virtual-cal.md │ │ │ └── yahoo.md │ │ ├── Hosted │ │ │ └── index.md │ │ ├── Native-Custom │ │ │ ├── index.md │ │ │ └── virtual-cals-custom.md │ │ └── Providers │ │ ├── google-imported.md │ │ ├── google-pubsub.md │ │ ├── google.md │ │ ├── ms-imported.md │ │ └── ms.md │ ├── Calendar │ │ ├── Availability │ │ │ └── index.md │ │ ├── calendar-apis.md │ │ ├── Calendars │ │ │ ├── index.md │ │ │ ├── read.md │ │ │ ├── webhooks.md │ │ │ └── write.md │ │ ├── Events │ │ │ ├── index.md │ │ │ ├── read.md │ │ │ ├── webhooks.md │ │ │ └── write.md │ │ ├── google.md │ │ ├── ms.md │ │ ├── Recurring │ │ │ └── index.md │ │ └── Virtual-Calendars │ │ └── index.md │ ├── Coda │ │ ├── index.md │ │ ├── testing.md │ │ └── webhooks.md │ ├── Contacts │ │ ├── index.md │ │ ├── read.md │ │ ├── webhooks.md │ │ └── write.md │ ├── Email │ │ ├── Drafts │ │ │ ├── index.md │ │ │ ├── read.md │ │ │ └── write.md │ │ ├── Files-Attachments │ │ │ ├── index.md │ │ │ └── read.md │ │ ├── Labels-Folders │ │ │ ├── index.md │ │ │ ├── read.md │ │ │ ├── webhooks.md │ │ │ └── write.md │ │ ├── Messages │ │ │ ├── index.md │ │ │ ├── read.md │ │ │ ├── webhooks.md │ │ │ └── write.md │ │ ├── Outbox │ │ │ ├── index.md │ │ │ ├── read.md │ │ │ ├── webhooks.md │ │ │ └── write.md │ │ ├── Search │ │ │ ├── index.md │ │ │ └── read.md │ │ ├── Threads │ │ │ ├── index.md │ │ │ ├── read.md │ │ │ ├── webhooks.md │ │ │ └── write.md │ │ └── Tracking │ │ ├── index.md │ │ └── webhooks.md │ ├── Start │ │ ├── api-import.md │ │ ├── Dashboard-Import │ │ │ └── index.md │ │ ├── index.md │ │ └── manual.md │ └── Webhooks │ ├── index.md │ └── new-pubsub-note.md ├── package-lock.json ├── package.json ├── README.md ├── src │ ├── index.ts │ ├── prompts │ │ └── index.ts │ ├── resources │ │ ├── code-samples.ts │ │ ├── docs.ts │ │ ├── endpoints.ts │ │ └── index.ts │ └── tools │ └── index.ts └── tsconfig.json ``` # Files -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- ``` # Dependencies node_modules/ # Build output dist/ # Environment variables .env .env.local .env.development.local .env.test.local .env.production.local # Logs npm-debug.log* yarn-debug.log* yarn-error.log* # IDE files .idea/ .vscode/ *.sublime-project *.sublime-workspace # OS files .DS_Store Thumbs.db # Project specific mcp-llms-full.txt mcp-typescript-sdk-readme.txt ``` -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- ```markdown # Nylas API MCP Server (Experimental) > **Note**: This project is experimental and is intended as an exploration of using the Model Context Protocol (MCP) as a guide for Nylas API integrations. It is not official and should be used for learning and experimentation purposes only. This project implements a Model Context Protocol (MCP) server for the Nylas API. It provides resources, tools, and prompts to help developers learn about and integrate with the Nylas API for email, calendar, and contacts functionality. ## What is MCP? The [Model Context Protocol (MCP)](https://modelcontextprotocol.io) is a standard for integrating data, tools, and prompts with AI applications. MCP servers can expose resources, tools, and prompts to AI applications like [Claude Desktop](https://claude.ai/download), [Cursor](https://cursor.sh), and other MCP-compatible applications. ## Features This MCP server provides: - **Documentation Resources**: Comprehensive documentation about Nylas API endpoints, authentication, and best practices - **Code Samples**: Ready-to-use code samples for various Nylas API operations in multiple languages (Node.js, Python, Java, Ruby, curl) - **Interactive Tools**: Tools to generate authentication code and endpoint-specific implementation code - **Prompts**: Pre-built prompts for common Nylas integration scenarios  ## Getting Started ### Prerequisites - Node.js 18+ - npm ### Installation 1. Clone this repository 2. Install dependencies ```bash npm install ``` 3. Build the project ```bash npm run build ``` ### Running the MCP Server #### Using stdio (for direct integration with MCP clients) ```bash npm start ``` #### Using HTTP with SSE (for remote connections) ```bash MCP_MODE=http npm start ``` By default, the HTTP server runs on port 3000. You can change this by setting the `PORT` environment variable: ```bash MCP_MODE=http PORT=8080 npm start ``` ## Using with MCP Clients ### Claude Desktop 1. Start the MCP server in stdio mode 2. In Claude Desktop, go to Settings > Servers 3. Click "Add Server" 4. Select "Run a local command" and enter the command to start this server: ```bash node /path/to/nylas-api-mcp/dist/index.js ``` 5. Give it a name (e.g., "Nylas API") 6. After adding, you can use all the Nylas API resources, tools, and prompts in your Claude conversations ### Cursor 1. Start the MCP server in stdio mode 2. In Cursor, add a new MCP server in the settings 3. Configure it to use the command: ```bash node /path/to/nylas-api-mcp/dist/index.js ``` ## Resources This MCP server provides the following resources: - **General Documentation**: Overview of Nylas API capabilities - **Authentication Guide**: How to authenticate with Nylas API - **API Endpoints**: Documentation for email, calendar, contacts, and webhook endpoints - **Code Samples**: Usage examples in multiple programming languages ## Tools Interactive tools for code generation: - **generate-auth-code**: Generates authentication code in your preferred language - **generate-endpoint-code**: Generates code for specific API endpoints - **search-api-docs**: Search through the Nylas API documentation ## Prompts Pre-built prompts for common scenarios: - **Getting Started**: Basic intro to Nylas API - **Authentication Guide**: Understanding OAuth flow - **Email/Calendar/Contacts Integration**: Feature-specific guidance - **Integration Scenarios**: Guidance for specific use cases (email client, calendar booking, etc.) - **SDK Examples**: Example code in various languages - **Debugging Guide**: Common issues and solutions ## Contributing Contributions are welcome! You can: 1. Add more code samples to the `nylas-code-samples` directory 2. Improve documentation resources 3. Add support for additional programming languages 4. Create new tools and prompts ## Disclaimer This is an experimental project and is not officially supported by Nylas. The information and code provided through this MCP server should be used as a learning resource only. Always refer to the [official Nylas documentation](https://developer.nylas.com/) for the most accurate and up-to-date information on the Nylas API. The sample code provided is for educational purposes and may need additional error handling, security considerations, and testing before being used in production environments. ## License MIT ``` -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- ```markdown # Nylas Open Source Code of Conduct ## Our Pledge We as members, contributors, and leaders pledge to make participation in our community a harassment-free experience for everyone, regardless of age, body size, visible or invisible disability, ethnicity, sex characteristics, gender identity and expression, level of experience, education, socio-economic status, nationality, personal appearance, race, religion, or sexual identity and orientation. We pledge to act and interact in ways that contribute to an open, welcoming, diverse, inclusive, and healthy community. ## Our Standards ### Examples of behavior that contributes to a positive environment for our community include: - Demonstrating empathy and kindness toward other people - Being respectful of differing opinions, viewpoints, and experiences - Giving and gracefully accepting constructive feedback - Accepting responsibility and apologizing to those affected by our mistakes, and learning from the experience - Focusing on what is best not just for us as individuals, but for the overall community ## Examples of unacceptable behavior include: - The use of sexualized language or imagery, and sexual attention or advances of any kind - Trolling, insulting or derogatory comments, and personal or political attacks - Public or private harassment - Publishing others’ private information, such as a physical or email address, without their explicit permission - Other conduct which could reasonably be considered inappropriate in a professional setting ## Enforcement Responsibilities Community leaders are responsible for clarifying and enforcing our standards of acceptable behavior and will take appropriate and fair corrective action in response to any behavior that they deem inappropriate, threatening, offensive, or harmful. Community leaders have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, and will communicate reasons for moderation decisions when appropriate. ## Scope This Code of Conduct applies within all community spaces, and also applies when an individual is officially representing the community in public spaces. Examples of representing our community include using an official e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. ## Enforcement Instances of abusive, harassing, or otherwise unacceptable behavior may be reported to the community leaders responsible for enforcement at [email protected]. All complaints will be reviewed and investigated promptly and fairly. All community leaders are obligated to respect the privacy and security of the reporter of any incident. ## Enforcement Guidelines Community leaders will follow these Community Impact Guidelines in determining the consequences for any action they deem in violation of this Code of Conduct: ### Correction **Community Impact:** Use of inappropriate language or other behavior deemed unprofessional or unwelcome in the community. **Consequence:** A private, written warning from community leaders, providing clarity around the nature of the violation and an explanation of why the behavior was inappropriate. A public apology may be requested. ### Warning **Community Impact:** A violation through a single incident or series of actions. **Consequence:** A warning with consequences for continued behavior. No interaction with the people involved, including unsolicited interaction with those enforcing the Code of Conduct, for a specified period of time. This includes avoiding interactions in community spaces as well as external channels like social media. Violating these terms may lead to a temporary or permanent ban. ### Temporary Ban **Community Impact:** A serious violation of community standards, including sustained inappropriate behavior. **Consequence:** A temporary ban from any sort of interaction or public communication with the community for a specified period of time. No public or private interaction with the people involved, including unsolicited interaction with those enforcing the Code of Conduct, is allowed during this period. Violating these terms may lead to a permanent ban. ### Permanent Ban **Community Impact:** Demonstrating a pattern of violation of community standards, including sustained inappropriate behavior, harassment of an individual, or aggression toward or disparagement of classes of individuals. **Consequence:** A permanent ban from any sort of public interaction within the community. ## Attribution This Code of Conduct is adapted from the Contributor Covenant, version 2.0, available at https://www.contributor-covenant.org/version/2/0/code_of_conduct.html. Community Impact Guidelines were inspired by Mozilla’s code of conduct enforcement ladder. For answers to common questions about this code of conduct, see the FAQ at https://www.contributor-covenant.org/faq. Translations are available at https://www.contributor-covenant.org/translations. ``` -------------------------------------------------------------------------------- /nylas-code-samples/Calendar/google.md: -------------------------------------------------------------------------------- ```markdown ### Changes to Google calendars - Added the `hex_color` property for all Calendar calls. ``` -------------------------------------------------------------------------------- /nylas-code-samples/Email/Labels-Folders/webhooks.md: -------------------------------------------------------------------------------- ```markdown ## Changes to Folders webhooks Nylas v3 supports the following folders webhook triggers: - `folder.created` - `folder.updated` - `folder.deleted` ``` -------------------------------------------------------------------------------- /nylas-code-samples/Email/Tracking/webhooks.md: -------------------------------------------------------------------------------- ```markdown ## Changes to message tracking webhooks Nylas v3 supports the following webhook triggers from v2: - `message.opened` - `message.link_clicked` - `thread.replied` ``` -------------------------------------------------------------------------------- /nylas-code-samples/Calendar/ms.md: -------------------------------------------------------------------------------- ```markdown ### Changes to Microsoft Graph calendars - Added the `hex_color` property for all Calendar calls. - Added access to shared calendars using the [Return Calendars endpoint](https://developer.nylas.com/docs/api/v3/ecc/#get-/v3/grants/-grant_id-/calendars). ``` -------------------------------------------------------------------------------- /nylas-code-samples/Email/Outbox/webhooks.md: -------------------------------------------------------------------------------- ```markdown ### Scheduled Send webhook notifications Nylas v3 introduces the following Scheduled Send webhook triggers: - `message.send_success`: A scheduled email message was sent successfully. - `message.send_failed`: A scheduled email message failed to send. - `message.bounce_detected`: (Gmail and Microsoft Graph only) A scheduled email message bounced. ``` -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- ```json { "compilerOptions": { "target": "ES2020", "module": "NodeNext", "moduleResolution": "NodeNext", "esModuleInterop": true, "outDir": "./dist", "strict": true, "skipLibCheck": true, "forceConsistentCasingInFileNames": true, "resolveJsonModule": true }, "include": ["src"], "exclude": ["node_modules", "dist"] } ``` -------------------------------------------------------------------------------- /nylas-code-samples/Auth/Connectors/icloud.md: -------------------------------------------------------------------------------- ```markdown To create an iCloud connector, make a [`POST /v3/connectors` request](https://developer.nylas.com/docs/api/v3/admin/#post-/v3/connectors) and set the `provider` to `icloud`. When you start an authentication request for an iCloud account, use the `Bearer auth` request header with your Nylas application's API key, set `provider_type: "icloud"`, and include any states. ``` -------------------------------------------------------------------------------- /nylas-code-samples/Email/Threads/webhooks.md: -------------------------------------------------------------------------------- ```markdown ## Threads webhook notifications Nylas v3 doesn't include Threads webhook triggers. Instead, you receive Messages notifications for updates to threads. You can subscribe to the `thread.replied` trigger if you [enable message tracking](https://developer.nylas.com/docs/v3/email/message-tracking/). This allows you to get notifications when a participant replies to an email message in a thread. ``` -------------------------------------------------------------------------------- /nylas-code-samples/Contacts/webhooks.md: -------------------------------------------------------------------------------- ```markdown ### Update contacts webhooks Nylas v3 supports the following contacts webhook triggers: - `contact.updated` - `contact.deleted` The `contact.created` webhook trigger is deprecated in v3 to maintain provider consistency. Instead, Nylas sends a `contact.updated` notification when an end user creates a contact. Be sure to update your subscriptions. Nylas does not use the `.truncated` notification suffix for contacts. ``` -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- ```json { "name": "nylas-api-mcp", "version": "1.0.0", "description": "MCP server for Nylas API Developer Docs", "main": "dist/index.js", "type": "module", "scripts": { "build": "tsc", "start": "node dist/index.js", "dev": "tsc && node dist/index.js" }, "dependencies": { "@modelcontextprotocol/sdk": "^1.2.1", "express": "^4.18.3", "zod": "^3.22.4" }, "devDependencies": { "@types/express": "^4.17.21", "@types/node": "^20.11.24", "typescript": "^5.3.3" } } ``` -------------------------------------------------------------------------------- /nylas-code-samples/Coda/testing.md: -------------------------------------------------------------------------------- ```markdown ## Test your auth settings When you have your provider auth apps and connectors working, you can test them out by creating a grant for each type of auth flow. You can then use these grants to test your upgraded Nylas Email, Calendar, and Contacts API integrations. As you test, you might find that you need to modify your v3 scopes. If you do, remember to change them on both your provider auth apps and in any defaults you set on the associated connectors (and connector credentials, if you're using them). ``` -------------------------------------------------------------------------------- /nylas-code-samples/Auth/Connectors/virtual-cal.md: -------------------------------------------------------------------------------- ```markdown You need to create a Virtual Calendar connector before you can create or work with Nylas virtual calendars. Each Nylas application that uses Virtual Calendars needs its own Virtual Calendar connector. To create a Virtual Calendar connector, make a [`POST /v3/connectors` request](https://developer.nylas.com/docs/api/v3/admin/#post-/v3/connectors) and set `provider` to `virtual-calendar`. You can also do this from the v3 Dashboard by clicking **Connectors** and then the **+** button next to Virtual Calendars. ``` -------------------------------------------------------------------------------- /nylas-code-samples/Auth/Connectors/google.md: -------------------------------------------------------------------------------- ```markdown To create a Google connector, make a [`POST /v3/connectors` request](https://developer.nylas.com/docs/api/v3/admin/#post-/v3/connectors) that includes your Google Cloud Platform (GCP) `client_id` and `client_secret`, and a default set of scopes for your project. You can also [configure the default scopes](https://developer.nylas.com/docs/v3/auth/#create-a-connector) for end users who authenticate through the Google connector. Make sure the scopes on the connector exactly match or are a literal subset of the scopes on your GCP app. ``` -------------------------------------------------------------------------------- /nylas-code-samples/Auth/Providers/google-pubsub.md: -------------------------------------------------------------------------------- ```markdown ### Set up Google Pub/Sub If your project monitors for email message-related webhook notifications from Google services, you must [set up a Pub/Sub topic in your GCP app](https://developer.nylas.com/docs/dev-guide/provider-guides/google/connect-google-pub-sub/) so Google can notify you about changes to email messages in real-time. This is _required_. For complete and detailed information, see [Create a Google authentication application for Nylas v3](https://developer.nylas.com/docs/dev-guide/provider-guides/google/create-google-app/). ``` -------------------------------------------------------------------------------- /nylas-code-samples/Auth/Connectors/yahoo.md: -------------------------------------------------------------------------------- ```markdown ### (Maybe don't) create a Yahoo connector (right now) A new Yahoo OAuth method is available that _greatly_ improves Yahoo performance and reliability. However, this new method requires that you create a new Yahoo OAuth application - and this might take a week or more. For now, we recommend you use the IMAP connector to authenticate users, but put this on your To Do list post-upgrade. When you've got your Yahoo OAuth app approved, you can make a [`POST /v3/connectors` request](https://developer.nylas.com/docs/api/v3/admin/#post-/v3/connectors) and set the `provider` to `yahoo`. ``` -------------------------------------------------------------------------------- /nylas-code-samples/Email/Tracking/index.md: -------------------------------------------------------------------------------- ```markdown ## Changes to message tracking In v3, the following message tracking response fields have been updated: | **v2** | **v3** | **Location** | | ------------------ | ------------------- | ---------------------------------- | | `tracking` | `tracking_options` | Request | | `tracking.payload` | `tracking.label` | Request and notification | | `recents.id` | `recents.click_id` | Link Clicked tracking notification | | `recents.id` | `recents.opened_id` | Message Open tracking notification | ``` -------------------------------------------------------------------------------- /nylas-code-samples/Auth/Native-Custom/virtual-cals-custom.md: -------------------------------------------------------------------------------- ```markdown For _most_ projects migrating from v2.x to v3, the first step is to upgrade authentication. However, since Virtual Calendars use [Custom Authentication](/docs/api/v3/admin/#post-/v3/connect/custom) (previously called "Native Authentication"), you get to skip worrying about scopes and provider auth apps, and just update how you authorize your API calls to the v3 method, and update the Custom auth API calls themselves. In fact, you don't actually need to upgrade to use Custom Authentication until you want to make new virtual accounts. We'll show the instructions for you so you have them for when you get to that point. ``` -------------------------------------------------------------------------------- /nylas-code-samples/Calendar/Calendars/webhooks.md: -------------------------------------------------------------------------------- ```markdown ## Calendar webhook triggers The webhook triggers for calendar notifications have not changed, however Nylas has changed the webhook notification format and some behavior. Nylas now sends webhook notifications enriched with information about the object that changed, and does not send historical webhooks. See the [Webhooks changes in v3](https://developer.nylas.com/docs/new/in-v3/webhooks-changes/) for more information about the changes, and the [Calendar webhook schemas](https://developer.nylas.com/docs/v3/notifications/notification-schemas/#calendar-notifications) for more information. Nylas v3 supports the following calendar webhook triggers: - `calendar.created` - `calendar.updated` - `calendar.deleted` ``` -------------------------------------------------------------------------------- /nylas-code-samples/Auth/Connectors/ms.md: -------------------------------------------------------------------------------- ```markdown To create a Microsoft connector, make a [`POST /v3/connectors` request](https://developer.nylas.com/docs/api/v3/admin/#post-/v3/connectors) that includes your Azure `client_id` and `client_secret`, and a default set of scopes for your project. When you start an authentication request with Microsoft, you use the `Bearer auth` request header with your Nylas application's API key, set `provider_type: "microsoft"`, and include any non-default scopes and states for the end user. :::warn ⚠️ **Before you create a Microsoft connector, make sure you've migrated your project's old scopes to the new Microsoft Graph scopes and entered them in your Azure application's Entra ID**. Because the upgrade to Nylas v3 includes this scope update, all Microsoft users must re-authenticate to accept the new v3 scopes. ::: ``` -------------------------------------------------------------------------------- /nylas-code-samples/Calendar/Recurring/index.md: -------------------------------------------------------------------------------- ```markdown ## Changes to recurring events Recurring events now use an array of `RRULE` and `EXDATE` strings. Recurring Events no longer accept a recurrence timezone, and instead use the timezone specified in your `when` object to calculate the time for event instances. The `when` object is a `time:` in epoch time, and a `timezone:` in [IANA format](https://en.wikipedia.org/wiki/List_of_tz_database_time_zones). See the [Recurring events](https://developer.nylas.com//docs/v3/calendar/schedule-recurring-events/) for more details. ### Architecture changes Nylas v3 no longer syncs data from providers, but instead queries them directly. This means that Nylas now relies on the provider as the source of truth, however some providers may require tuning to best handle their differing behavior. This is especially true for recurring events. ``` -------------------------------------------------------------------------------- /nylas-code-samples/Auth/Connectors/ews.md: -------------------------------------------------------------------------------- ```markdown The Nylas Microsoft connector (`provider: microsoft`) allows end users to authenticate with several different Microsoft services so they can connect to your project. However, Microsoft no longer supports Exchange servers, and end users cannot auth with them using Nylas v3. To authenticate end users who use Exchange on-premises services for email _and_ calendar, you must [create a separate EWS connector](https://developer.nylas.com/docs/dev-guide/provider-guides/exchange-web-services/). If you use Exchange on-premises services for email _only_ and your project doesn't need to use the Nylas Calendar APIs, you can authenticate using an IMAP connector instead. Nylas no longer supports Exchange on-premises _service_ accounts. Personal accounts on Hotmail, Live, and Outlook must start a new authentication using the Microsoft connector instead of Exchange. ``` -------------------------------------------------------------------------------- /nylas-code-samples/Webhooks/new-pubsub-note.md: -------------------------------------------------------------------------------- ```markdown If you get lots of Message or Event webhook notifications, you might want to [consider setting up PubSub channel](https://developer.nylas.com/docs/v3/notifications/pubsub-channel/) for these notifications _instead_ of using webhooks. PubSub channels are new in v3, and are ideal for projects where webhook volume, latency, or deliverability are concerns, or where your project requires deliverability guarantees. A PubSub channel is separate from the PubSub topic you set up to get notifications about Google messages. The PubSub _topic_ improves Google's latency sending notifications _to_ your Nylas app, while the PubSub _channel_ allows you to _consume_ notifications. :::info 🔍 **Good to know**: Although you set up the PubSub notification channel on Google Cloud, it can receive notifications about events on any of the providers Nylas supports - not just Google. ::: ``` -------------------------------------------------------------------------------- /nylas-code-samples/Auth/Connectors/imap.md: -------------------------------------------------------------------------------- ```markdown To create an IMAP connector, make a [`POST /v3/connectors` request](https://developer.nylas.com/docs/api/v3/admin/#post-/v3/connectors) and set the `provider` to `imap`. The IMAP connector doesn't take a client ID or secret, or use any scopes. Instead, you pass individual server connection information with each authentication request. You can use the [Detect provider endpoint](https://developer.nylas.com/docs/api/v3/admin/#post-/v3/providers/detect) to help you pre-fill that information for [supported common IMAP providers](https://developer.nylas.com/docs/dev-guide/provider-guides/supported-providers/#supported-imap-providers). When you start an auth request for an IMAP account, use the `Bearer auth` request header with your Nylas application's API key, along with the end user's IMAP username, password, and IMAP host and port. In Nylas v3, IMAP providers can only sync data within a rolling 90 day window. ``` -------------------------------------------------------------------------------- /nylas-code-samples/Calendar/Events/webhooks.md: -------------------------------------------------------------------------------- ```markdown ## Event webhook triggers The webhook triggers for calendar and events notifications have not changed, however Nylas has changed the webhook notification format and some behavior. Nylas now sends webhook notifications enriched with information about the object that changed, and does not send historical webhooks. See the [Webhooks changes in v3](https://developer.nylas.com/docs/new/in-v3/webhooks-changes/) for more information about the changes, and the [Event webhook schemas](https://developer.nylas.com/docs/v3/notifications/notification-schemas/#event-notifications) for more information. Nylas v3 supports the following event webhook triggers: - `event.created` - `event.updated` - `event.deleted` ## Truncated event webhooks The `.truncated` suffix is new in v3. Watch for it, because it indicates a data payload that exceeds a base limit of 1MB. You will want to re-query for additional data in that case only. ``` -------------------------------------------------------------------------------- /nylas-code-samples/Email/Files-Attachments/index.md: -------------------------------------------------------------------------------- ```markdown **In v3, "Files" are now called "Attachments"**. Be sure to find all instances of the v2 `/files` endpoints in your code and change them to the appropriate v3 endpoints. In Nylas v3, you use the Messages or Drafts APIs to add or remove attachments from a message payload, and use the Attachments APIs to download or get information about the attachments. [Learn more about working with attachments in v3](/docs/v3/email/attachments/). ### Migrated Attachments endpoints - Return attachment metadata: `GET /files/<FILE_ID>` → [`GET /v3/grants/<NYLAS_GRANT_ID>/attachments/<ATTACHMENT_ID>`](https://developer.nylas.com/docs/api/v3/ecc/#get-/v3/grants/-grant_id-/attachments/-attachment_id-) - Download an attachment: `GET /files/<FILE_ID>/download` → [`GET /v3/grants/<NYLAS_GRANT_ID>/attachments/<ATTACHMENT_ID>/download`](https://developer.nylas.com/docs/api/v3/ecc/#get-/v3/grants/-grant_id-/attachments/-attachment_id-/download) ``` -------------------------------------------------------------------------------- /nylas-code-samples/Auth/auth.md: -------------------------------------------------------------------------------- ```markdown The first step for every project migrating from Nylas v2.x to v3 is to assess and upgrade your authentication systems. When you have your auth systems working in v3, you can then test and upgrade your other code and systems. ### Check for compatibility Before you upgrade your authentication systems, check if you're using any of the methods that are no longer supported in Nylas v3: - **No support for unsecured HTTP**: Nylas no longer supports unsecured connections over HTTP (port 80). You must now use HTTPS (port 443). - **No support for Basic auth**: Nylas no longer supports Basic authentication, and instead uses Bearer auth with either an access token or API key. If you haven't already updated your code to use Bearer auth, you must do that before you can use v3. - **No support for server-side hosted auth**: Nylas v3 no longer supports server-side hosted authentication. You must use the new Hosted OAuth method. If you're using any of these methods, you must update them or migrate to a more modern version before you proceed. ``` -------------------------------------------------------------------------------- /nylas-code-samples/Email/Search/index.md: -------------------------------------------------------------------------------- ```markdown ## Changes to Search - The `/messages/search` and `/threads/search` endpoints are deprecated. Instead, you can now include a URL-encoded provider query string in a [`GET /v3/grants/<NYLAS_GRANT_ID>/messages` request](https://developer.nylas.com/docs/api/v3/ecc/#get-/v3/grants/-grant_id-/messages) or a [`GET /v3/grants/<NYLAS_GRANT_ID>/threads` request](https://developer.nylas.com/docs/api/v3/ecc/#get-/v3/grants/-grant_id-/threads). - You can search for email messages and threads on IMAP providers using all [standard SEARCH operators](https://datatracker.ietf.org/doc/html/rfc3501#section-6.4.4). - You can include the `search_query_native` query parameter to add provider-specific search query strings to your criteria. - Metadata is not supported. ### Migrated Search endpoints - Search messages: `/messages/search` → [`GET /v3/grants/<NYLAS_GRANT_ID>/messages` request](https://developer.nylas.com/docs/api/v3/ecc/#get-/v3/grants/-grant_id-/messages) - Search threads: `/threads/search` → [`GET /v3/grants/<NYLAS_GRANT_ID>/threads` request](https://developer.nylas.com/docs/api/v3/ecc/#get-/v3/grants/-grant_id-/threads) ``` -------------------------------------------------------------------------------- /nylas-code-samples/Calendar/Availability/index.md: -------------------------------------------------------------------------------- ```markdown ### Changes to Calendar availability - Round-robin now uses the value of the Nylas `key5` metadata to indicate Events to consider when calculating the next available time among group members. Set `key5` to a specific string to mark events to consider when calculating a round robin order. - Some fields have been renamed for clarity. See [Check calendar availability](https://developer.nylas.com/docs/v3/calendar/calendar-availability/), and the [v3 Availability reference](https://developer.nylas.com/docs/api/v3/ecc/#post-/v3/calendars/availability) for more details. - [`POST /v3/calendars/availability` requests](https://developer.nylas.com/docs/api/v3/ecc/#post-/v3/calendars/availability) do not require a grant ID or calendar ID because you can specify multiple participants to check for availability. - You can no longer use a `free_busy` object to override or add busy data to an Availability request. - `time_slots` and `tentative_busy` parameters are removed for all Availability calls. #### Deprecated Availability endpoints - Get availability for multiple consecutive meetings: `POST /calendars/availability/consecutive` ``` -------------------------------------------------------------------------------- /nylas-code-samples/Start/manual.md: -------------------------------------------------------------------------------- ```markdown Nylas applications are the containers for your project's authentication configuration and general settings, and your end users' grants. Depending on how your project is organized, you might use separate Nylas applications to provide your end users with different experiences. Nylas doesn't provide an API endpoint to create applications; you must create them using the v3 Nylas Dashboard. After they're created you can modify and interact with them using the [Administration APIs](https://developer.nylas.com/docs/api/v3/admin/#tag--Applications). :::warn ⚠️ **Keep in mind**: You can create and manage legacy v2 Nylas applications from the v3 Dashboard, but you can't create or manage v3 applications from the v2 Dashboard. ::: 1. Log in to the v3 Dashboard. 2. Click **Create new app**. 3. Add a name for the application, and choose the data residency location. 4. Optionally, add a description and an environment label. 5. Click **Create app**. Repeat this for each app you want to re-create. At minimum, you should create a Nylas application for testing, and a separate production application. This guide covers the rest of the upgrade process in the sections below. ``` -------------------------------------------------------------------------------- /nylas-code-samples/Email/Outbox/index.md: -------------------------------------------------------------------------------- ```markdown ## Changes to Outbox The v3 Email API consolidates several v2 endpoints, and specifically replaces the v2.x `/outbox` endpoints with the `/v3/messages` endpoints. To switch from using v2 Instant Send to v3 [Scheduled Send](https://developer.nylas.com/docs/v3/email/scheduled-send/), make a [`POST /v3/grants/<NYLAS_GRANT_ID>/messages/send` request](https://developer.nylas.com/docs/api/v3/ecc/#post-/v3/grants/-grant_id-/messages/send) that includes the time when you want Nylas to send the email message. ### New Scheduled Send endpoints - Return a specific scheduled email message: [`GET /v3/grants/<NYLAS_GRANT_ID>/messages/schedules/<SCHEDULE_ID>`](https://developer.nylas.com/docs/api/v3/ecc/#get-/v3/grants/-grant_id-/messages/schedules/-scheduleId-) ### Migrated Outbox endpoints - Return all scheduled email messages: `GET /outbox` → [`GET /v3/grants/<NYLAS_GRANTID>/messages/schedules`](https://developer.nylas.com/docs/api/v3/ecc/#get-/v3/grants/-grant_id-/messages/schedules) - Cancel scheduled send instructions: `DELETE /outbox/<SCHEDULE_ID>` → [`DELETE /v3/grants/<NYLAS_GRANT_ID>/messages/schedules/<SCHEDULE_ID>`](https://developer.nylas.com/docs/api/v3/ecc/#delete-/v3/grants/-grant_id-/messages/schedules/-scheduleId-) ``` -------------------------------------------------------------------------------- /nylas-code-samples/Email/Messages/webhooks.md: -------------------------------------------------------------------------------- ```markdown ### Changes to Messages webhooks Nylas v3 supports the following webhook triggers from v2: - `message.created` - `message.updated` #### Truncated webhook notifications If the object returned in a `message.created` or `message.updated` notification is larger than 1MB, Nylas v3 omits the large fields and adds the `.truncated` suffix to the notification (for example, `message.created.truncated`). When you get a `.truncated` notification, you might want to re-query if the omitted data is important to your project's workflow. Nylas might send you `.truncated` notifications if you subscribe to `message.created` or `message.updated` triggers, so make sure your webhook processor can handle them. You can't subscribe to `.truncated` triggers separately, and they aren't a subscription option in the v3 Dashboard webhooks page. ### Provider limitations iCloud, Yahoo, and IMAP providers can retrieve data for email messages that's up to 90 days old. For most providers, email message notifications are sent in real-time, but Yahoo and AOL accounts might take up to 5 minutes to deliver those notifications. Nylas does not send webhook notifications for changes to email messages (for example, folder changes) if the email message being updated is older than 90 days. ``` -------------------------------------------------------------------------------- /nylas-code-samples/Calendar/calendar-apis.md: -------------------------------------------------------------------------------- ```markdown The Calendar and Events endpoints are available for Google, Microsoft Graph, iCloud, Exchange on-prem, and Virtual Calendars. Nylas v3 includes the same basic ability to create, read, update, and delete both Events and Calendars (which contain Events). It also includes the following changes: - You now need a `calendar_id` for almost all calls to Events endpoints. You can specify it in one of the following ways: - Using the `primary` parameter to specify the primary calendar for the grant. - Look up and use the ID of the calendar that you want to work with. - You can now specify open hours by adding the `default_open_hours` parameter to the `availability_rules` object. - You can override the default settings by specifying a different availability for each participant. - Round-robin availability now uses the Nylas `key5` metadata to indicate events that Nylas should consider when calculating the next available time amongst a group of participants. - The format for event start and end times has been simplified. - The format for recurring events has been simplified. - Recurring events no longer accept a recurrence time zone, and instead use the time zone specified in the `when` object to calculate the time for each event instance. - You can now list all calendars and events for a specific grant. ``` -------------------------------------------------------------------------------- /nylas-code-samples/Auth/Connectors/index.md: -------------------------------------------------------------------------------- ```markdown **In v3, "integrations" are now called "connectors**. These objects store information about auth providers for each Nylas application. All authentication types, including Custom auth (previously called "Native auth"), require connectors for each provider. Each Nylas application can have only one connector per provider. If you're in a multi-tenant environment, you can use connector credentials with Google and Microsoft connectors to override connection information. ### New connector endpoints - Return all connectors: [`GET /v3/connectors`](https://developer.nylas.com/docs/api/v3/admin/#get-/v3/connectors) - Return a specific connector: [`GET /v3/connectors/<PROVIDER>`](https://developer.nylas.com/docs/api/v3/admin/#get-/v3/connectors/-provider-) - Create a connector: [`POST /v3/connectors`](https://developer.nylas.com/docs/api/v3/admin/#post-/v3/connectors) - Update a connector: [`PATCH /v3/connectors/<provider>`](https://developer.nylas.com/docs/api/v3/admin/#patch-/v3/connectors/-provider-) - Delete a connector: [`DELETE /v3/connectors/<provider>`](https://developer.nylas.com/docs/api/v3/admin/#delete-/v3/connectors/-provider-) ### Migrated integrations endpoints - Detect provider: `POST /connect/detect-provider` → [`POST /v3/providers/detect`](https://developer.nylas.com/docs/api/v3/admin/#post-/v3/providers/detect) ``` -------------------------------------------------------------------------------- /nylas-code-samples/Auth/Providers/google-imported.md: -------------------------------------------------------------------------------- ```markdown If you used the Dashboard Migration button, or the [Import application settings endpoint](https://developer.nylas.com/docs/api/v3/migration/#post-/v3/migration-tools/import-v2-app), Nylas created a connector for Google, but left the Scopes blank for you to fill in. Use the Google scopes you calculated in the previous steps. To complete the import process, you can make an [Update connector request](https://developer.nylas.com/docs/api/v3/admin/#patch-/v3/connectors/-provider-) to update the scopes. ```API curl --location --request PATCH 'http://api.us.nylas.com/v3/connectors/google' \ --header 'Authorization: Bearer <NYLAS_API_KEY>' \ --header 'Content-Type: application/json' \ --data '{ "scope": [ "https://www.googleapis.com/auth/userinfo.email", "https://www.googleapis.com/auth/userinfo.profile" ] }' ``` If you prefer, you can also update the Microsoft connector from the Dashboard: 1. Navigate to the v3 Dashboard, click **Connectors**, click **...** (the "more" menu) next to the Microsoft connector and select **Edit**. 2. On the page that appears, double-check that the Client ID and Client secret for the Google connector are correct, and update them if needed. 3. Next, scroll down to **Authentication scopes** section, and select the scopes your app needs. 4. Click **Save**. ``` -------------------------------------------------------------------------------- /nylas-code-samples/Email/Labels-Folders/index.md: -------------------------------------------------------------------------------- ```markdown **In v3, Folders and Labels are consolidated as Folders**. Be sure to find all instances of the v2 `/labels` endpoints and `labels` objects in your code and update them to `folders`. Nylas v3 also includes the following changes to folders and labels: - Folders and Labels have been consolidated. - `folder` → `folders` for all Messages and Threads requests. - `labels` → `folders` for all Messages and Threads requests. - The `folder` object in all Messages and Threads now contains a list of `folder_id`s instead of an array of Folders. ### Migrated Folders endpoints - Return all folders/labels: `GET /folders`, `GET /labels` → [`GET /v3/grants/<NYLAS_GRANT_ID>/folders`](https://developer.nylas.com/docs/api/v3/ecc/#get-/v3/grants/-grant_id-/folders) - Create a folder/label: `POST /folders`, `POST /labels` → [`POST /v3/grants/<NYLAS_GRANT_ID>/folders`](https://developer.nylas.com/docs/api/v3/ecc/#post-/v3/grants/-grant_id-/folders) - Return a folder/label: `GET /folders/<FOLDER_ID>`, `GET /labels/<LABEL_ID>` → [`GET /v3/grants/<NYLAS_GRANT_ID>/folders/<FOLDER_ID>`](https://developer.nylas.com/docs/api/v3/ecc/#get-/v3/grants/-grant_id-/folders/-folder_id-) - Update a folder/label: `PUT /folders/<FOLDER_ID>`, `PUT /labels/<LABEL_ID>` → [`PUT /v3/grants/<NYLAS_GRANT_ID>/folders/<FOLDER_ID>`](https://developer.nylas.com/docs/api/v3/ecc/#put-/v3/grants/-grant_id-/folders/-folder_id-) - Delete a folder/label: `DELETE /folders/<FOLDER_ID>`, `DELETE /labels/<LABEL_ID>` → [`DELETE /v3/grants/<NYLAS_GRANT_ID>/folders/<FOLDER_ID>`](https://developer.nylas.com/docs/api/v3/ecc/#delete-/v3/grants/-grant_id-/folders/-folder_id-) ``` -------------------------------------------------------------------------------- /nylas-code-samples/Auth/Providers/google.md: -------------------------------------------------------------------------------- ```markdown Nylas v3 includes the following changes to how Google Cloud Platform (GCP) auth apps are handled: - The **Authorized redirect URIs** for Hosted Auth have been updated in v3. - **U.S.**: `https://api.us.nylas.com/v3/connect/callback` - **E.U.**: `https://api.eu.nylas.com/v3/connect/callback` - Nylas v3 no longer uses "Nylas scopes", and uses Google's scopes instead. ### Update authorized redirect URIs in GCP app For all GCP auth apps that you want to connect to Nylas v3, the authorized redirect URIs for Hosted Auth have been updated. Add one, or both, of the following to the **Authorized redirect URIs** section of your GCP app: - **U.S.**: `https://api.us.nylas.com/v3/connect/callback` - **E.U.**: `https://api.eu.nylas.com/v3/connect/callback` ### Calculate v3 scopes for GCP app In v3, Nylas no longer uses "Nylas scopes", but directly uses Google's scopes instead. Make a list of the v2 API endpoints your project uses, then read the [v3 scopes documentation](https://developer.nylas.com/docs/v3/auth/v3-scopes/) to determine the scopes you need for v3. When you know what v3 scopes you need, add them to your GCP app. ### Do I need to re-verify my GCP app? _In general_, you shouldn't need to re-verify your GCP app unless you're adding new features to your project. However, two items might require changes if your app doesn't already include their scopes: - **If you use `thread.replied` webhook notifications**, these now use the `email.read` scope in v3, instead of the v2 `email.metadata` scopes. - **If you use the Contacts APIs and want to use domain-level or inbox contacts**, you might need to add some scopes. If you use address book contacts only, you don't need to make any changes. ``` -------------------------------------------------------------------------------- /nylas-code-samples/Auth/Providers/ms-imported.md: -------------------------------------------------------------------------------- ```markdown If you used the Dashboard Migration button, or the [Import application settings endpoint](https://developer.nylas.com/docs/api/v3/migration/#post-/v3/migration-tools/import-v2-app), Nylas created a Microsoft connector in your v3 application, but didn't configure it because of the required Microsoft Graph changes. You can't connect Microsoft users in v3 until you update your application. To complete the import process, you can make an [Update connector request](https://developer.nylas.com/docs/api/v3/admin/#patch-/v3/connectors/-provider-) to update the `client_id` and `client_secret` to those used by the Azure auth app you configured with Graph scopes. ```API curl --location --request PATCH 'http://api.us.nylas.com/v3/connectors/microsoft' \ --header 'Authorization: Bearer <NYLAS_API_KEY>' \ --header 'Content-Type: application/json' \ --data '{ "settings": { "client_id": "<AZURE_CLIENT_ID>", "client_secret": "<AZURE_CLIENT_SECRET", "tenant": "<AZURE_TENANT>" // Optional. Can be tenant ID or common. }, "scope": [ "Mail.Read", "User.Read", "offline_access" ] }' ``` You can also [configure default scopes for users connecting through Microsoft](https://developer.nylas.com/docs/v3/auth/#create-a-connector). If you prefer, you can also update the Microsoft connector from the Dashboard: 1. Navigate to the v3 Dashboard, click **Connectors**, click **...** (the "more" menu) next to the Microsoft connector and select **Edit**. 2. On the page that appears, update the Client ID and Client secret for the Microsoft connector. 3. Next, scroll down to **Authentication scopes** section, and select the scopes your app needs. 4. Click **Save**. ``` -------------------------------------------------------------------------------- /nylas-code-samples/Calendar/Calendars/index.md: -------------------------------------------------------------------------------- ```markdown ## Changes to Calendars Nylas v3 includes the following changes to the Calendars API: - All Calendar call responses now include the `is_owned_by_user` property. ### Migrated Calendars endpoints - Return all calendars: `GET /calendars` → [`GET /v3/grants/<NYLAS_GRANT_ID>/calendars`](https://developer.nylas.com//docs/api/v3/ecc/#get-/v3/grants/-grant_id-/calendars) - Create a calendar: `POST /calendars` → [`POST /v3/grants/<NYLAS_GRANT_ID>/calendars`](https://developer.nylas.com//docs/api/v3/ecc/#post-/v3/grants/-grant_id-/calendars) - Return a calendar: `GET /calendars/<CALENDAR_ID>` → [`GET /v3/grants/<NYLAS_GRANT_ID>/calendars/<CALENDAR_ID>`](https://developer.nylas.com//docs/api/v3/ecc/#get-/v3/grants/-grant_id-/calendars/-calendar_id-) - Update a calendar: `PUT /calendars/<CALENDAR_ID>` → [`PUT /v3/grants/<NYLAS_GRANT_ID>/calendars/<CALENDAR_ID>`](https://developer.nylas.com//docs/api/v3/ecc/#put-/v3/grants/-grant_id-/calendars/-calendar_id-) - Delete a calendar: `DELETE /calendars/<CALENDAR_ID>` → [`DELETE /v3/grants/<NYLAS_GRANT_ID>/calendars/<CALENDAR_ID>`](https://developer.nylas.com//docs/api/v3/ecc/#delete-/v3/grants/-grant_id-/calendars/-calendar_id-) - Check a calendar for availability: `POST /calendars/availability` → [`POST /v3/calendars/availability`](https://developer.nylas.com/docs/api/v3/ecc/#post-/v3/calendars/availability) - Check a calendar for free/busy status: `POST /calendars/free-busy` → [`POST /v3/grants/<NYLAS_GRANT_ID>/calendars/free-busy`](https://developer.nylas.com/docs/api/v3/ecc/#post-/v3/grants/-grant_id-/calendars/free-busy) ### Deprecated Calendars endpoints - Get availability for multiple consecutive meetings: `POST /calendars/availability/consecutive` ``` -------------------------------------------------------------------------------- /nylas-code-samples/Email/Drafts/index.md: -------------------------------------------------------------------------------- ```markdown ## Changes to Drafts Nylas v3 includes the following changes to the Drafts API: - All Drafts endpoints now require a `grant_id`, an email address, or — if you're using access token authentication — the [`/me/` construction](https://developer.nylas.com/docs/api/v3/ecc/#overview--me-syntax-for-api-calls). - You can now send drafts. - The `use_draft` parameter is now available for `POST` requests. - Updated draft-related response fields in the Threads API: - Added `latest_draft_or_message` field. - Added `has_drafts` field. - Updated `last_message_timestap` field to be a `date` sub-field in the `latest_draft_or_message` object. ### New Drafts endpoints - Send a draft: [`POST /v3/grants/<NYLAS_GRANT_ID>/drafts/<DRAFT_ID>`](https://developer.nylas.com/docs/api/v3/ecc/#post-/v3/grants/-grant_id-/drafts/-draft_id-) ### Migrated Drafts endpoints - Return all drafts: `GET /drafts` → [`GET /v3/grants/<NYLAS_GRANT_ID>/drafts`](https://developer.nylas.com/docs/api/v3/ecc/#get-/v3/grants/-grant_id-/drafts) - Create a draft: `POST /drafts` → [`POST /v3/grants/<NYLAS_GRANT_ID>/drafts`](https://developer.nylas.com/docs/api/v3/ecc/#post-/v3/grants/-grant_id-/drafts) - Return a draft: `GET /drafts/<DRAFT_ID>` → [`GET /v3/grants/<NYLAS_GRANT_ID>/drafts/<DRAFT_ID>`](https://developer.nylas.com/docs/api/v3/ecc/#get-/v3/grants/-grant_id-/drafts/-draft_id-) - Update a draft: `PUT /drafts/<DRAFT_ID>` → [`PUT /v3/grants/<NYLAS_GRANT_ID>/drafts/<DRAFT_ID>`](https://developer.nylas.com/docs/api/v3/ecc/#put-/v3/grants/-grant_id-/drafts/-draft_id-) - Delete a draft: `DELETE /drafts/<DRAFT_ID>` → [`DELETE /v3/grants/<NYLAS_GRANT_ID>/drafts/<DRAFT_ID>`](https://developer.nylas.com/docs/api/v3/ecc/#delete-/v3/grants/-grant_id-/drafts/-draft_id-) ``` -------------------------------------------------------------------------------- /nylas-code-samples/Email/Threads/index.md: -------------------------------------------------------------------------------- ```markdown ## Changes to Threads Nylas v3 includes the following changes to the Threads API: - All Threads endpoints now require a `grant_id`, an email address, or — if you're using access token authentication — the [`/me/` construction](https://developer.nylas.com/docs/api/v3/ecc/#overview--me-syntax-for-api-calls). - You can now soft-delete threads. - The `folder` object in all Threads now contains a list of `folder_id`s instead of an array of Folders. - Support for the `view=expanded` query parameter is deprecated. ### New Threads endpoints - Delete a thread: [`DELETE /v3/grants/<NYLAS_GRANT_ID>/threads/<THREAD_ID>`](https://developer.nylas.com/docs/api/v3/ecc/#delete-/v3/grants/-grant_id-/threads/-thread_id-) ### Migrated Threads endpoints - Return all threads: `GET /threads` → [`GET /v3/grants/<NYLAS_GRANT_ID>/threads`](https://developer.nylas.com/docs/api/v3/ecc/#get-/v3/grants/-grant_id-/threads) - Return a specific thread: `GET /threads/<THREAD_ID>` → [`GET /v3/grants/<NYLAS_GRANT_ID>/threads/<THREAD_ID>`](https://developer.nylas.com/docs/api/v3/ecc/#get-/v3/grants/-grant_id-/threads/-thread_id-) - Update a specific thread: `PUT /threads/<THREAD_ID>` → [`PUT /v3/grants/<NYLAS_GRANT_ID>/threads/<THREAD_ID>`](https://developer.nylas.com/docs/api/v3/ecc/#put-/v3/grants/-grant_id-/threads/-thread_id-) ### Changes to Threads response fields Nylas v3 adds the following response fields for `/threads` requests: - `latest_draft_or_message`: The most recent draft or email message in the thread. - `has_drafts`: Indicates whether the thread has associated Drafts. The following response fields have also been updated: - `first_message_timestamp` → `earliest_message_date` - `last_message_received_timestamp` → `latest_message_received_date` - `last_message_sent_timestamp` → `latest_message_sent_date` - `last_message_timestamp` → `latest_draft_or_message.date` ``` -------------------------------------------------------------------------------- /nylas-code-samples/Auth/connected-accounts.md: -------------------------------------------------------------------------------- ```markdown ### Connected Accounts changed to Grants In Nylas v3, the concept of "grants" (as in, "the end user _granted_ you this access") replaces "connected accounts". This name better reflects the new offering and lays the groundwork for future enhancements and features. - Nylas APIs now return a `grant_id` instead of an `account_id`. - All Email, Calendar, and Contacts APIs include the `/v3/grants/<NYLAS_GRANT_ID>` prefix. - You can refer to an end user's grant using their `grant_id`, their email address, or (if you're using an access token for their grant), the `/me/` construction. - The `account.invalid` webhook trigger is deprecated. You should subscribe to `grant.expired` instead. ### New Grants endpoints - Update a grant: [`PATCH /v3/grants/<NYLAS_GRANT_ID>`](https://developer.nylas.com/docs/api/v3/admin/#patch-/v3/grants/-grantId-) - Get the current grant: [`GET /v3/grants/me`](https://developer.nylas.com/docs/api/v3/admin/#get-/v3/grants/me) ### Migrated Accounts endpoints - Return all grants: `GET /a/<NYLAS_CLIENT_ID>` → [`GET /v3/grants`](https://developer.nylas.com/docs/api/v3/admin/#get-/v3/grants) - Return a specific grant: `GET /a/<NYLAS_CLIENT_ID>/accounts/<NYLAS_ACCOUNT_ID>` → [`GET /v3/grants/<NYLAS_GRANT_ID>`](https://developer.nylas.com/docs/api/v3/admin/#get-/v3/grants/-grantId-) - Delete a grant: `DELETE /a/<NYLAS_CLIENT_ID>/accounts/<NYLAS_ACCOUNT_ID>` → [`DELETE /v3/grants/<NYLAS_GRANT_ID>`](https://developer.nylas.com/docs/api/v3/admin/#delete-/v3/grants/-grantId-) ### Deprecated Accounts endpoints - Cancel an account: `POST /a/<NYLAS_CLIENT_ID>/accounts/<NYLAS_ACCOUNT_ID>/downgrade` - Reactivate an account: `POST /a/<NYLAS_CLIENT_ID>/accounts/<NYLAS_ACCOUNT_ID>/upgrade` - Revoke all tokens: `POST /a/<NYLAS_CLIENT_ID>/accounts/<NYLAS_ACCOUNT_ID>/revoke-all` - Return token information: `POST /a/<NYLAS_CLIENT_ID>/accounts/<NYLAS_ACCOUNT_ID>/token-info` - Revoke an access token: `POST /oauth/revoke` - Return account details: `GET /account` ``` -------------------------------------------------------------------------------- /nylas-code-samples/Email/Messages/index.md: -------------------------------------------------------------------------------- ```markdown ## Changes to Messages Nylas v3 includes the following changes to the Messages API: - All Messages endpoints now require a `grant_id`, an email address, or — if you're using access token authentication — the [`/me/` construction](https://developer.nylas.com/docs/api/v3/ecc/#overview--me-syntax-for-api-calls). - You can now soft-delete email messages. - The `folder` object in all Messages now contains a list of `folder_id`s instead of an array of Folders. ### New Messages endpoints - Clean an email message: [`PUT /v3/grants/<NYLAS_GRANT_ID>/messages/clean`](https://developer.nylas.com/docs/api/v3/ecc/#put-/v3/grants/-grant_id-/messages/clean) - Delete an email message: [`DELETE /v3/grants/<NYLAS_GRANT_ID>/messages/<MESSAGE_ID>`](https://developer.nylas.com/docs/api/v3/ecc/#delete-/v3/grants/-grant_id-/messages/-message_id-) - Compose an email message: [`POST /v3/grants/<NYLAS_GRANT_ID>/messages/smart-compose`](https://developer.nylas.com/docs/api/v3/ecc/#post-/v3/grants/-grant_id-/messages/smart-compose) - Compose a reply to a specific email message: [`POST /v3/grants/<NYLAS-GRANT_ID>/messages/<MESSAGE_ID>/smart-compose`](https://developer.nylas.com/docs/api/v3/ecc/#post-/v3/grants/-grant_id-/messages/-message_id-/smart-compose) ### Migrated Messages endpoints - Return all email messages: `GET /messages` → [`GET /v3/grants/<NYLAS_GRANT_ID>/messages`](https://developer.nylas.com/docs/api/v3/ecc/#get-/v3/grants/-grant_id-/messages) - Return a specific email message: `GET /messages/<MESSAGE_ID>` → [`GET /v3/grants/<NYLAS_GRANT_ID>/messages/<MESSAGE_ID>`](https://developer.nylas.com/docs/api/v3/ecc/#get-/v3/grants/-grant_id-/messages/-message_id-) - Update an email message: `PUT /messages/<MESSAGE_ID>` → [`PUT /v3/grants/<NYLAS_GRANT_ID>/messages/<MESSAGE_ID>`](https://developer.nylas.com/docs/api/v3/ecc/#put-/v3/grants/-grant_id-/messages/-message_id-) - Send an email message: `POST /send` → [`POST /v3/grants/<NYLAS_GRANT_ID>/messages/send`](https://developer.nylas.com/docs/api/v3/ecc/#post-/v3/grants/-grant_id-/messages/send) ``` -------------------------------------------------------------------------------- /nylas-code-samples/Email/Outbox/write.md: -------------------------------------------------------------------------------- ```markdown ### Cancel scheduled send instructions `DELETE /outbox/<SCHEDULE_ID>` → [`DELETE /v3/grants/<NYLAS_GRANT_ID>/messages/schedules/<SCHEDULE_ID>`](https://developer.nylas.com/docs/api/v3/ecc/#delete-/v3/grants/-grant_id-/messages/schedules/-scheduleId-) ```Node import "dotenv/config"; import Nylas from "nylas"; const NylasConfig = { apiKey: process.env.NYLAS_API_KEY, apiUri: process.env.NYLAS_API_URI, }; const nylas = new Nylas(NylasConfig); async function deleteMessageSchedule() { try { const result = await nylas.messages.stopScheduledMessage({ identifier: process.env.NYLAS_GRANT_ID, scheduleId: process.env.SCHEDULE_ID, }); console.log("Result:", result); } catch (error) { console.error("Error deleting message:", error); } } deleteMessageSchedule(); ``` ```Java import com.nylas.NylasClient; import com.nylas.models.*; public class ReturnMessage { public static void main(String[] args) throws NylasSdkTimeoutError, NylasApiError { NylasClient nylas = new NylasClient.Builder("<NYLAS_API_KEY>").build(); Response<StopScheduledMessageResponse> message = nylas.messages().stopScheduledMessage( "<NYLAS_GRANT_ID>", "SCHEDULED_MESSAGE_ID"); System.out.println(message.getData()); } } ``` ```Python from dotenv import load_dotenv load_dotenv() import os import sys from nylas import Client nylas = Client( os.environ.get('NYLAS_API_KEY'), os.environ.get('NYLAS_API_URI') ) grant_id = os.environ.get("NYLAS_GRANT_ID") schedule_id = os.environ.get("SCHEDULE_ID") result = nylas.messages.stop_scheduled_message( grant_id, schedule_id, ) print(result) ``` ```Ruby require 'nylas' nylas = Nylas::Client.new( api_key: "<NYLAS_API_KEY>" ) messages, _ = nylas.messages.stop_scheduled_messages( identifier: ENV["NYLAS_GRANT_ID"], schedule_id: "<SCHEDULE_ID>") messages.each {|message| puts message } ``` ```API curl --request DELETE \ --url 'https://api.us.nylas.com/v3/grants/<NYLAS_GRANT_ID>/messages/schedules/<SCHEDULE_ID>' \ --header 'Accept: application/json' \ --header 'Authorization: Bearer <NYLAS_API_KEY>' ``` ``` -------------------------------------------------------------------------------- /nylas-code-samples/Auth/AppPermission-Bulk/index.md: -------------------------------------------------------------------------------- ```markdown You can use connector credentials for bulk authentication with Google's Service Accounts. What are connector credentials, I hear you asking? They're new in v3, read on... #### Introducing connector credentials Nylas v3 includes "credentials", which are packages of information that you can use when authenticating to override default connector settings and auth methods. You might use this if you're using more than one GCP auth app to authenticate your end users. You can create credentials for alternate GCP auth apps, which include the app's `client_id` and `client_secret`, and any other connection settings. Nylas encrypts and stores this information securely in a credential record. You then refer to the record in Custom auth requests using the `credential_id`. This allows you to authenticate end users with the non-default GCP auth app. Connector credentials are linked to a specific connector in a specific Nylas application. If you created the connector credential for a specific Google connector, you can't use the `credential_id` for a different connector, or with a different Nylas application. ### Bulk authentication with Google Service Accounts You can use connector credentials for bulk authentication with Google's Service Accounts. To do this, first create a [Google auth app](https://developer.nylas.com/docs/dev-guide/provider-guides/google/create-google-app/) and a [Google connector](https://developer.nylas.com/docs/dev-guide/provider-guides/google/create-google-app/#add-a-connector-to-your-nylas-application). Next, create your connector credential: 1. Make a [`POST /v3/connectors/<PROVIDER>/creds` request](https://developer.nylas.com/docs/api/v3/admin/#post-/v3/connectors/-provider-/creds). 2. Nylas returns a JSON response with the `id` of the new connector credential. Be sure to save it somewhere secure. Now, you can create an app permission grant: 1. Make a [`POST /v3/connect/custom` request](https://developer.nylas.com/docs/api/v3/admin/#post-/v3/connect/custom) that includes the `credential_id` of the connector credential you created. 2. Nylas returns a JSON response containing the grant's information. ``` -------------------------------------------------------------------------------- /CONTRIBUTION.md: -------------------------------------------------------------------------------- ```markdown ## How to Contribute First, thanks for taking the time to contribute to our code sample! This guide will tell you everything you need to know about contributing to the Nylas Samples. ## Code of Conduct We as members, contributors, and leaders pledge to make participation in our community a harassment-free experience for everyone, regardless of age, body size, visible or invisible disability, ethnicity, sex characteristics, gender identity and expression, level of experience, education, socio-economic status, nationality, personal appearance, race, religion, or sexual identity and orientation. We pledge to act and interact in ways that contribute to an open, welcoming, diverse, inclusive, and healthy community. By participating in this project, you agree to abide by the [Nylas Code of Conduct](CODE_OF_CONDUCT.md). If you feel there is a conduct issue related to this project, please raise it per the outlined process, and we will address it. How to Ask a Question If you have a question that needs an answer, create an issue, and label it as a question. ## How To Contribute ### Report a Bug or Request a Feature If you encounter any bugs while using this software or request a new feature or enhancement, feel free to create an issue to report it; make sure you add a label to indicate what type of issue it is. ### Contribute Code Pull requests are welcome for bug fixes. If you want to implement something new, please create an issue to request a feature first to discuss it. The earlier you tell us about your intentions, the better. ### Create a Pull Request Please follow best practices for creating git commits. When your code is ready to be submitted, you can submit a pull request to begin the code review process. We can only accept code you are authorized to contribute to the project. This project includes a PR template that includes the line below. This line must be present in your PR for us to accept it: - I confirm that this contribution is made under the terms of the [license](LICENSE) found in the root directory of this repository's source tree and that I have the authority necessary to make this contribution on behalf of its copyright owner. ``` -------------------------------------------------------------------------------- /nylas-code-samples/Email/Search/read.md: -------------------------------------------------------------------------------- ```markdown ### Search messages `/messages/search` → [`GET /v3/grants/<NYLAS_GRANT_ID>/messages` request](https://developer.nylas.com/docs/api/v3/ecc/#get-/v3/grants/-grant_id-/messages) ```Node import 'dotenv/config' import Nylas from 'nylas' const NylasConfig = { apiKey: process.env.NYLAS_API_KEY, apiUri: process.env.NYLAS_API_URI, } const nylas = new Nylas(NylasConfig) async function searchInbox() { try { const result = await nylas.messages.list({ identifier: process.env.NYLAS_GRANT_ID, queryParams: { search_query_native: 'nylas', limit: 5 } }) console.log('search results:', result) } catch (error) { console.error('Error to complete search:', error) } } searchInbox() ``` ```Java ``` ```Python from dotenv import load_dotenv load_dotenv() import os import sys from nylas import Client nylas = Client( os.environ.get('NYLAS_API_KEY'), os.environ.get('NYLAS_API_URI') ) grant_id = os.environ.get("NYLAS_GRANT_ID") messages = nylas.messages.list( grant_id, query_params={ "limit": 5, "search_query_native": 'nylas' } ) print(messages) ``` ```Ruby ``` ```API ``` ### Search threads `/threads/search` → [`GET /v3/grants/<NYLAS_GRANT_ID>/threads` request](https://developer.nylas.com/docs/api/v3/ecc/#get-/v3/grants/-grant_id-/threads) ```Node import 'dotenv/config' import Nylas from 'nylas' const NylasConfig = { apiKey: process.env.NYLAS_API_KEY, apiUri: process.env.NYLAS_API_URI, } const nylas = new Nylas(NylasConfig) async function searchInbox() { try { const result = await nylas.threads.list({ identifier: process.env.NYLAS_GRANT_ID, queryParams: { search_query_native: 'nylas', limit: 5 } }) console.log('search results:', result) } catch (error) { console.error('Error to complete search:', error) } } searchInbox() ``` ```Java ``` ```Python from dotenv import load_dotenv load_dotenv() import os import sys from nylas import Client nylas = Client( os.environ.get('NYLAS_API_KEY'), os.environ.get('NYLAS_API_URI') ) grant_id = os.environ.get("NYLAS_GRANT_ID") messages = nylas.threads.list( grant_id, query_params={ "limit": 5, "search_query_native": 'nylas' } ) print(messages) ``` ```Ruby ``` ```API ``` ``` -------------------------------------------------------------------------------- /nylas-code-samples/Start/Dashboard-Import/index.md: -------------------------------------------------------------------------------- ```markdown :::warn ⚠️ **You can only import a v2 application into a v3 application in the same data center region**. You cannot use the Import tools to move a v2 application to a different region. ::: 1. Go to the [Nylas v3 Dashboard](https://dashboard-v3.nylas.com/?utm_source=migration-station&utm_medium=devrel-surfaces&utm_campaign=migration-station&utm_content=one-click-migrate) and log in as an admin user. Only administrators can migrate v2 applications. 2. Find the v2 app you want to migrate. It'll be marked `Legacy`. 3. Click **Migrate app** next to that legacy app to create a new v3 application. 4. In the dialog, confirm the title and the environment tag (Development, Stating, Production), and optionally add a description. 5. Click **Create app**. Nylas copies your v2 connector data to the v3 application and starts migrating connected accounts. After you start a migration, Nylas brings you back to the application list. The migration process is asynchronous, and you can continue working on other things while Nylas works. The v3 version of the application appears in the list with a `Migrated` label. The info icon shows the v2 application ID that the v3 app is based on. You can click the v3 application and go to the **Grants** page to see the migration progress. 6. After you start the migration, go to your v3 application, and check the list of migrated connectors to make sure it looks complete. If you need to do additional steps for specific providers, they are covered later in this guide. 7. Continue following the rest of the instructions in this guide. The account migration might take some time. (If you're already done with the rest of your migration, have a ☕️ and maybe even some 🍰 to celebrate! You're almost done.) 8. When Nylas finishes migrating accounts to Grants, the Grants page for the new v3 application shows progress as 100%. You can then download a CSV report of the migration details, including any accounts that could not be migrated and the error Nylas encountered. :::success 👀 **Good to know:** The Nylas migration job marks an account migration as `failed` if it was unable to import and create a grant for that account. However, for many of these cases, the user can still start a completely new authentication on the v3 system to use your application. This is expected in the case of Microsoft accounts that used Exchange, such as personal accounts on Hotmail, Live, and Outlook. ::: ``` -------------------------------------------------------------------------------- /nylas-code-samples/Contacts/index.md: -------------------------------------------------------------------------------- ```markdown ## Changes to Contacts Nylas v3 includes the following changes to the Contacts API: - Contacts are now returned as JSON objects, instead of being listed as comma-separated values. - All Contacts endpoints now require a `grant_id`, an email address, or — if you're using access token authentication — the [`/me/` construction](https://developer.nylas.com/docs/api/v3/ecc/#overview--me-syntax-for-api-calls). - You might need to add scopes to access domain-level or inbox contacts. If you use only contacts from address books, you don't need to make any changes. - To access contacts parsed from an end user's inbox, use the `inbox` source and request the `contacts.other.readonly` Google scope or `People.Read` Microsoft scope. - To access contacts parsed from an end user's domain address book, use the `domain` source and request the `directory.readonly` Google scope or `People.Read` Microsoft scope. - Nylas polls for changes to contacts for Google accounts in five-minute intervals. ### Migrated Contacts endpoints - Return all contacts: `GET /contacts` → [`GET /v3/grants/<NYLAS_GRANT_ID>/contacts`](https://developer.nylas.com/docs/api/v3/ecc/#get-/v3/grants/-grant_id-/contacts) - Create a contact: `POST /contacts` → [`POST /v3/grants/<NYLAS_GRANT_ID>/contacts`](https://developer.nylas.com/docs/api/v3/ecc/#post-/v3/grants/-grant_id-/contacts) - Return a contact: `GET /contacts/<CONTACT_ID>` → [`GET /v3/grants/<NYLAS_GRANT_ID>/contacts/<CONTACT_ID>`](https://developer.nylas.com/docs/api/v3/ecc/#get-/v3/grants/-grant_id-/contacts/-contact_id-) - Update a contact: `PUT /contacts/<CONTACT_ID>` → [`PUT /v3/grants/<NYLAS_GRANT_ID>/contacts/<CONTACT_ID>`](https://developer.nylas.com/docs/api/v3/ecc/#put-/v3/grants/-grant_id-/contacts/-contact_id-) - Delete a contact: `DELETE /contacts/<CONTACT_ID>` → [`DELETE /v3/grants/<NYLAS_GRANT_ID>/contacts/<CONTACT_ID>`](https://developer.nylas.com/docs/api/v3/ecc/#delete-/v3/grants/-grant_id-/contacts/-contact_id-) - Return all contact groups: `GET /contacts/groups` → [`GET /v3/grants/<NYLAS_GRANT_ID>/contacts/groups`](https://developer.nylas.com/docs/api/v3/ecc/#get-/v3/grants/-grant_id-/contacts/groups) ### Deprecated Contacts endpoints The v2 `GET /contacts/<CONTACT_ID>/picture` endpoint has been deprecated. Instead, you can now include the `?profile_picture=true` query parameter in a [Get Contact request](https://developer.nylas.com/docs/api/v3/ecc/#get-/v3/grants/-grant_id-/contacts/-contact_id-). ``` -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- ```typescript import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"; import express from "express"; import { SSEServerTransport } from "@modelcontextprotocol/sdk/server/sse.js"; // Import resources, tools, and prompts import { registerResources } from "./resources/index.js"; import { registerTools } from "./tools/index.js"; import { registerPrompts } from "./prompts/index.js"; // Create an MCP server const server = new McpServer({ name: "Nylas API Docs", version: "1.0.0" }); // Register all resources, tools, and prompts registerResources(server); registerTools(server); registerPrompts(server); async function main() { // Determine if we're running in stdio or HTTP mode const mode = process.env.MCP_MODE || "stdio"; if (mode === "stdio") { // Connect using stdio transport const transport = new StdioServerTransport(); await server.connect(transport); } else if (mode === "http") { // Start HTTP server with SSE const app = express(); const port = parseInt(process.env.PORT || "3000"); // Map to store active connections const activeConnections = new Map(); app.get("/sse", async (req, res) => { const id = Date.now().toString(); const transport = new SSEServerTransport("/messages", res); // Store the transport in our map activeConnections.set(id, transport); // Set headers for SSE res.setHeader("Content-Type", "text/event-stream"); res.setHeader("Cache-Control", "no-cache"); res.setHeader("Connection", "keep-alive"); res.setHeader("Access-Control-Allow-Origin", "*"); // Add a query parameter with the connection ID res.write(`data: ${JSON.stringify({ id })}\n\n`); // Connect to the server await server.connect(transport); // Remove the connection when it closes req.on("close", () => { activeConnections.delete(id); }); }); app.post("/messages", express.json(), async (req, res) => { const id = req.query.id as string; if (!id || !activeConnections.has(id)) { return res.status(404).json({ error: "Connection not found" }); } const transport = activeConnections.get(id); await transport.handlePostMessage(req, res); }); app.listen(port, () => { console.log(`Nylas API MCP server running on http://localhost:${port}`); }); } else { console.error(`Unknown mode: ${mode}. Use 'stdio' or 'http'.`); process.exit(1); } } main().catch(err => { console.error("Fatal error:", err); process.exit(1); }); ``` -------------------------------------------------------------------------------- /nylas-code-samples/Auth/Providers/ms.md: -------------------------------------------------------------------------------- ```markdown Nylas v3 includes the following changes that require you modify your Azure auth app before you can re-authenticate your end users: - The **Authorized redirect URIs** for Hosted Auth have been updated in v3. - **U.S.**: `https://api.us.nylas.com/v3/connect/callback` - **E.U.**: `https://api.eu.nylas.com/v3/connect/callback` - Nylas v3 no longer uses "Nylas scopes", and uses Microsoft's scopes instead. ### Update authorized redirect URIs in Azure app For all Azure auth apps that you want to connect to Nylas v3, the authorized redirect URIs for Hosted Auth have been updated. Add one, or both, of the following to the **Authorized redirect URIs** section of your Azure app: - **U.S.**: `https://api.us.nylas.com/v3/connect/callback` - **E.U.**: `https://api.eu.nylas.com/v3/connect/callback` ### Calculate v3 scopes for Azure app In v3, Nylas no longer uses "Nylas scopes", but directly uses Microsoft's scopes instead. Make a list of the v2 API endpoints your project uses, then read the [v3 scopes documentation](https://developer.nylas.com/docs/v3/auth/v3-scopes/) to determine the scopes you need for v3. When you know what v3 scopes you need, add them to your Azure auth app's Entra ID system (previously "Azure ID"). For more information, see Microsoft's official [Configure Azure AD Graph permissions for an app registration guide](https://learn.microsoft.com/en-us/graph/migrate-azure-ad-graph-configure-permissions). ### Do I need to re-verify my Azure app? _In general_, you shouldn't need to re-verify your Azure app unless you're adding new features to your project. However, some items might require changes if your app doesn't already include their scopes: - **If you're using Microsoft Hosted OAuth in v2 _without_ Graph scopes**, you might need to re-verify when you migrate to Graph scopes. - Nylas recommends you migrate to Graph scopes using a new Azure auth app, so you can switch over without disrupting your end users' workflows. - **If you use `thread.replied` webhook notifications**, these now use the `email.read` scope in v3, instead of the v2 `email.metadata` scopes. - **If you use the Contacts APIs and want to use domain-level or inbox contacts**, you might need to add some scopes. If you use address book contacts only, you don't need to make any changes. ### Changes to Microsoft auth features - The only EWS account type that Nylas v3 supports is **Exchange on-premises**. Other EWS accounts (personal accounts on Hotmail, Office 365, Live, and Outlook) must complete a fresh authentication flow using the Microsoft connector, which upgrades them to use Graph scopes. - Nylas no longer supports Exchange on-premises service accounts. - Nylas now supports **Account in any organizational directory and personal Microsoft accounts**. This allows end users to authenticate using their personal accounts. ``` -------------------------------------------------------------------------------- /nylas-code-samples/Start/api-import.md: -------------------------------------------------------------------------------- ```markdown The [Nylas Migration APIs](https://developer.nylas.com/docs/api/v3/migration/) help you migrate your application and end-user data from Nylas v2 to v3. This section is intended to help you [upgrade the rest of your application](https://developer.nylas.com/docs/v2/upgrade-to-v3/) to Nylas v3. :::warn ⚠️ **The Migration APIs are for one-time translation of data and objects from v2 to v3**. You can retry API calls if you run into issues, but they will be rate-limited, and you _should not_ use them as part of your project's code or object handling logic. ::: Before you begin... - Make sure your v2 and v3 Nylas organizations are linked. If they're correctly linked, you can see all your legacy v2 Nylas applications from both regions in the v3 Dashboard. - If you don't see all of your legacy applications on the Dashboard, [contact Nylas Support](https://support.nylas.com/hc/en-us/requests/new?ticket_form_id=360000247971). - Create a v3 Nylas application for each v2 application you plan to migrate. ### Migrate your Nylas implementation :::warn ⚠️ **Make sure you use the correct region!** In Nylas v3, you manage both your U.S. and E.U. applications using the same Dashboard. Make sure you select the same region as the corresponding v2 application when you create a v3 application. ::: Now that you're set up, it's time to migrate your data: 1. Make a [`POST /v3/migration-tools/link-v2v3-apps` request](https://developer.nylas.com/docs/api/v3/migration/#post-/v3/migration-tools/link-v2v3-apps) to link a v2 application to its equivalent v3 application. 2. Make a [`POST /v3/migration-tools/import-v2-app` request](https://developer.nylas.com/docs/api/v3/migration/#post-/v3/migration-tools/link-v2v3-apps) to migrate your v2 Integrations to v3 Connectors, and copy other important application settings. 3. Make any necessary changes to your provider auth apps. This will be covered later in the guide, but you can return to this section when you complete the process. 4. Configure your connectors. (This is also covered later in the guide.) 5. **Subscribe to v3 notification triggers** (using either webhooks or Pub/Sub) so you can keep track of any objects that change while you're migrating. 6. Make a [`POST /v3/migration-tools/grants/<NYLAS_ACCOUNT_ID>/clone` request](https://developer.nylas.com/docs/api/v3/migration/#post-/v3/migration-tools/grants/-account_id-/clone) to test migrating a single account at a time. - You can use this migrated account to do initial testing on your webhooks and other API settings before you migrate more end users. 7. When you're ready to migrate more end-user information, make a [`POST /v3/migration-tools/snapshot-batch-clone` request](https://developer.nylas.com/docs/api/v3/migration/#post-/v3/migration-tools/snapshot-batch-clone) to migrate all of your v2 Connected Accounts to v3 Grants. - Migrated grants are de-duplicated with your v2 connected accounts, so they're counted as one account for billing purposes. 8. Upgrade your project code to use the Nylas v3 APIs and systems. - Make sure you update your login page to use v3 authentication, so end users can seamlessly use v3 when they re-authenticate! ``` -------------------------------------------------------------------------------- /nylas-code-samples/Coda/index.md: -------------------------------------------------------------------------------- ```markdown You did it! 🎉 You got through all the migration instructions! The info below is some "good to know" information that you don't necessarily need to upgrade and migrate, but that you'll want to know for later. ### Tuning your queries Service providers like Google and Microsoft implement strict [rate limiting](https://developer.nylas.com/docs/dev-guide/platform/rate-limits/). Because Nylas v3 queries providers directly, it's important that you make sure your API requests limit the amount of data they request so you don't hit these provider rate limits. You can use filtering and query selection to limit both the number of objects Nylas queries, and the amount of data Nylas returns about those objects. For example, if you're searching for an event with a specific title, you would ignore other returned fields like the event description. To make an efficient query, you could use a `title=<Your title here>` query parameter, and then use `select=title,id` to make sure Nylas only returns the information you need. ### Changes to metadata In Nylas v2, you could add up to 50 pairs of arbitrary metadata key-pairs to Calendars, Events, and Messages, and use the keys as query parameters to filter data that Nylas returns. This sometimes led to high latency when returning results. In v3, you can still add metadata key-pairs to Calendars and Events, but Nylas only indexes five specific keys: `key1`, `key2`, `key3`, `key4`, and `key5`. To continue using metadata filtering, you must write the values you want to filter by to the keys that Nylas indexes. Nylas uses the `key5` value when calculating round-robin availability for events. If you're using round-robin scheduling, you might want to make a plan for how you distribute queryable metadata among the five keys. You cannot use metadata filters with provider value filters, except for `calendar_id`. ### Terminology updates We've taken the opportunity to clarify some of our language with Nylas v3: - The **integration** object that you create in your Nylas application to store provider details is now called a **connector**. - **Provider integration applications** are now called **provider auth apps**. - **Native authentication** is now **Custom authentication**. - **Service accounts** (sometimes called "app permissions") are now **bulk authentication grants**. - The `redirect_uri` set in Nylas applications that use Hosted auth is now called the `callback_uri`. This helps to distinguish it from the Nylas `redirect_uri` set in provider auth apps. - The `callback_uri` used by webhook subscriptions is now called `webhook_url`. ### Do your users need to re-authenticate? When you migrate from Nylas v2 to v3, some end users will be able to log in right away, and some will need to re-authenticate. - When you create a connector for a new provider such as iCloud, Yahoo, or Exchange on-prem. - When you upgrade a Microsoft account's scopes from Exchange scopes to Graph scopes. - If the account used token auth, they just need to refresh their account. - If the account used password auth, they must fully re-authenticate. - When you change the scopes you request for your Google or Microsoft provider auth app. ``` -------------------------------------------------------------------------------- /nylas-code-samples/Start/index.md: -------------------------------------------------------------------------------- ```markdown #### Changes to API format and use - The Nylas API endpoints have changed for v3: - `api.nylas.com` → `api.us.nylas.com` - `canada.api.nylas.com` → `api.us.nylas.com` - `ireland.api.nylas.com` → `api.eu.nylas.com` - All Nylas traffic now goes through HTTPS (port `443`), and Nylas no longer supports HTTP (port `80`). - The Nylas API no longer honors the `Nylas-API-Version` header. - Endpoint URIs now include the Nylas API version (for example, `GET /v3/applications`). - URIs for endpoints that act on an end user's data include a `/grants/<NYLAS_GRANT_ID>` prefix that serves as a record locator (for example, `POST /v3/grants/<NYLAS_GRANT_ID>/calendars`). - Nylas v3 retrieves data directly from the service provider, and references it using the provider's object IDs. - Nylas no longer generates a "Nylas ID" for objects. If your project uses Nylas IDs to keep track of objects, you can use the [`POST /v3/migration-tools/translate` endpoint](https://developer.nylas.com/docs/api/v3/migration/#post-/v3/migration-tools/translate) to update object IDs in your database. #### Upgrading to Bearer token authentication Nylas v3 supports `Authorization: Bearer <TOKEN>` only. You can no longer authenticate end users using `Authorization: <TOKEN>` or `Authorization: Basic <BASE64_ENCODED_TOKEN>`. You can authenticate using either the secret from an application-wide API key (generated in the v3 Nylas Dashboard), or the short-lived access token for an individual end user (returned as part of a successful authentication). #### Use API key instead of Client Secret The v3 Nylas Dashboard only displays the `client_id` for Nylas applications. Where you would previously have used the Nylas application's `client_secret` in API requests, you now use an _API key secret_. #### Grants replace Connected Accounts Nylas v3 replaces "connected accounts" with "grants" (as in, "the end user _granted_ you access to their data"). Because of this, Nylas v3 returns a `grant_id` instead of an `account_id` in responses. If you're authorizing requests using an API key, you can reference a specific grant using either its `grant_id` or the associated email address. If you're authorizing requests using an end user's access token, you can use the [`/me/` construction](https://developer.nylas.com/docs/api/v3/ecc/#overview--me-syntax-for-api-calls) to refer to the grant associated with the token (for example, `GET /v3/grants/me/messages`). #### Provider scopes replace Nylas scopes Nylas v3 removes the [Nylas scopes abstraction](https://developer.nylas.com/docs/v2/developer-guide-v2/authentication/authentication-scopes/), and instead uses provider scopes to control your application's access to end-user data. You can either create a login button for each provider that your application supports, or use the [`POST /v3/providers/detect` endpoint](https://developer.nylas.com/docs/api/v3/admin/#post-/v3/providers/detect) to determine which provider an end user is trying to authenticate with, so you can request the correct set of provider scopes. #### Changes to pagination Nylas v3 replaces the `offset` parameter with the new `cursor` query parameter, and changes the default page size to 50. For more information, see the [Pagination reference](https://developer.nylas.com/docs/api/v3/ecc/#overview--pagination). ``` -------------------------------------------------------------------------------- /src/resources/index.ts: -------------------------------------------------------------------------------- ```typescript import { McpServer, ResourceTemplate } from "@modelcontextprotocol/sdk/server/mcp.js"; // Import specific resource handlers import { registerDocsResources } from "./docs.js"; import { registerCodeSamplesResources } from "./code-samples.js"; import { registerEndpointResources } from "./endpoints.js"; /** * Register all resources with the MCP server */ export function registerResources(server: McpServer) { // Register overview resources registerOverviewResources(server); // Register documentation resources registerDocsResources(server); // Register code sample resources registerCodeSamplesResources(server); // Register endpoint documentation registerEndpointResources(server); } /** * Register overview resources that provide general information about the Nylas API */ function registerOverviewResources(server: McpServer) { // Register a general overview of the Nylas API server.resource( "nylas-overview", "nylas://overview", async (uri) => ({ contents: [{ uri: uri.href, text: `# Nylas API Overview The Nylas Communications Platform is a set of APIs that allows developers to easily integrate email, calendar, and contacts functionality into their applications. The platform provides a unified interface to access data from various providers like Gmail, Office 365, Exchange, and more. ## Key Features - **Email API**: Read, send, and organize emails - **Calendar API**: Create, read, update, and delete calendar events - **Contacts API**: Manage contact information - **Authentication**: Secure OAuth-based authentication - **Webhooks**: Real-time updates when data changes - **Provider Agnostic**: Works with Gmail, Office 365, Exchange, and more ## Getting Started 1. Create a Nylas developer account 2. Set up your application 3. Authenticate users 4. Start making API calls For more information, use the resources provided by this MCP server to explore specific parts of the Nylas API.`, mimeType: "text/markdown" }] }) ); // Register authentication overview server.resource( "nylas-auth", "nylas://auth/overview", async (uri) => ({ contents: [{ uri: uri.href, text: `# Nylas Authentication Overview Nylas uses OAuth 2.0 for authentication. The authentication flow allows your application to obtain an access token that can be used to make API calls on behalf of your users. ## Authentication Flow 1. **Redirect user**: Direct your user to the Nylas OAuth page 2. **User grants access**: User authorizes your application 3. **Receive callback**: Nylas redirects back to your application with a code 4. **Exchange code**: Your server exchanges the code for an access token 5. **Make API calls**: Use the access token to make API requests ## Code Example \`\`\`typescript import { Nylas } from '@nylas/nylas-js'; // Initialize Nylas client Nylas.config({ clientId: 'YOUR_CLIENT_ID', clientSecret: 'YOUR_CLIENT_SECRET', }); // Generate OAuth URL const authUrl = Nylas.urlForAuthentication({ redirectURI: 'YOUR_REDIRECT_URI', scopes: ['email.read_only', 'calendar.read_only', 'contacts.read_only'], }); // Redirect user to authUrl // After user is redirected back to your redirect URI: const code = 'CODE_FROM_URL_PARAMETER'; // Exchange code for token Nylas.exchangeCodeForToken(code).then(token => { console.log('Access token:', token); }); \`\`\` For more detailed information on authentication, refer to the specific authentication resources and code samples.`, mimeType: "text/markdown" }] }) ); } ``` -------------------------------------------------------------------------------- /nylas-code-samples/Calendar/Events/index.md: -------------------------------------------------------------------------------- ```markdown ## Changes to Events Nylas v3 includes the following changes to the Events API: - The format for event start and end times has been simplified. - The `starts_before`, `starts_after`, `ends_before`, and `ends_after` parameters have been simplified to only two variables: `start` and `end`. `start` is equivalent to `ends_after` and `end` is equivalent to `starts_before`. When you use either in a query, an Event is returned if any part of it occurs between the `start` and `end` times. - The following are added in v3: - `order_by` query parameter for all Events `GET` calls. - `html_link` property for all Events `GET` calls. - `capacity` property for all Events calls. - The following are removed in v3: - `event_id` query parameter for `GET` calls. - `updated_at_before` property for `GET` calls. - `participants` query parameter for `GET` calls. - `customer_event_id` query parameter and property for all calls. - `notifications` property for `POST` and `PUT` calls. - `original_start_time` property for `GET` calls. - `organizer_email` property for `GET` calls. - `organizer_name` property for `GET` calls. - `owner` property for `GET` calls. - `message_id` for property `GET` calls. - `recurrence.timezone` property for `GET` calls. - The Generate ICS file endpoint (`POST /events/to-ics`) is not available. ### Changes to syntax **Nylas v3 requires a calendar ID for almost all Events requests**, but also includes a new endpoint that you can use to return all calendars an end user has access to. This means that you can no longer list all events from all calendars, and you must be able to identify which calendar an event is associated with in order to retrieve, update, or delete it. You can set the `calendar_id` to the end user's email address, or set it to `default` to select the default or main calendar for the end user on the service provider. You can approach finding the calendar ID in the following ways: - Get a list of all calendars for the grant ID, and find the ID of the calendar you want to work with, and use that. - Use the `primary` parameter to specify the main calendar associated with the Grant on the service provider. - For virtual calendars, the `primary` calendar is the first calendar created for a grant, and it cannot be deleted. - iCloud calendars do not have a `primary` calendar, so you need to query for the ID first. ### New Events endpoints - Send RSVP: [`POST /v3/grants/<NYLAS_GRANT_ID>/events/<EVENT_ID>/send-rsvp`](https://developer.nylas.com/docs/api/v3/ecc/#post-/v3/grants/-grant_id-/events/-event_id-/send-rsvp) ### Migrated Events endpoints - Return all events: `GET /events` → [`GET /v3/grants/<NYLAS_GRANT_ID>/events`](https://developer.nylas.com/docs/api/v3/ecc/#get-/v3/grants/-grant_id-/events) - Create an event: `POST /events` → [`POST /v3/grants/<NYLAS_GRANT_ID>/events`](https://developer.nylas.com/docs/api/v3/ecc/#post-/v3/grants/-grant_id-/events) - Return an event: `GET /events/<EVENT_ID>` → [`GET /v3/grants/<NYLAS_GRANT_ID>/events/<EVENT_ID>`](https://developer.nylas.com/docs/api/v3/ecc/#get-/v3/grants/-grant_id-/events/-event_id-) - Update an event: `PUT /events/<EVENT_ID>` → [`PUT /v3/grants/<NYLAS_GRANT_ID>/events/<EVENT_ID>`](https://developer.nylas.com/docs/api/v3/ecc/#put-/v3/grants/-grant_id-/events/-event_id-) - Delete an event: `DELETE /events/<EVENT_ID>` → [`DELETE /v3/grants/<NYLAS_GRANT_ID>/events/<EVENT_ID>`](https://developer.nylas.com/docs/api/v3/ecc/#delete-/v3/grants/-grant_id-/events/-event_id-) - Send RSVP: `POST /send-rsvp` → [`POST /v3/grants/<NYLAS_GRANT_ID>/events/<EVENT_ID>/send-rsvp`](https://developer.nylas.com/docs/api/v3/ecc/#post-/v3/grants/-grant_id-/events/-event_id-/send-rsvp) ``` -------------------------------------------------------------------------------- /nylas-code-samples/Email/Labels-Folders/read.md: -------------------------------------------------------------------------------- ```markdown ### Get all folders `GET /folders`, `GET /labels` → [`GET /v3/grants/<NYLAS_GRANT_ID>/folders`](https://developer.nylas.com/docs/api/v3/ecc/#get-/v3/grants/-grant_id-/folders) ```Node import 'dotenv/config' import Nylas from 'nylas' const NylasConfig = { apiKey: process.env.NYLAS_API_KEY, apiUri: process.env.NYLAS_API_URI, } const nylas = new Nylas(NylasConfig) async function fetchFolders() { try { const folders = await nylas.folders.list({ identifier: process.env.NYLAS_GRANT_ID, }) console.log('folders:', folders) } catch (error) { console.error('Error fetching folders:', error) } } fetchFolders() ``` ```Java import com.nylas.NylasClient; import com.nylas.models.*; public class ReturnFolders { public static void main(String[] args) throws NylasSdkTimeoutError, NylasApiError { NylasClient nylas = new NylasClient.Builder("<NYLAS_API_KEY>").build(); ListResponse<Folder> folders = nylas.folders().list("<NYLAS_GRANT_ID>"); for(Folder folder : folders.getData()){ System.out.println(folder.getId() + " | " + folder.getName()); } } } ``` ```Python from dotenv import load_dotenv load_dotenv() import os import sys from nylas import Client nylas = Client( os.environ.get('NYLAS_API_KEY'), os.environ.get('NYLAS_API_URI') ) grant_id = os.environ.get("NYLAS_GRANT_ID") folder_id = os.environ.get("FOLDER_ID") folder = nylas.folders.list( grant_id ) print(folder) ``` ```Ruby require 'nylas' nylas = Nylas::Client.new( api_key: "<NYLAS_API_KEY>" ) folders, _ = nylas.folders.list(identifier: "<NYLAS_GRANT_ID>") folders.each { |folder| puts "#{folder[:id]} | #{folder[:name]}" } ``` ```API curl --request GET \ --url https://api.us.nylas.com/v3/grants/<NYLAS_GRANT_ID>/folders \ --header 'Accept: application/json' \ --header 'Authorization: Bearer <NYLAS_API_KEY>' \ --header 'Content-Type: application/json' ``` ### Get a specific folder `GET /folders/<FOLDER_ID>`, `GET /labels/<LABEL_ID>` → [`GET /v3/grants/<NYLAS_GRANT_ID>/folders/<FOLDER_ID>`](https://developer.nylas.com/docs/api/v3/ecc/#get-/v3/grants/-grant_id-/folders/-folder_id-) ```Node import 'dotenv/config' import Nylas from 'nylas' const NylasConfig = { apiKey: process.env.NYLAS_API_KEY, apiUri: process.env.NYLAS_API_URI, } const nylas = new Nylas(NylasConfig) async function fetchFolderById() { try { const folder = await nylas.folders.find({ identifier: process.env.NYLAS_GRANT_ID, folderId: process.env.FOLDER_ID, }) console.log('Folder:', folder) } catch (error) { console.error('Error fetching folder:', error) } } fetchFolderById() ``` ```Java import com.nylas.NylasClient; import com.nylas.models.*; public class GetLabel { public static void main(String[] args) throws NylasSdkTimeoutError, NylasApiError { Dotenv dotenv = Dotenv.load(); NylasClient nylas = new NylasClient.Builder("<NYLAS_API_KEY>").build(); Response<Folder> folder = nylas.folders().find(dotenv.get("NYLAS_GRANT_ID"), "<FOLDER_ID>"); System.out.println(folder); } } ``` ```Python from dotenv import load_dotenv load_dotenv() import os import sys from nylas import Client nylas = Client( os.environ.get('NYLAS_API_KEY'), os.environ.get('NYLAS_API_URI') ) grant_id = os.environ.get("NYLAS_GRANT_ID") folder_id = os.environ.get("FOLDER_ID") message = nylas.folders.find( grant_id, folder_id, ) print(message) ``` ```Ruby require 'nylas' nylas = Nylas::Client.new( api_key: ENV["NYLAS_API_KEY"] ) folder, _ = nylas.folders.find(identifier: ENV["NYLAS_GRANT_ID"], folder_id: "<FOLDER_ID>") puts folder ``` ```API curl --request GET \ --url https://api.us.nylas.com/v3/grants/<NYLAS_GRANT_ID>/folders/<FOLDER_ID> \ --header 'Accept: application/json' \ --header 'Authorization: Bearer <NYLAS_API_KEY>' \ --header 'Content-Type: application/json' ``` ``` -------------------------------------------------------------------------------- /nylas-code-samples/Email/Outbox/read.md: -------------------------------------------------------------------------------- ```markdown ### Get all scheduled messages `GET /outbox` → [`GET /v3/grants/{grant_id}/messages/schedules`](https://developer.nylas.com/docs/api/v3/ecc/#get-/v3/grants/-grant_id-/messages/schedules) ```Node import 'dotenv/config' import Nylas from 'nylas' const NylasConfig = { apiKey: process.env.NYLAS_API_KEY, apiUri: process.env.NYLAS_API_URI, } const nylas = new Nylas(NylasConfig) async function fetchMessageSchedules() { try { const identifier: string = process.env.NYLAS_GRANT_ID const messageSchedules = await nylas.messages.listScheduledMessages({ identifier, }) console.log('Message Schedules:', messageSchedules) } catch (error) { console.error('Error fetching message schedules:', error) } } fetchMessageSchedules() ``` ```Java import com.nylas.NylasClient; import com.nylas.models.*; public class ReturnMessage { public static void main(String[] args) throws NylasSdkTimeoutError, NylasApiError { NylasClient nylas = new NylasClient.Builder("<NYLAS_API_KEY>").build(); Response<ScheduledMessagesList> message = nylas.messages().listScheduledMessages("<NYLAS_GRANT_ID>"); System.out.println(message.getData()); } } ``` ```Python from dotenv import load_dotenv load_dotenv() import os import sys from nylas import Client nylas = Client( os.environ.get('NYLAS_API_KEY'), os.environ.get('NYLAS_API_URI') ) grant_id = os.environ.get("NYLAS_GRANT_ID") messages = nylas.messages.list_scheduled_messages( grant_id ) print(messages) ``` ```Ruby require 'nylas' nylas = Nylas::Client.new( api_key: "<NYLAS_API_KEY>" ) messages, _ = nylas.messages.list_scheduled_messages(identifier: ENV["NYLAS_GRANT_ID"]) messages.each {|message| puts message } ``` ```API curl --request GET \ --url 'https://api.us.nylas.com/v3/grants/<NYLAS_GRANT_ID>/messages/schedules' \ --header 'Accept: application/json' \ --header 'Authorization: Bearer <NYLAS_API_KEY>' ``` ### Get a specific scheduled message You can use the [`GET /v3/grants/<NYLAS_GRANT_ID>/messages/schedules/<SCHEDULE_ID>` endpoint](https://developer.nylas.com/docs/api/v3/ecc/#get-/v3/grants/-grant_id-/messages/schedules/-scheduleId-) to get a specific scheduled email message. ```Node import "dotenv/config"; import Nylas from "nylas"; const NylasConfig = { apiKey: process.env.NYLAS_API_KEY, apiUri: process.env.NYLAS_API_URI, }; const nylas = new Nylas(NylasConfig); async function fetchScheduledMessageById() { try { const events = await nylas.messages.findScheduledMessage({ identifier: process.env.NYLAS_GRANT_ID, scheduleId: process.env.SCHEDULE_ID, }); console.log("Events:", events); } catch (error) { console.error("Error fetching calendars:", error); } } fetchScheduledMessageById(); ``` ```Java import com.nylas.NylasClient; import com.nylas.models.*; public class ReturnMessage { public static void main(String[] args) throws NylasSdkTimeoutError, NylasApiError { NylasClient nylas = new NylasClient.Builder("<NYLAS_API_KEY>").build(); Response<ScheduledMessage> message = nylas.messages().findScheduledMessage( "<NYLAS_GRANT_ID>", "<SCHEDULED_MESSAGE_ID>"); System.out.println(message.getData()); } } ``` ```Python from dotenv import load_dotenv load_dotenv() import os import sys from nylas import Client nylas = Client( os.environ.get('NYLAS_API_KEY'), os.environ.get('NYLAS_API_URI') ) grant_id = os.environ.get("NYLAS_GRANT_ID") schedule_id = os.environ.get("SCHEDULE_ID") event = nylas.messages.find_scheduled_message( grant_id, schedule_id, ) print(event) ``` ```Ruby require 'nylas' nylas = Nylas::Client.new( api_key: "<NYLAS_API_KEY>" ) messages, _ = nylas.messages.find_scheduled_messages( identifier: ENV["NYLAS_GRANT_ID"], schedule_id: "<SCHEDULE_ID>") messages.each {|message| puts message } ``` ```API curl --request GET \ --url 'https://api.us.nylas.com/v3/grants/<NYLAS_GRANT_ID>/messages/schedules/<SCHEDULE_ID>' \ --header 'Accept: application/json' \ --header 'Authorization: Bearer <NYLAS_API_KEY>' ``` ``` -------------------------------------------------------------------------------- /nylas-code-samples/Email/Drafts/read.md: -------------------------------------------------------------------------------- ```markdown ### Get all drafts `GET /drafts` → [`GET /v3/grants/<NYLAS_GRANT_ID>/drafts`](https://developer.nylas.com/docs/api/v3/ecc/#get-/v3/grants/-grant_id-/drafts) ```API curl --request GET \ --url 'https://api.us.nylas.com/v3/grants/<NYLAS_GRANT_ID>/drafts' \ --header 'Authorization: Bearer <NYLAS_API_KEY>' \ --header 'Content-Type: application/json' ``` ```Node import 'dotenv/config' import Nylas from 'nylas' const NylasConfig = { apiKey: process.env.NYLAS_API_KEY, apiUri: process.env.NYLAS_API_URI, } const nylas = new Nylas(NylasConfig) async function fetchDrafts() { try { const identifier = process.env.NYLAS_GRANT_ID const threads = await nylas.drafts.list({ identifier, }) console.log('Recent Drafts:', threads) } catch (error) { console.error('Error fetching drafts:', error) } } fetchDrafts() ``` ```Ruby require 'nylas' nylas = Nylas::Client.new( api_key: "<NYLAS_API_KEY>" ) drafts, _ = nylas.drafts.list(identifier: "<NYLAS_GRANT_ID>") drafts.each {|draft| puts "[#{Time.at(draft[:date]).strftime("%d/%m/%Y at %H:%M:%S")}] | \ #{draft[:id]} | \ #{draft[:subject]} | \ #{draft[:folders]}" } ``` ```Python from dotenv import load_dotenv load_dotenv() import os import sys from nylas import Client nylas = Client( os.environ.get('NYLAS_API_KEY'), os.environ.get('NYLAS_API_URI') ) grant_id = os.environ.get("NYLAS_GRANT_ID") drafts = nylas.drafts.list( grant_id, ) print(drafts) ``` ```Java import com.nylas.NylasClient; import com.nylas.models.*; import java.text.SimpleDateFormat; public class ListDraft { public static void main(String[] args) throws NylasSdkTimeoutError, NylasApiError { NylasClient nylas = new NylasClient.Builder("<NYLAS_API_KEY>").build(); ListResponse<Draft> drafts = nylas.drafts().list("<NYLAS_GRANT_ID>"); for (Draft draft : drafts.getData()){ String date = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new java.util.Date((draft.getDate() * 1000L))); System.out.printf("[ %s] | %s | %s | %s", date, draft.getId(), draft.getSubject(), draft.getFolders()); } } } ``` ### Get a specific draft `GET /drafts/<DRAFT_ID>` → [`GET /v3/grants/<NYLAS_GRANT_ID>/drafts/<DRAFT_ID>`](https://developer.nylas.com/docs/api/v3/ecc/#get-/v3/grants/-grant_id-/drafts/-draft_id-) ```API curl --request GET \ --url https://api.us.nylas.com/v3/grants/<NYLAS_GRANT_ID>/drafts/<DRAFT_ID> \ --header 'Authorization: Bearer <NYLAS_API_KEY>' \ --header 'Content-Type: application/json' ``` ```Node import 'dotenv/config' import Nylas from 'nylas' const NylasConfig = { apiKey: process.env.NYLAS_API_KEY, apiUri: process.env.NYLAS_API_URI, } const nylas = new Nylas(NylasConfig) async function fetchDraftById() { try { const events = await nylas.drafts.find({ identifier: process.env.NYLAS_GRANT_ID, draftId: process.env.DRAFT_ID, }) console.log('Events:', events) } catch (error) { console.error('Error fetching calendars:', error) } } fetchDraftById() ``` ```Ruby require 'dotenv/load' require 'nylas' nylas = Nylas::Client.new( api_key: "<NYLAS_API_KEY>" ) draft, _ = nylas.drafts.find(identifier: ENV["NYLAS_GRANT_ID"], draft_id: "<DRAFT_ID>") puts draft ``` ```Python from dotenv import load_dotenv load_dotenv() import os import sys from nylas import Client nylas = Client( os.environ.get('NYLAS_API_KEY'), os.environ.get('NYLAS_API_URI') ) grant_id = os.environ.get("NYLAS_GRANT_ID") draft_id = os.environ.get("DRAFT_ID") draft = nylas.drafts.find( grant_id, draft_id, ) print(draft) ``` ```Java import com.nylas.NylasClient; import com.nylas.models.*; public class ListDraft { public static void main(String[] args) throws NylasSdkTimeoutError, NylasApiError { NylasClient nylas = new NylasClient.Builder("<NYLAS_API_KEY>").build(); Response<Draft> draft = nylas.drafts().find("<NYLAS_GRANT_ID>", "<DRAFT_ID>"); assert draft.getData().getTo() != null; System.out.printf(" %s | %s | %s", draft.getData().getId(), draft.getData().getTo().get(0).getEmail(), draft.getData().getSubject()); } } ``` ``` -------------------------------------------------------------------------------- /nylas-code-samples/Calendar/Virtual-Calendars/index.md: -------------------------------------------------------------------------------- ```markdown Virtual Calendars are much the same in Nylas v3, except that virtual calendars can now have a `primary` calendar, and can have up to 10 total calendars per account, and [a few other improvements](#general-changes-to-virtual-calendars). Virtual calendars use the new v3 Nylas Calendar and Events APIs, and all Calendar-related endpoint changes in v3 API also apply to virtual calendars. To upgrade these APIs, use the Upgrade Calendar APIs instructions. ## Virtual calendar schema changes The schema for the underlying virtual account has changed. The client ID is now passed in the API request, the `scopes` and `name` fields have been removed, the `provider` is now `virtual-calendar`, and the `email` string is now inside the `settings`. ``` { //v2 virtual calendar account "client_id": "3", "provider": "nylas", "scopes": "calendar", "email": "virtual_account_unique_id", "name": "Virtual Calendar", "settings": {} } ``` ``` { //v3 virtual calendar account "provider": "virtual-calendar", "settings": { "email": "virtual_account_unique_id" //a human-readable ID } } ``` When you make new virtual accounts in the future, make sure the `email` string provides a clear description for the virtual account. ## Migrate virtual calendar data to v3 You can start by migrating your virtual calendar data to v3 to help you upgrade the rest of your project code. Start with a dev or test application so you don't disturb your production users. Migrated virtual calendar event and calendar object IDs don't change. You can use the same IDs to reference the same events and calendars in your v3 app. Migrated virtual accounts keep the `email` from the v2 version, but are converted to a grant. Use the grant ID for the virtual account's grant to locate its calendar and event data. When you're ready for your users to start switching to the v3 version, you can re-run the migration tool to catch up on any new or changed data since the initial sync. ### Changes to virtual calendar data during migration The migration tool looks at your v2 virtual calendar data, transforms it into the v3 format, and copies it to the v3 systems. You can expect the following changes: - When you migrate virtual calendar data using the [Nylas migration API](/docs/api/v3/migration/), Nylas saves the content of the `name` and `organizer` fields to the grant's metadata. If you need access to this metadata, contact Nylas Support. - Other metadata from v2 is not migrated to v3. - The v2 virtual account `name` and v2 event `organizer` are not supported in v3 Virtual Calendars. The v3 equivalent for the `organizer` field is the `email` string that you set when you created the virtual calendar. - The `created_at` and `updated_at` timestamps are be updated for all events and calendars during migration. - In v2, when a VC event is deleted, it was marked as `status=cancelled`, considered a soft delete. In v3, however, events are permanently deleted immediately. - The v2 recurrence timezone is now part of the `when` property of v3 events. Timezones are not available for all-day events in v3, so Nylas drops the timezone when converting v2 all-day events to v3 format. - The `read_only` field in v3 Virtual Calendars is always set to `null`. - Reminders are not supported in v3 Virtual Calendars and are not migrated. ## General changes to virtual calendars - Virtual calendars are no longer limited to one calendar per account (now called a grant). You can now have up to 10 calendars per grant, and you can still create as many grants as you need. You are still billed per grant. - You create new grants using Custom auth and the `virtual-calendar` provider. - In v3 new virtual accounts are created without any calendars so you can specify the `email` identifier and control which is set as `primary`. Create new virtual calendars for each grant using the `POST /v3/calendars` endpoint. - You must provide an identifier when you create the Grant for virtual calendars. Previously this was optional. This can be any arbitrary string, and no longer needs to be in email address format. - In v3 Nylas can set one calendar per account as the "primary" calendar, and you can use the word `primary` instead of a `calendar_id` in Calendar API calls. In virtual calendars, the `primary` calendar is the first calendar created for a grant. Once created, that first calendar cannot be deleted. ``` -------------------------------------------------------------------------------- /nylas-code-samples/Auth/AdminConsent-Bulk/index.md: -------------------------------------------------------------------------------- ```markdown You can use connector credentials for bulk authentication with Microsoft's Admin Consent flow. What are connector credentials, I hear you asking? They're new in v3, read on... #### Introducing connector credentials Nylas v3 includes "credentials", which are packages of information that you can use when authenticating to override default connector settings and auth methods. You might use this if you're using more than one Azure auth app to authenticate your end users. You can create credentials for alternate Azure auth apps, which include the app's `client_id` and `client_secret`, and any other connection settings. Nylas encrypts and stores this information securely in a credential record. You then refer to the record in Custom auth requests using the `credential_id`. This allows you to authenticate end users with the non-default Azure auth app. Connector credentials are linked to a specific connector in a specific Nylas application. If you created the connector credential for a specific Microsoft connector, you can't use the `credential_id` for a different connector, or with a different Nylas application. ### Bulk authentication with Microsoft Admin Consent You can use connector credentials for bulk authentication with Microsoft's "Admin consent" system. To do this, first [create an Azure auth app](https://developer.nylas.com/docs/dev-guide/provider-guides/microsoft/create-azure-app/), then open the app in the Azure Dashboard and configure it: 1. From the **Authentication** tab, add a platform and set its **redirect URI**. 2. Select **Certificates & secrets** and create a `client_secret`. Be sure to save it somewhere secure — the Dashboard only shows the value once! 3. Under **API permissions**, add all Microsoft Graph scopes that your project needs access to. - You don't need to select **Grant admin consent**. You add this later using a special Nylas authorization API call. 4. Select **Manifest** and add the following values: - `"accessTokenAcceptedVersion": 2` - `"signInAudience": "AzureADandPersonalMicrosoftAccount"` #### Microsoft Service Accounts Nylas v3 supports two versions of Microsoft's Service Accounts: - **Version 1.0**: The _older_ version, where every scope defined in your Azure auth app is required during the authorization step of the Admin Consent flow. The `tenant` definition is optional, and Nylas uses `"tenant": "common"` if it's not defined. - **Version 2.0**: The _new_ version, where scopes are defined for the Admin Consent flow only. All requested scopes must still be defined in your Azure auth app, and you must specify the exact `tenant` (v2.0 doesn't support `"tenant": "common"`). Nylas automatically determines which version of the Admin Consent flow to use. #### Prepare for Admin Consent flow Before you begin, [set up a Microsoft connector](https://developer.nylas.com/docs/dev-guide/provider-guides/microsoft/create-azure-app/#add-a-microsoft-connector-to-nylas). Then, prepare your Nylas application for the Microsoft Admin Consent flow. 1. Make a [`POST /v3/connectors/<PROVIDER>/creds` request](https://developer.nylas.com/docs/api/v3/admin/#post-/v3/connectors/-provider-/creds) to create a connector credential. The request must include the `client_id` and `client_secret` for your provider auth app, and can include a `tenant`. - If you don't define the `client_id` and `client_secret`, Nylas uses the credentials associated with your application's Microsoft connector. - If you define the `tenant`, Nylas attempts to use Microsoft's Admin Consent flow v2.0. 2. Nylas returns a JSON response with the `id` of the new connector credential. Be sure to save it somewhere secure. #### Make an Admin Consent flow request Now you can make a request to use the Microsoft Admin Consent flow with the Nylas APIs. 1. Make a [`GET /v3/connect/auth` request](https://developer.nylas.com/docs/api/v3/admin/#get-/v3/connect/auth) and set `response_type` to `adminconsent`, and `credential_id` to the ID of the connector credential you created. 2. Nylas redirects the Service Account to the redirect URI. The response URL contains `admin_consent:true` and the contents of the `state`, if defined. - If the flow isn't successful, Nylas returns a normal OAuth 2.0 error. 3. Make a [`POST /v3/connect/custom` request](https://developer.nylas.com/docs/api/v3/admin/#post-/v3/connect/custom) to create a grant for the Service Account. 4. Nylas returns a JSON response containing the grant's information. ``` -------------------------------------------------------------------------------- /nylas-code-samples/Email/Threads/write.md: -------------------------------------------------------------------------------- ```markdown ### Update a specific thread `PUT /threads/<THREAD_ID>` → [`PUT /v3/grants/<NYLAS_GRANT_ID>/threads/<THREAD_ID>`](https://developer.nylas.com/docs/api/v3/ecc/#put-/v3/grants/-grant_id-/threads/-thread_id-) ```Node import 'dotenv/config' import Nylas from 'nylas' const NylasConfig = { apiKey: process.env.NYLAS_API_KEY, apiUri: process.env.NYLAS_API_URI, } const nylas = new Nylas(NylasConfig) async function updateThread() { try { const calendar = await nylas.threads.update({ identifier: process.env.NYLAS_GRANT_ID, threadId: process.env.THREAD_ID, requestBody: { starred: true } }) console.log('Updated Thread:', calendar) } catch (error) { console.error('Error to update thread:', error) } } updateThread() ``` ```Java import com.nylas.NylasClient; import com.nylas.models.*; import com.nylas.models.Thread; public class UpdateThread { public static void main(String[] args) throws NylasSdkTimeoutError, NylasApiError { NylasClient nylas = new NylasClient.Builder("<NYLAS_API_KEY>").build(); UpdateThreadRequest requestBody = new UpdateThreadRequest. Builder(). unread(true). starred(true). build(); Response<Thread> draft = nylas.threads().update("<NYLAS_GRANT_ID>", "<THREAD_ID>", requestBody); System.out.printf("%s%s%s%n", draft.getData().getId(), draft.getData().getUnread(), draft.getData().getStarred() ); } } ``` ```Python from dotenv import load_dotenv load_dotenv() import os import sys from nylas import Client nylas = Client( os.environ.get('NYLAS_API_KEY'), os.environ.get('NYLAS_API_URI') ) grant_id = os.environ.get("NYLAS_GRANT_ID") thread = nylas.threads.update( grant_id, thread_id=os.environ.get("THREAD_ID"), request_body={ "starred": True } ) print(thread) ``` ```Ruby require 'nylas' nylas = Nylas::Client.new( api_key: "<NYLAS_API_KEY>" ) request_body = { unread: true, starred: true } thread, _ = nylas.threads.update(identifier: "<NYLAS_GRANT_ID>", thread_id: "<THREAD_ID>", request_body: request_body) puts thread ``` ```API curl --request PUT \ --url https://api.us.nylas.com/v3/grants/<NYLAS_GRANT_ID>/threads/<THREAD_ID>\ --header 'Accept: application/json' \ --header 'Authorization: Bearer <NYLAS_API_KEY>' \ --header 'Content-Type: application/json' \ --data '{ "unread": true, "starred": false, "folders": [ "<FOLDER_ID>", "<FOLDER_ID>" ] }' ``` ### Delete a thread [`DELETE /v3/grants/<NYLAS_GRANT_ID>/threads/<THREAD_ID>`](https://developer.nylas.com/docs/api/v3/ecc/#delete-/v3/grants/-grant_id-/threads/-thread_id-) ```Node import 'dotenv/config' import Nylas from 'nylas' const NylasConfig = { apiKey: process.env.NYLAS_API_KEY, apiUri: process.env.NYLAS_API_URI, } const nylas = new Nylas(NylasConfig) const identifier = process.env.NYLAS_GRANT_ID const threadId = process.env.THREAD_ID const deleteThread = async () => { try { await nylas.threads.destroy({ identifier, threadId }) console.log(`Thread with ID ${threadId} deleted successfully.`) } catch (error) { console.error(`Error deleting thread with ID ${threadId}:`, error) } } deleteThread() ``` ```Java import com.nylas.NylasClient; import com.nylas.models.*; import com.nylas.models.Thread; public class ReturnThread { public static void main(String[] args) throws NylasSdkTimeoutError, NylasApiError { NylasClient nylas = new NylasClient.Builder("<NYLAS_API_KEY>").build(); DeleteResponse thread = nylas.threads().destroy("<NYLAS_GRANT_ID>", "<THREAD_ID>"); System.out.println(thread); } } ``` ```Python from dotenv import load_dotenv load_dotenv() import os import sys from nylas import Client nylas = Client( os.environ.get('NYLAS_API_KEY'), os.environ.get('NYLAS_API_URI') ) grant_id = os.environ.get("NYLAS_GRANT_ID") thread_id = os.environ.get("THREAD_ID") request = nylas.threads.destroy( grant_id, thread_id, ) print(request) ``` ```Ruby require 'nylas' nylas = Nylas::Client.new( api_key: "<NYLAS_API_KEY>" ) thread, _ = nylas.threads.destroy(identifier: "<NYLAS_GRANT_ID>", thread_id: "<THREAD_ID>") puts thread ``` ```API curl --request DELETE \ --url https://api.us.nylas.com/v3/grants/<NYLAS_GRANT_ID>/threads/<THREAD_ID> \ --header 'Accept: application/json' \ --header 'Authorization: Bearer <NYLAS_API_KEY>' \ --header 'Content-Type: application/json' ``` ``` -------------------------------------------------------------------------------- /nylas-code-samples/Auth/Native-Custom/index.md: -------------------------------------------------------------------------------- ```markdown Nylas v3 preserves Native authentication ("Bring your own token") almost unchanged, but renames it "Custom authentication". If you already have a refresh token (or credentials, if using IMAP) for your end users from your own authentication implementation, you can use it with the Nylas APIs to create a grant and get the `grant_id`, which you then use in requests to the provider. ### Upgrading Native authentication to Custom authentication If you used Native auth in Nylas v2.x, the only changes are to the Nylas API URLs, the requirement that you create a connector (previously called an "integration") for your application, and the change from token authentication to bearer-token authentication. The v3 requests now go through a connector, which can supply some of the provider details. This makes your [v3 Custom auth requests](https://developer.nylas.com/docs/api/v3/admin/#post-/v3/connect/custom) much simpler: you now pass the provider and request token, and can include scopes overrides and a state. See [Creating grants with Custom authentication](https://developer.nylas.com/docs/v3/auth/custom/) for more details. ### Migrated endpoints - Native auth → Custom auth: `POST /connect/token` → [`POST /v3/connect/custom`](https://developer.nylas.com/docs/api/v3/admin/#post-/v3/connect/custom) ### Removed Connect endpoints - Native auth - Exchange Token: `POST /connect/token` ### Create a grant using a Custom authentication flow `POST /auth` → [`POST /v3/connect/custom`](https://developer.nylas.com/docs/api/v3/admin/#post-/v3/connect/custom) ```Node import 'dotenv/config'; import Nylas from 'nylas'; const NylasConfig = { apiKey: process.env.NYLAS_API_KEY as string, apiUri: process.env.NYLAS_API_URI as string, }; // gpt: does not initialize Nylas SDK, was using string const nylas = new Nylas(NylasConfig); async function customAuthentication() { const auth = await nylas.auth.customAuthentication({ requestBody: { provider: "<PROVIDER>", settings: { username: "<USERNAME>", password: "<PASSWORD>" }, state: "<STATE>", scope: ["email.read_only", "calendar.read_only", "contacts.read_only"], } }) console.log({ auth }); } await customAuthentication(); ``` ```Java package org.example; import com.nylas.NylasClient; import com.nylas.models.*; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; public class Main { public static void main(String[] args) throws NylasSdkTimeoutError, NylasApiError { NylasClient nylas = new NylasClient.Builder(<NYLAS_API_KEY>).build(); AuthProvider provider = AuthProvider.ICLOUD; Map<String, String> settings = new HashMap<String, String>(); settings.put("username","<USERNAME>"); settings.put("password","<PASSWORD>"); List<String> scope = new ArrayList<String>(); scope.add("email.read_only"); scope.add("calendar.read_only"); scope.add("contacts.read_only"); CreateGrantRequest request_body = new CreateGrantRequest(provider,settings, "<STATE>", scope); Response<Grant> auth = nylas.auth().customAuthentication(request_body); System.out.println(auth); } } ``` ```Python import os import sys from nylas import Client from nylas.models.grants import CreateGrantRequest from nylas.models.auth import Provider nylas = Client( "<NYLAS_API_KEY>", "<NYLAS_API_URI>" ) request_body = CreateGrantRequest( { "provider": "<PROVIDER>", "settings": {"username": "<USERNAME>", "password" : "<PASSWORD>"}, "scope": ["email.read_only", "calendar.read_only", "contacts.read_only"], "state": "<STATE>" } ) auth = nylas.auth.custom_authentication(request_body) print(auth) ``` ```Ruby # Load gems require 'dotenv/load' require 'nylas' # Initialize Nylas client nylas = Nylas::Client.new( api_key: "<NYLAS_API_KEY>" ) # Request body request_body = { provider: '<PROVIDER>', settings: {'username': '<USERNAME>', 'password': '<PASSWORD>'}, scope: 'email.read_only,calendar.read_only,contacts.read_only', state: '<STATE>' } # Call Custom Authentication auth = nylas.auth.custom_authentication(request_body) puts auth ``` ```API curl --request POST \ --url "https://api.us.nylas.com/v3/connect/custom" \ --header 'Accept: application/json' \ --header 'Authorization: Bearer <NYLAS_API_KEY>' \ -H "accept: application/json"\ -H "content-type: application/json" \ -d '{"provider":"<PROVIDER>","settings":{"username":"<USERNAME>","password":"<PASSWORD>"}, "scope":"email.read_only,calendar.read_only,contacts.read_only"}' ``` ``` -------------------------------------------------------------------------------- /nylas-code-samples/Email/Threads/read.md: -------------------------------------------------------------------------------- ```markdown ### Get all threads `GET /threads` → [`GET /v3/grants/<NYLAS_GRANT_ID>/threads`](https://developer.nylas.com/docs/api/v3/ecc/#get-/v3/grants/-grant_id-/threads) ```Node import 'dotenv/config' import Nylas from 'nylas' const NylasConfig = { apiKey: process.env.NYLAS_API_KEY, apiUri: process.env.NYLAS_API_URI, } const nylas = new Nylas(NylasConfig) async function fetchRecentThreads() { try { const identifier = process.env.NYLAS_GRANT_ID const threads = await nylas.threads.list({ identifier, queryParams: { limit: 5, } }) console.log('Recent Threads:', threads) } catch (error) { console.error('Error fetching threads:', error) } } fetchRecentThreads() ``` ```Java import com.nylas.NylasClient; import com.nylas.models.*; import com.nylas.models.Thread; public class ReadThreadParameters { public static void main(String[] args) throws NylasSdkTimeoutError, NylasApiError { NylasClient nylas = new NylasClient.Builder("<NYLAS_API_KEY>").build(); ListThreadsQueryParams queryParams = new ListThreadsQueryParams.Builder().limit(5).build(); ListResponse<Thread> threads = nylas.threads().list("<NYLAS_GRANT_ID>", queryParams); int index = 0; for(Thread thread : threads.getData()){ System.out.printf("%s ", index); List<EmailName> participants = thread.getParticipants(); assert participants != null; for(EmailName participant : participants){ System.out.printf(" Subject: %s | Participant: %s | Email: %s%n", thread.getSubject(), participant.getName(), participant.getEmail()); } index++; } } } ``` ```Python from dotenv import load_dotenv load_dotenv() import os import sys from nylas import Client nylas = Client( os.environ.get('NYLAS_API_KEY'), os.environ.get('NYLAS_API_URI') ) grant_id = os.environ.get("NYLAS_GRANT_ID") threads = nylas.threads.list( grant_id, query_params={ "limit": 5 } ) print(threads) ``` ```Ruby require 'nylas' nylas = Nylas::Client.new(api_key: "<NYLAS_API_KEY>") query_params = { limit: 5 } threads, _ = nylas.threads.list(identifier: "<NYLAS_GRANT_ID>", query_params: query_params) threads.map.with_index { |thread, i| puts("Thread #{i}") participants = thread[:participants] participants.each{ |participant| puts( "Subject: #{thread[:subject]} | "\ "Participant: #{participant[:name]} | "\ "Email: #{participant[:email]}" ) } } ``` ```API curl --request GET \ --url "https://api.us.nylas.com/v3/grants/<NYLAS_GRANT_ID>/threads?limit=5" \ --header 'Accept: application/json' \ --header "Authorization: Bearer <NYLAS_API_KEY>' \ --header 'Content-Type: application/json' ``` ### Get a specific thread `GET /threads/<THREAD_ID>` → [`GET /v3/grants/<NYLAS_GRANT_ID>/threads/<THREAD_ID>`](https://developer.nylas.com/docs/api/v3/ecc/#get-/v3/grants/-grant_id-/threads/-thread_id-) ```Node import 'dotenv/config' import Nylas from 'nylas' const NylasConfig = { apiKey: process.env.NYLAS_API_KEY, apiUri: process.env.NYLAS_API_URI, } const nylas = new Nylas(NylasConfig) async function fetchThreadById(): Promise<void> { try { const events = await nylas.threads.find({ identifier: process.env.NYLAS_GRANT_ID, threadId: process.env.THREAD_ID, }) console.log('Events:', events) } catch (error) { console.error('Error fetching calendars:', error) } } fetchThreadById() ``` ```Java import com.nylas.NylasClient; import com.nylas.models.*; import com.nylas.models.Thread; public class ReturnThread { public static void main(String[] args) throws NylasSdkTimeoutError, NylasApiError { NylasClient nylas = new NylasClient.Builder("<NYLAS_API_KEY>").build(); Response<Thread> thread = nylas.threads().find("<NYLAS_GRANT_ID>", "<THREAD_ID>"); System.out.println(thread); } } ``` ```Python from dotenv import load_dotenv load_dotenv() import os import sys from nylas import Client nylas = Client( os.environ.get('NYLAS_API_KEY'), os.environ.get('NYLAS_API_URI') ) grant_id = os.environ.get("NYLAS_GRANT_ID") thread_id = os.environ.get("THREAD_ID") thread = nylas.threads.find( grant_id, thread_id, ) print(thread) ``` ```Ruby require 'nylas' nylas = Nylas::Client.new( api_key: "<NYLAS_API_KEY>" ) thread, _ = nylas.threads.find(identifier: "<NYLAS_GRANT_ID>", thread_id: "<THREAD_ID>") participants = thread[:participants] participants.each{ |participant| puts("Id: #{thread[:id]} | "\ "Subject: #{thread[:subject]} | "\ "Participant: #{participant[:name]} | "\ "Email: #{participant[:email]}" ) } ``` ```API curl --request GET \ --url https://api.us.nylas.com/v3/grants/<NYLAS_GRANT_ID>/threads/<THREAD_ID> \ --header 'Accept: application/json' \ --header 'Authorization: Bearer <NYLAS_API_KEY>' \ --header 'Content-Type: application/json' ``` ``` -------------------------------------------------------------------------------- /nylas-code-samples/Email/Files-Attachments/read.md: -------------------------------------------------------------------------------- ```markdown ### Get metadata for a specific attachment `GET /files/<FILE_ID>` → [`GET /v3/grants/<NYLAS_GRANT_ID>/attachments/<ATTACHMENT_ID>`](https://developer.nylas.com/docs/api/v3/ecc/#get-/v3/grants/-grant_id-/attachments/-attachment_id-) ```Node import 'dotenv/config' import Nylas from 'nylas' const NylasConfig = { apiKey: process.env.NYLAS_API_KEY, apiUri: process.env.NYLAS_API_URI, } const nylas = new Nylas(NylasConfig) async function fetchAttachmentById() { try { const attachment = await nylas.attachments.find({ identifier: process.env.NYLAS_GRANT_ID, attachmentId: process.env.ATTACHMENT_ID, queryParams: { messageId: process.env.MESSAGE_ID, } }) console.log('Attachment:', attachment) } catch (error) { console.error('Error fetching attachment:', error) } } fetchAttachmentById() ``` ```Java import com.nylas.NylasClient; import com.nylas.models.*; public class attachment { public static void main(String[] args) throws NylasSdkTimeoutError, NylasApiError, NylasOAuthError { NylasClient nylas = new NylasClient.Builder("NYLAS_API_KEY").build(); FindAttachmentQueryParams queryParams = new FindAttachmentQueryParams("<MESSAGE_ID>"); Attachment attachment = nylas.attachments().find("<NYLAS_GRANT_ID>", "<ATTACHMENT_ID>", queryParams).getData(); System.out.println(attachment); } } ``` ```Python from dotenv import load_dotenv load_dotenv() import os import sys from nylas import Client nylas = Client( os.environ.get('NYLAS_API_KEY'), os.environ.get('NYLAS_API_URI') ) grant_id = os.environ.get("NYLAS_GRANT_ID") folder_id = os.environ.get("FOLDER_ID") attachment_id = os.environ.get("ATTACHMENT_ID") attachment = nylas.attachments.find( grant_id, attachment_id, query_params= { "message_id": os.environ.get("MESSAGE_ID"), } ) print(attachment) ``` ```Ruby require 'dotenv/load' require 'nylas' nylas = Nylas::Client.new( api_key: "<NYLAS_API_KEY>" ) query_params = { message_id: "<MESSAGE_ID>" } attachment = nylas.attachments.find(identifier: "<NYLAS_GRANT_ID>", attachment_id: "<ATTACHMENT_ID>", query_params: query_params) puts attachment ``` ```API curl --request GET \ --url https://api.us.nylas.com/v3/grants/<NYLAS_GRANT_ID>/attachments/<ATTACHMENT_ID>?message_id=<MESSAGE_ID> \ --header 'Accept: application/json' \ --header 'Authorization: Bearer <NYLAS_API_KEY>' ``` ### Download a specific attachment `GET /files/<FILE_ID>/download` → [`GET /v3/grants/<NYLAS_GRANT_ID>/attachments/<ATTACHMENT_ID>/download`](https://developer.nylas.com/docs/api/v3/ecc/#get-/v3/grants/-grant_id-/attachments/-attachment_id-/download) ```Node import 'dotenv/config' import fs from 'fs' import Nylas from 'nylas' const NylasConfig = { apiKey: process.env.NYLAS_API_KEY, apiUri: process.env.NYLAS_API_URI, } const nylas = new Nylas(NylasConfig) async function downloadAttachment() { try { const attachmentResponse = await nylas.attachments.download({ identifier: process.env.NYLAS_GRANT_ID, attachmentId: process.env.ATTACHMENT_ID, queryParams: { messageId: process.env.MESSAGE_ID, } }) const fileName = 'attachment' const fileStream = fs.createWriteStream(fileName) attachmentResponse.pipe(fileStream) fileStream.on('finish', () => { console.log(`File saved as ${fileName}`) }) } catch (error) { console.error('Error fetching attachment:', error) } } downloadAttachment() ``` ```Java import com.nylas.NylasClient; import com.nylas.models.*; import okhttp3.ResponseBody; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; public class attachment_download { public static void main(String[] args) throws NylasSdkTimeoutError, NylasOAuthError, IOException { NylasClient nylas = new NylasClient.Builder("<NYLAS_API_KEY>").build(); FindAttachmentQueryParams queryParams = new FindAttachmentQueryParams("<MESSAGE_ID>"); ResponseBody attachment = nylas.attachments().download("<NYLAS_GRANT_ID>", "<ATTACHMENT_ID>", queryParams); try { FileOutputStream out = new FileOutputStream("src/main/resources/image.png"); out.write(attachment.bytes()); out.close(); } catch (FileNotFoundException e) { System.out.println("File not found"); } } } ``` ```Python from dotenv import load_dotenv load_dotenv() import os from nylas import Client nylas = Client( os.environ.get('NYLAS_API_KEY'), os.environ.get('NYLAS_API_URI') ) grant_id = os.environ.get("NYLAS_GRANT_ID") attachment_id = os.environ.get("ATTACHMENT_ID") attachment = nylas.attachments.download( grant_id, attachment_id, query_params= { "message_id": os.environ.get("MESSAGE_ID"), } ) with open("attachment", 'wb') as f: f.write(attachment.content) ``` ```Ruby require 'dotenv/load' require 'nylas' nylas = Nylas::Client.new( api_key: "<NYLAS_API_KEY>" ) query_params = { message_id: "<MESSAGE_ID>" } attachment = nylas.attachments.download(identifier: "<NYLAS_GRANT_ID">, attachment_id: "<ATTACHMENT_ID>", query_params: query_params) File.open("./image.png", "wb") do |file| file.write(attachment) end ``` ```API curl --request GET \ --url https://api.us.nylas.com/v3/grants/<NYLAS_GRANT_ID>/attachments/<ATTACHMENT_ID>/download?message_id=<MESSAGE_ID> \ --header 'Authorization: Bearer <NYLAS_API_KEY>' ``` ``` -------------------------------------------------------------------------------- /nylas-code-samples/Email/Messages/read.md: -------------------------------------------------------------------------------- ```markdown ### Get all messages `GET /messages` → [`GET /v3/grants/<NYLAS_GRANT_ID>/messages`](https://developer.nylas.com/docs/api/v3/ecc/#get-/v3/grants/-grant_id-/messages) ```Node app.get("/nylas/recent-emails", async (req, res) => { try { const identifier = process.env.USER_GRANT_ID; const messages = await nylas.messages.list({ identifier, queryParams: { limit: 5, }, }); res.json(messages); } catch (error) { console.error("Error fetching emails:", error); } }); ``` ```Java import com.nylas.NylasClient; import com.nylas.models.*; import java.text.SimpleDateFormat; public class ReadEmail { public static void main(String[] args) throws NylasSdkTimeoutError, NylasApiError { NylasClient nylas = new NylasClient.Builder("<NYLAS_API_KEY>").build(); ListResponse<Message> message = nylas.messages().list(dotenv.get("NYLAS_GRANT_ID")); for(Message email : message.getData()){ String date = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"). format(new java.util.Date((email.getDate() == null ? 1 : 1000L))); System.out.println(email.getId() + "[" + date + "] | " + email.getSubject() + " | " + email.getFolders()); } } } ``` ```Python from dotenv import load_dotenv load_dotenv() import os import sys from nylas import Client nylas = Client( os.environ.get('NYLAS_API_KEY'), os.environ.get('NYLAS_API_URI') ) grant_id = os.environ.get("NYLAS_GRANT_ID") messages = nylas.messages.list( grant_id, query_params={ "limit": 5 } ) print(messages) ``` ```Ruby require 'nylas' # Initialize Nylas client nylas = Nylas::Client.new( api_key: "<NYLAS_API_KEY>" ) messages, _ = nylas.messages.list(identifier: "<NYLAS_GRANT_ID>") messages.each {|message| puts "[#{Time.at(message[:date]).strftime("%d/%m/%Y at %H:%M:%S")}] | \ #{message[:id]} | \ #{message[:subject]} | \ #{message[:folders]}" } ``` ```API curl --request GET \ --url "https://api.us.nylas.com/v3/grants/<NYLAS_GRANT_ID>/messages?limit=5" \ --header 'Accept: application/json' \ --header 'Authorization: Bearer <NYLAS_API_KEY>' \ --header 'Content-Type: application/json' ``` ### Get a specific message `GET /messages/<MESSAGE_ID>` → `GET /v3/grants/<NYLAS_GRANT_ID>/messages/<MESSAGE_ID>` ```Node import 'dotenv/config' import Nylas from 'nylas' const NylasConfig = { apiKey: process.env.NYLAS_API_KEY, apiUri: process.env.NYLAS_API_URI, } const nylas = new Nylas(NylasConfig) async function fetchMessageById() { try { const message = await nylas.messages.find({ identifier: process.env.NYLAS_GRANT_ID, messageId: process.env.MESSAGE_ID, }) console.log('message:', message) } catch (error) { console.error('Error fetching message:', error) } } fetchMessageById() ``` ```Java import com.nylas.NylasClient; import com.nylas.models.*; public class ReturnMessage { public static void main(String[] args) throws NylasSdkTimeoutError, NylasApiError { NylasClient nylas = new NylasClient.Builder("<NYLAS_API_KEY>").build(); Response<Message> message = nylas.messages().find("<NYLAS_GRANT_ID>", "<MESSAGE_ID>"); System.out.println(message); } } ``` ```Python from dotenv import load_dotenv load_dotenv() import os import sys from nylas import Client nylas = Client( os.environ.get('NYLAS_API_KEY'), os.environ.get('NYLAS_API_URI') ) grant_id = os.environ.get("NYLAS_GRANT_ID") message_id = os.environ.get("MESSAGE_ID") message = nylas.messages.find( grant_id, message_id, ) print(message) ``` ```Ruby require 'nylas' nylas = Nylas::Client.new( api_key: "<NYLAS_API_KEY>" ) message, _ = nylas.messages.find(identifier: ENV["NYLAS_GRANT_ID"], message_id: "<MESSAGE_ID>") puts message ``` ```API curl --request GET \ --url https://api.us.nylas.com/v3/grants/<NYLAS_GRANT_ID>/messages/<MESSAGE_ID> \ --header 'Accept: application/json' \ --header 'Authorization: Bearer <NYLAS_API_KEY>' \ --header 'Content-Type: application/json' ``` ### Clean a message You can use the [`PUT /v3/grants/<NYLAS_GRANT_ID>/messages/clean` endpoint](https://developer.nylas.com/docs/api/v3/ecc/#put-/v3/grants/-grant_id-/messages/clean) to remove extra information from an email message. ```API curl --location --request PUT 'https://api.us.nylas.com/v3/grants/NYLAS_GRANT_ID/messages/clean' \ --header 'Content-Type: application/json' \ --header 'Accept: application/json' \ --header 'Authorization: Bearer <NYLAS_API_KEY_OR_ACCESS_TOKEN>' \ --data '{ "message_id": ["18df98cadcc8534a"], "ignore_links": false, "ignore_images": false, "images_as_markdown": true, "ignore_tables": true, "remove_conclusion_phrases": true }' ``` ```Java import com.nylas.NylasClient; import com.nylas.models.*; import java.util.ArrayList; import java.util.Arrays; import java.util.List; public class Clean_Message { public static void main(String[] args) throws NylasSdkTimeoutError, NylasApiError { NylasClient nylas = new NylasClient.Builder("<NYLAS_API_KEY>").build(); List<String> messagesId = List.of("<MESSAGE_ID>"); CleanMessagesRequest requestBody = new CleanMessagesRequest.Builder(messagesId). ignoreImages(true). ignoreLinks(true). ignoreTables(true). imagesAsMarkdown(true). removeConclusionPhrases(true). build(); ListResponse<CleanMessagesResponse> clean = nylas.messages().cleanMessages("<NYLAS_GRANT_ID>", requestBody); System.out.println(clean.getData()); } } ``` ``` -------------------------------------------------------------------------------- /nylas-code-samples/Contacts/read.md: -------------------------------------------------------------------------------- ```markdown ### Get all contacts `GET /contacts` → [`GET /v3/grants/<NYLAS_GRANT_ID>/contacts`](https://developer.nylas.com/docs/api/v3/ecc/#get-/v3/grants/-grant_id-/contacts) ```API curl --request GET \ --url https://api.us.nylas.com/v3/grants/<NYLAS_GRANT_ID>/contacts \ --header 'Accept: application/json' \ --header 'Authorization: Bearer <NYLAS_API_KEY>' \ --header 'Content-Type: application/json' ``` ```Node import 'dotenv/config' import Nylas from 'nylas' const NylasConfig = { apiKey: process.env.NYLAS_API_KEY, apiUri: process.env.NYLAS_API_URI, } const nylas = new Nylas(NylasConfig) async function fetchContacts() { try { const identifier = process.env.NYLAS_GRANT_ID const contacts = await nylas.contacts.list({ identifier, queryParams: {}, }) console.log('Recent Contacts:', contacts) } catch (error) { console.error('Error fetching drafts:', error) } } fetchContacts() ``` ```Ruby require 'nylas' nylas = Nylas::Client.new(api_key: "<NYLAS_API_KEY>") contacts, _ = nylas.contacts.list(identifier: ENV["NYLAS_GRANT_ID"]) contacts.each {|contact| puts "Name: #{contact[:given_name]} #{contact[:surname]} | " \ "Email: #{contact[:emails][0][:email]} | ID: #{contact[:id]}" } ``` ```Python from dotenv import load_dotenv load_dotenv() import os import sys from nylas import Client nylas = Client( os.environ.get('NYLAS_API_KEY'), os.environ.get('NYLAS_API_URI') ) grant_id = os.environ.get("NYLAS_GRANT_ID") contacts = nylas.contacts.list( grant_id, ) print(contacts) ``` ```Java import com.nylas.NylasClient; import com.nylas.models.*; public class ReadAllContacts { public static void main(String[] args) throws NylasSdkTimeoutError, NylasApiError { NylasClient nylas = new NylasClient.Builder("<NYLAS_API_KEY>").build(); ListResponse<Contact> contacts = nylas.contacts().list("<NYLAS_GRANT_ID>"); for(Contact contact : contacts.getData()) { System.out.println(contact); System.out.println("\n"); } } } ``` ### Get a specific contact `GET /contacts/<CONTACT_ID>` → [`GET /v3/grants/<NYLAS_GRANT_ID>/contacts/<CONTACT_ID>`](https://developer.nylas.com/docs/api/v3/ecc/#get-/v3/grants/-grant_id-/contacts/-contact_id-) ```API curl --request GET \ --url https://api.us.nylas.com/v3/grants/<NYLAS_GRANT_ID>/contacts/<CONTACT_ID> \ --header 'Accept: application/json' \ --header 'Authorization: Bearer <NYLAS_API_KEY>' \ --header 'Content-Type: application/json' ``` ```Node import 'dotenv/config' import Nylas from 'nylas' const NylasConfig = { apiKey: process.env.NYLAS_API_KEY, apiUri: process.env.NYLAS_API_URI, } const nylas = new Nylas(NylasConfig) async function fetchContactById() { try { const contact = await nylas.contacts.find({ identifier: process.env.NYLAS_GRANT_ID, contactId: process.env.CONTACT_ID, queryParams: {}, }) console.log('contact:', contact) } catch (error) { console.error('Error fetching contact:', error) } } fetchContactById() ``` ```Ruby require 'nylas' nylas = Nylas::Client.new(api_key: "<NYLAS_API_KEY>") contact, _ = nylas.contacts.find(identifier: "<NYLAS_GRANT_ID>", contact_id: "<CONTACT_ID>") puts contact ``` ```Python from dotenv import load_dotenv load_dotenv() import os import sys from nylas import Client nylas = Client( os.environ.get('NYLAS_API_KEY'), os.environ.get('NYLAS_API_URI') ) grant_id = os.environ.get("NYLAS_GRANT_ID") contact_id = os.environ.get("CONTACT_ID") contact = nylas.contacts.find( grant_id, contact_id, ) print(contact) ``` ```Java import com.nylas.NylasClient; import com.nylas.models.*; public class ReturnAContact { public static void main(String[] args) throws NylasSdkTimeoutError, NylasApiError { NylasClient nylas = new NylasClient.Builder("<NYLAS_API_KEY>").build(); Response<Contact> contact = nylas.contacts().find("<NYLAS_GRANT_ID>", "<CONTACT_ID>"); System.out.println(contact); } } ``` ### Return all contact groups `GET /groups` → [`GET /v3/grants/<NYLAS_GRANT_ID>/contacts/groups`](https://developer.nylas.com/docs/api/v3/ecc/#get-/v3/grants/-grant_id-/contacts/groups) ```API curl --request GET \ --url https://api.us.nylas.com/v3/grants/<NYLAS_GRANT_ID>/contacts/groups \ --header 'Accept: application/json' \ --header 'Authorization: Bearer <NYLAS_API_KEY>' \ --header 'Content-Type: application/json' ``` ```Node import 'dotenv/config' import Nylas from 'nylas' const NylasConfig = { apiKey: process.env.NYLAS_API_KEY, apiUri: process.env.NYLAS_API_URI, } const nylas = new Nylas(NylasConfig) async function fetchContactGroups() { try { const identifier = process.env.NYLAS_GRANT_ID const contactGroups = await nylas.contacts.groups({ identifier, }) console.log('Contacts Groups:', contactGroups) } catch (error) { console.error('Error fetching contact groups:', error) } } fetchContactGroups() ``` ```Ruby require 'nylas' nylas = Nylas::Client.new(api_key: "<NYLAS_API_KEY>") groups = nylas.contacts.list_groups(identifier: "<NYLAS_GRANT_ID>") puts groups ``` ```Python from dotenv import load_dotenv load_dotenv() import os import sys from nylas import Client nylas = Client( os.environ.get('NYLAS_API_KEY'), os.environ.get('NYLAS_API_URI') ) grant_id = os.environ.get("NYLAS_GRANT_ID") contact_groups = nylas.contacts.list_groups( grant_id, ) print(contact_groups) ``` ```Java import com.nylas.NylasClient; import com.nylas.models.*; public class ReadContactGroups { public static void main(String[] args) throws NylasSdkTimeoutError, NylasApiError { NylasClient nylas = new NylasClient.Builder("<NYLAS_API_KEY>").build(); ListResponse<ContactGroup> groups = nylas.contacts().listGroups("<NYLAS_GRANT_ID>"); System.out.println(groups); } } ``` ``` -------------------------------------------------------------------------------- /nylas-code-samples/Calendar/Calendars/read.md: -------------------------------------------------------------------------------- ```markdown ### Get all calendars GET /calendars → /v3/grants/{grant_id}/calendars ```Node import 'dotenv/config' import Nylas from 'nylas' const NylasConfig = { apiKey: process.env.NYLAS_API_KEY, apiUri: process.env.NYLAS_API_URI, } const nylas = new Nylas(NylasConfig); async function fetchFiveAvailableCalendars() { try { const calendars = await nylas.calendars.list({ identifier: process.env.NYLAS_GRANT_ID, limit: 5 }) console.log('Available Calendars:', calendars); } catch (error) { console.error('Error fetching calendars:', error) } } fetchFiveAvailableCalendars() ``` ```Java import com.nylas.NylasClient; import com.nylas.models.*; import java.util.List; import java.util.Map; public class ReturnCalendars { public static void main(String[] args) throws NylasSdkTimeoutError, NylasApiError { NylasClient nylas = new NylasClient.Builder("<NYLAS_API_KEY>").build(); ListCalendersQueryParams listCalendersQueryParams = new ListCalendersQueryParams(); ListCalendersQueryParams returnFiveCalendars = new ListCalendersQueryParams. Builder().limit(5).build(); List<Calendar> calendars = nylas.calendars(). list("<NYLAS_GRANT_ID>", listCalendersQueryParams).getData(); for (Calendar calendar : calendars){ System.out.println("Id: " + calendar.getId() + " | Name: " + calendar.getName() + " | Description: " + calendar.getDescription() + " | Is Read Only?: " + calendar.getReadOnly() + " | Metadata: " + calendar.getMetadata()); } ListCalendersQueryParams CalendarsMetadata = new ListCalendersQueryParams. Builder(). metadataPair( Map.of("key1", "This is my metadata") ). build(); List<Calendar> metaCalendars = nylas.calendars(). list("<NYLAS_GRANT_ID>", CalendarsMetadata). getData(); System.out.println(); System.out.println(metaCalendars); } } ``` ```Python from dotenv import load_dotenv load_dotenv() import os import sys from nylas import Client nylas = Client( os.environ.get('NYLAS_API_KEY'), os.environ.get('NYLAS_API_URI') ) grant_id = os.environ.get("NYLAS_GRANT_ID") calendars = nylas.calendars.list(grant_id) print(calendars) ``` ```Ruby # Load gems require 'nylas' # Initialize Nylas client nylas = Nylas::Client.new( api_key: "<NYLAS_API_KEY>" ) # Build the query without parameters listCalendersQueryParams = {} # Build the query with parameters returnFiveCalendars = { limit: 5 } # Get a list of calendars calendars, _request_ids = nylas.calendars.list(identifier: "<NYLAS_GRANT_ID>", query_params: listCalendersQueryParams) # Loop the calendars calendars.each {|calendar| puts("Name: #{calendar[:name]} | " \ "Description: #{calendar[:description]} | " \ "Is Read Only?: #{calendar[:read_only]} | " \ "Metadata: #{calendar[:metadata]}") } # Build the event parameters with metadata CalendarsMetadata = { metadata_pair: {"key1":"This is my metadata"} } # Get a list of calendars calendars, _request_ids = nylas.calendars.list(identifier: ENV["NYLAS_GRANT_ID"], query_params: CalendarsMetadata) puts "" calendars.each {|calendar| puts calendar } ``` ```API curl --request GET \ --url 'https://api.us.nylas.com/v3/grants/<NYLAS_GRANT_ID>/calendars' \ --header 'Authorization: Bearer <NYLAS_API_KEY>' \ --header 'Content-Type: application/json' ``` ### Get a specific calendar GET /calendars/{id} → /v3/grants/{grant_id}/calendars/{calendar_id} ```Node import 'dotenv/config' import Nylas from 'nylas' const NylasConfig = { apiKey: process.env.NYLAS_API_KEY, apiUri: process.env.NYLAS_API_URI, } const nylas = new Nylas(NylasConfig); async function fetchCalendar() { try { const calendar = await nylas.calendars.find({ identifier: process.env.NYLAS_GRANT_ID, calendarId: process.env.CALENDAR_ID, }) console.log('Calendar:', calendar) } catch (error) { console.error('Error fetching calendars:', error) } } fetchCalendar() ``` ```Java import com.nylas.NylasClient; import com.nylas.models.*; public class GetCalendar { public static void main(String[] args) throws NylasSdkTimeoutError, NylasApiError { NylasClient nylas = new NylasClient.Builder("<NYLAS_API_KEY>").build(); Response<Calendar> calendar = nylas.calendars().find("<NYLAS_GRANT_ID>", "<CALENDAR_ID>"); System.out.println("Id: " + calendar.getData().getId() + " | Name: " + calendar.getData().getName() + " | Description: " + calendar.getData().getDescription()); } } ``` ```Python from dotenv import load_dotenv load_dotenv() import os import sys from nylas import Client nylas = Client( os.environ.get('NYLAS_API_KEY'), os.environ.get('NYLAS_API_URI') ) grant_id = os.environ.get("NYLAS_GRANT_ID") calendar = nylas.calendars.find( grant_id, os.environ.get("CALENDAR_ID") ) print(calendar) ``` ```Ruby require 'nylas' nylas = Nylas::Client.new(api_key: "<NYLAS_API_KEY>") calendar, _request_ids = nylas.calendars.find( identifier: "<NYLAS_GRANT_ID>", calendar_id: "<CALENDAR_ID>" ) puts calendar ``` ```API curl --request GET \ --url https://api.us.nylas.com/v3/grants/<NYLAS_GRANT_ID>/calendars/<CALENDAR_ID> \ --header 'Accept: application/json' \ --header 'Authorization: Bearer <NYLAS_API_KEY>' \ --header 'Content-Type: application/json' ``` ``` -------------------------------------------------------------------------------- /nylas-code-samples/Coda/webhooks.md: -------------------------------------------------------------------------------- ```markdown These changes to webhooks are important to know about, but you don't _need_ to know about in order to migrate and update your code. ### New webhook utilities Nylas API v3 introduces new Webhook endpoints to make testing and maintenance easier. Use these endpoints to quickly get a "test" payload, to see the IP addresses for your webhooks, and to rotate webhook credentials. - [**Get Mock Payload**](https://developer.nylas.com/docs/api/v3/admin/#post-/v3/webhooks/mock-payload): Retrieve a mock webhook notification payload for testing purposes. - [**Send Test Event**](https://developer.nylas.com/docs/api/v3/admin/#post-/v3/webhooks/send-test-event): Send a test event to a specific `webhook_url` so you can verify the webhook's functionality and responsiveness. - [**Get IP Addresses**](https://developer.nylas.com/docs/api/v3/admin/#get-/v3/webhooks/ip-addresses): Get a list of IP addresses associated with the specified webhook. Use this for allowlisting and other security purposes. - [**Rotate webhook secret**](https://developer.nylas.com/docs/api/v3/admin/#post-/v3/webhooks/rotate-secret/-id-): Update the webhook secret used to encode the `X-Nylas-Signature` header on webhook requests, for enhanced security and authentication. ### Build v3 webhook infrastructure for scale In general, you should prepare for changes in webhook notification size, which might mean you should [Build for scalability](https://developer.nylas.com/docs/dev-guide/best-practices/webhook-best-practices/#build-for-scalability). Nylas also suggests you make the following changes to accommodate the v3 webhook infrastructure: - Make sure the webhook listener that you subscribe to `message` and `event` triggers can handle webhook notification payloads up to 1MB in size. Changes to the v3 webhooks shouldn't drastically impact the number of notifications you get (unless your API v2 implementation uses historical webhooks), but it will change the amount of data processed for each notification. - Make sure that any systems that autoscale are prepared to accept and process larger webhook payloads. For more information, see [Build for scalability](https://developer.nylas.com/docs/dev-guide/best-practices/webhook-best-practices/#build-for-scalability). - You might want to run some tests using v3 webhooks to determine your average payload size. - You can remove or adapt the logic you used to re-query the Nylas API for an object using its ID, and instead parse the webhook notification payload. - You can adapt your re-query logic to listen for `.truncated` webhook triggers (for objects over 1MB in size), then query for the rest of the object's information as needed. - No more historical sync means that if you previously used webhooks to access your end users' historical data, you must query for that data the first time an end user authenticates instead. You can use the `grant.created` webhook to listen for new grants so you can trigger the query logic. ### You can now rotate webhook signatures In previous versions of the API, Nylas webhook destinations used HMAC-SHA256 signatures with your application's `client_secret` as the signing key. In API v3, Nylas generates a new webhook secret for each webhook destination when it is created, and returns the value as `webhook_secret` in the success response. These webhook secrets are used as unique signing keys, and the signatures are still HMAC-SHA256-encoded. You use the webhook secret to sign any requests you make to update the webhook. If you want to update a webhook secret, you can now make a `POST` request to the [Rotate Secret endpoint](https://developer.nylas.com/docs/api/v3/admin/#post-/v3/webhooks/rotate-secret/-id-). See [Respond to webhook verification request](https://developer.nylas.com/docs/v3/notifications/webhooks/#respond-to-webhook-verification-request) for more details. ### Failed webhooks are no longer restarted API v3 improves how Nylas handles webhook destinations that are `failing` or `failed`. You can read more about that [in the v3 Webhook documentation](https://developer.nylas.com/docs/v3/notifications/webhooks/#failing-and-failed-webhooks). :::warn ⚠️ **Nylas API v3 does not automatically restart or reactivate webhook destinations in a "failed" state**. When you get a webhook failure notification, check your webhook listener, resolve any issues, and when you're confident that the issues have been resolved change the webhook's status to `active`. ::: Nylas v3 also adds the `webhook_delivery_attempt` counter to the webhook notification payloads, so you can see how many times Nylas tried to deliver the notification. ### No webhook notifications for historic sync Nylas no longer syncs data from before the user _first_ authenticated, and you don't get notifications for changes, messages, or calendar events that occurred _before_ the user authenticated. Instead of relying on webhook notifications for historical data you can query the Nylas API endpoints after the user authenticates to get any historical data you need. ### Webhook notification backfill for expired grants In v3, Nylas does _not_ send notifications for events that happened before an end user authenticated with your application for the first time. When an end user's grant expires, Nylas stops sending notifications for changes because it can no longer access the account's data. If the grant expires then becomes valid _within 72 hours_, Nylas produces notifications for the period that the grant was out of service. You might get a high volume of incoming webhooks while Nylas syncs the events. See the [webhooks best practices guide](https://developer.nylas.com/docs/dev-guide/best-practices/webhook-best-practices/) for suggestions on how to prepare to process a large number of incoming webhooks. If the grant expires and becomes valid _after_ that 72 hour period, Nylas does _not_ send backfill notifications for events that occurred while their grant was out of service. In this case, look for the `grant.expired` and `grant.updated` notifications and query the Nylas APIs for objects that changed between those timestamps. ``` -------------------------------------------------------------------------------- /nylas-code-samples/Email/Labels-Folders/write.md: -------------------------------------------------------------------------------- ```markdown ### Update a specific folder `PUT /folders/<FOLDER_ID>`, `PUT /labels/<LABEL_ID>` → [`PUT /v3/grants/<NYLAS_GRANT_ID>/folders/<FOLDER_ID>`](https://developer.nylas.com/docs/api/v3/ecc/#put-/v3/grants/-grant_id-/folders/-folder_id-) ```Node import 'dotenv/config' import Nylas from 'nylas' const NylasConfig = { apiKey: process.env.NYLAS_API_KEY, apiUri: process.env.NYLAS_API_URI, } const nylas = new Nylas(NylasConfig) async function updateFolder() { try { const folder = await nylas.folders.update({ identifier: process.env.NYLAS_GRANT_ID, folderId: process.env.FOLDER_ID, requestBody: { name: "Updated Folder Name", textColor: "#000000", backgroundColor: "#434343", } }) console.log('Updated Folder:', folder) } catch (error) { console.error('Error to update folder:', error) } } updateFolder() ``` ```Java import com.nylas.NylasClient; import com.nylas.models.*; public class UpdateLabel { public static void main(String[] args) throws NylasSdkTimeoutError, NylasApiError { NylasClient nylas = new NylasClient.Builder("<NYLAS_API_KEY>").build(); UpdateFolderRequest updateRequest = new UpdateFolderRequest.Builder().name("Renamed ").build(); Response<Folder> folder = nylas.folders().update("<NYLAS_GRANT_ID>", "<FOLDER_ID>", updateRequest); } } ``` ```Python from dotenv import load_dotenv load_dotenv() import os import sys from nylas import Client nylas = Client( os.environ.get('NYLAS_API_KEY'), os.environ.get('NYLAS_API_URI') ) grant_id = os.environ.get("NYLAS_GRANT_ID") folder = nylas.folders.update( grant_id, folder_id=os.environ.get("FOLDER_ID"), request_body={ "name": "Updated Folder Name", "text_color": "#000000", } ) print(folder) ``` ```Ruby require 'nylas' nylas = Nylas::Client.new( api_key: ENV["NYLAS_API_KEY"] ) request_body = { name: "Renamed folder" } folder, _ = nylas.folders.update(identifier: ENV["NYLAS_GRANT_ID"], folder_id: "Label_19", request_body: request_body) puts folder ``` ```API curl --request PUT \ --url https://api.us.nylas.com/v3/grants/<NYLAS_GRANT_ID>/folders/<FOLDER_ID> \ --header 'Accept: application/json' \ --header 'Authorization: Bearer <NYLAS_API_KEY>' \ --header 'Content-Type: application/json' \ --data '{ "name": "Renamed folder" }' ``` ### Create a folder `POST /folders`, `POST /labels` → [`POST /v3/grants/<NYLAS_GRANT_ID>/folders`](https://developer.nylas.com/docs/api/v3/ecc/#post-/v3/grants/-grant_id-/folders) ```Node import 'dotenv/config' import Nylas from 'nylas' const NylasConfig = { apiKey: process.env.NYLAS_API_KEY, apiUri: process.env.NYLAS_API_URI, } const nylas = new Nylas(NylasConfig) const identifier = process.env.NYLAS_GRANT_ID const createFolder = async () => { try { const folder = await nylas.folders.create({ identifier, requestBody: { name: 'New Folder' } }) console.log('Folder created:', folder) } catch (error) { console.error('Error creating folder:', error) } } createFolder() ``` ```Java import com.nylas.NylasClient; import com.nylas.models.*; public class CreateFolder { public static void main(String[] args) throws NylasSdkTimeoutError, NylasApiError { NylasClient nylas = new NylasClient.Builder("<NYLAS_API_KEY>").build(); CreateFolderRequest request = new CreateFolderRequest("My Custom folder", "", "", ""); Response<Folder> label = nylas.folders().create("<NYLAS_GRANT_ID>", request); } } ``` ```Python from dotenv import load_dotenv load_dotenv() import os import sys from nylas import Client nylas = Client( os.environ.get('NYLAS_API_KEY'), os.environ.get('NYLAS_API_URI') ) grant_id = os.environ.get("NYLAS_GRANT_ID") folder = nylas.folders.create( grant_id, request_body={ "name": 'New Folder', "parent": None, } ) print(folder) ``` ```Ruby require 'nylas' nylas = Nylas::Client.new( api_key: "<NYLAS_API_KEY>" ) request_body = { name: "My Custom label" } folder = nylas.folders.create(identifier: "<NYLAS_GRANT_ID>", request_body: request_body) ``` ```API curl --location 'https://api.us.nylas.com/v3/grants/<NYLAS_GRANT_ID>/folders' \ --header 'Content-Type: application/json' \ --header 'Accept: application/json' \ --header 'Authorization: Bearer <NYLAS_API_KEY>' \ --data '{ "text_color": "#000000", "name": "new folder", "background_color": "#434343" }' ``` ### Delete a specific folder `DELETE /folders/<FOLDER_ID>`, `DELETE /labels/<LABEL_ID>` → [`DELETE /v3/grants/<NYLAS_GRANT_ID>/folders/<FOLDER_ID>`](https://developer.nylas.com/docs/api/v3/ecc/#delete-/v3/grants/-grant_id-/folders/-folder_id-) ```Node import 'dotenv/config' import Nylas from 'nylas' const NylasConfig = { apiKey: process.env.NYLAS_API_KEY, apiUri: process.env.NYLAS_API_URI, } const nylas = new Nylas(NylasConfig) const identifier = process.env.NYLAS_GRANT_ID const folderId = process.env.FOLDER_ID const deleteFolder = async () => { try { await nylas.folders.destroy({ identifier, folderId }) console.log(`Folder with ID ${folderId} deleted successfully.`) } catch (error) { console.error(`Error deleting folder with ID ${folderId}:`, error) } } deleteFolder() ``` ```Java import com.nylas.NylasClient; import com.nylas.models.*; public class DestroyLabel { public static void main(String[] args) throws NylasSdkTimeoutError, NylasApiError { NylasClient nylas = new NylasClient.Builder("<NYLAS_API_KEY>").build(); DeleteResponse folder = nylas.folders().destroy("<NYLAS_GRANT_ID>", "<FOLDER_ID>"); } } ``` ```Python from dotenv import load_dotenv load_dotenv() import os import sys from nylas import Client nylas = Client( os.environ.get('NYLAS_API_KEY'), os.environ.get('NYLAS_API_URI') ) grant_id = os.environ.get("NYLAS_GRANT_ID") folder_id = os.environ.get("FOLDER_ID") request = nylas.folders.destroy( grant_id, folder_id, ) print(request) ``` ```Ruby require 'nylas' nylas = Nylas::Client.new( api_key: "<NYLAS_API_KEY>" ) folder, _ = nylas.folders.destroy(identifier: "<NYLAS_GRANT_ID>", folder_id: "<FOLDER_ID>") puts folder ``` ```API curl --location --request DELETE 'https://api.us.nylas.com/v3/grants/<NYLAS_GRANT_ID>/folders/<FOLDER_ID>' \ --header 'Accept: application/json' \ --header 'Authorization: Bearer <NYLAS_API_KEY>' ``` ``` -------------------------------------------------------------------------------- /nylas-code-samples/Calendar/Events/read.md: -------------------------------------------------------------------------------- ```markdown ### Get all events GET /events → GET /v3/grants/{grant_id}/events ```Node import 'dotenv/config' import Nylas from 'nylas' const NylasConfig = { apiKey: process.env.NYLAS_API_KEY, apiUri: process.env.NYLAS_API_URI } const nylas = new Nylas(NylasConfig) async function fetchAllEventsFromCalendar() { try { const events = await nylas.events.list({ identifier: process.env.NYLAS_GRANT_ID, queryParams: { calendarId: process.env.CALENDAR_ID, } }) console.log('Events:', events) } catch (error) { console.error('Error fetching calendars:', error) } } fetchAllEventsFromCalendar() ``` ```Java import com.nylas.NylasClient; import com.nylas.models.When; import com.nylas.models.*; import java.text.SimpleDateFormat; import java.util.List; import java.util.Objects; public class read_calendar_events { public static void main(String[] args) throws NylasSdkTimeoutError, NylasApiError { NylasClient nylas = new NylasClient.Builder("<NYLAS_API_KEY>").build(); ListEventQueryParams listEventQueryParams = new ListEventQueryParams.Builder("<CALENDAR_ID>").build(); List<Event> events = nylas.events().list("<NYLAS_GRANT_ID>", listEventQueryParams).getData(); for (Event event : events) { System.out.print("Id: " + event.getId() + " | "); System.out.print("Title: " + event.getTitle()); switch (Objects.requireNonNull(event.getWhen().getObject()).getValue()) { case "datespan" -> { When.Datespan date = (When.Datespan) event.getWhen(); System.out.print(" | The date of the event is from: " + date.getStartDate() + " to " + date.getEndDate()); } case "date" -> { When.Date date = (When.Date) event.getWhen(); System.out.print(" | The date of the event is: " +date.getDate()); } case "timespan" -> { When.Timespan timespan = (When.Timespan) event.getWhen(); String initDate = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"). format(new java.util.Date((timespan.getStartTime() * 1000L))); String endDate = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"). format(new java.util.Date((timespan.getEndTime() * 1000L))); System.out.print(" | The time of the event is from: " + initDate + " to " + endDate); } } System.out.print(" | Participants: "); for(Participant participant : event.getParticipants()){ System.out.print(" Email: " + participant.getEmail() + " Name: " + participant.getName() + " Status: " + participant.getStatus()); } System.out.println("\n"); } } } ``` ```Python from dotenv import load_dotenv load_dotenv() import os import sys from nylas import Client nylas = Client( os.environ.get('NYLAS_API_KEY'), os.environ.get('NYLAS_API_URI') ) grant_id = os.environ.get("NYLAS_GRANT_ID") events = nylas.events.list( grant_id, query_params={ "calendar_id": os.environ.get("CALENDAR_ID") } ) print(events) ``` ```Ruby # Load gems require 'nylas' # Initialize Nylas client nylas = Nylas::Client.new( api_key: "<NYLAS_API_KEY>" ) # Query parameters query_params = { calendar_id: "<NYLAS_GRANT_ID>" } # Read events from our main calendar in the specified date and time events, _request_ids = nylas.events.list(identifier: "<NYLAS_GRANT_ID>", query_params: query_params) # Loop events events.each {|event| case event[:when][:object] when 'timespan' start_time = Time.at(event[:when][:start_time]).strftime("%d/%m/%Y at %H:%M:%S") end_time = Time.at(event[:when][:end_time]).strftime("%d/%m/%Y at %H:%M:%S") event_date = "The time of the event is from: #{start_time} to #{end_time}" when 'datespan' start_time = event[:when][:start_date] end_time = event[:when][:end_date] event_date = "The date of the event is from: #{start_time} to: #{end_time}" when 'date' start_time = event[:when][:date] event_date = "The date of the event is: #{start_time}" end event[:participants].each {|participant| participant_details += "Email: #{participant[:email]} " \ "Name: #{participant[:name]} Status: #{participant[:status]} - " } print "Id: #{event[:id]} | Title: #{event[:title]} | #{event_date} | " puts "Participants: #{participant_details.chomp(' - ')}" puts "\n" } ``` ```API curl --request GET \ --url https://api.us.nylas.com/v3/grants/<NYLAS_GRANT_ID>/events?calendar_id=<CALENDAR_ID>&start=<EPOCH_TIMESTAMP>&end=<EPOCH_TIMESTAMP> \ --header 'Accept: application/json' \ --header 'Authorization: Bearer <NYLAS_API_KEY>' \ --header 'Content-Type: application/json' ``` ### Get a specific event GET /events/{id} → GET /v3/grants/{grant_id}/events/{event_id} ```Node import 'dotenv/config' import Nylas from 'nylas' const NylasConfig = { apiKey: process.env.NYLAS_API_KEY, apiUri: process.env.NYLAS_API_URI, } const nylas = new Nylas(NylasConfig) async function fetchEventById() { try { const events = await nylas.events.find({ identifier: process.env.NYLAS_GRANT_ID, eventId: process.env.EVENT_ID, queryParams: { calendarId: process.env.CALENDAR_ID, } }) console.log('Events:', events) } catch (error) { console.error('Error fetching calendars:', error) } } fetchEventById() ``` ```Java // Import Nylas packages import com.nylas.NylasClient; import com.nylas.models.When; import com.nylas.models.*; public class ReadEvent { public static void main(String[] args) throws NylasSdkTimeoutError, NylasApiError { NylasClient nylas = new NylasClient.Builder("<NYLAS_API_KEY>").build(); FindEventQueryParams queryParams = new FindEventQueryParams("<CALENDAR_ID>"); Response<Event> event = nylas.events().find("<NYLAS_GRANT_ID>", "<EVENT_ID>", queryParams); } } ``` ```Python from dotenv import load_dotenv load_dotenv() import os import sys from nylas import Client nylas = Client( os.environ.get('NYLAS_API_KEY'), os.environ.get('NYLAS_API_URI') ) grant_id = os.environ.get("NYLAS_GRANT_ID") event_id = os.environ.get("EVENT_ID") calendar_id = os.environ.get("CALENDAR_ID") event = nylas.events.find( grant_id, event_id, query_params={ "calendar_id": calendar_id } ) print(event) ``` ```Ruby # Load gems require 'nylas' # Initialize Nylas client nylas = Nylas::Client.new( api_key: "<NYLAS_API_KEY>" ) # Query parameters query_params = { calendar_id: "<NYLAS_GRANT_ID>" } # Read events from our main calendar in the specified date and time events, _request_ids = nylas.events.find(identifier: "<NYLAS_GRANT_ID>", event_id: "<EVENT_ID>", query_params: query_params) # Loop events events.each {|event| puts event } ``` ```API curl --request GET \ --url https://api.us.nylas.com/v3/grants/<NYLAS_GRANT_ID>/events/<EVENT_ID>?calendar_id=<CALENDAR_ID> \ --header 'Accept: application/json' \ --header 'Authorization: Bearer <NYLAS_API_KEY>' \ --header 'Content-Type: application/json' ``` ``` -------------------------------------------------------------------------------- /nylas-code-samples/Calendar/Calendars/write.md: -------------------------------------------------------------------------------- ```markdown ### Update a specific calendar PUT /calendars/{id} → PUT /v3/grants/{grant_id}/calendars/{calendar_id} ```Node import 'dotenv/config' import Nylas from 'nylas' const NylasConfig = { apiKey: process.env.NYLAS_API_KEY, apiUri: process.env.NYLAS_API_URI, } const nylas = new Nylas(NylasConfig) async function updateCalendar() { try { const calendar = await nylas.calendars.update({ identifier: process.env.NYLAS_GRANT_ID, calendarId: process.env.CALENDAR_ID, requestBody: { name: 'Nylas DevRel Calendar', description: 'Nylas Developer Relations', } }) console.log('Updated Calendar:', calendar) } catch (error) { console.error('Error to update calendar:', error) } } updateCalendar() ``` ```Java import com.nylas.NylasClient; import com.nylas.models.*; public class UpdateCalendar { public static void main(String[] args) throws NylasSdkTimeoutError, NylasApiError { NylasClient nylas = new NylasClient.Builder("<NYLAS_API_KEY>").build(); UpdateCalendarRequest requestBody = new UpdateCalendarRequest.Builder(). name("My New Calendar"). description("Description of my new calendar"). location("Location description"). timezone("America/Los_Angeles"). build(); Response<Calendar> calendar = nylas.calendars().update( "<CALENDAR_ID>", "<CALENDAR_ID>", requestBody); System.out.println(calendar.getData()); } } ``` ```Python from dotenv import load_dotenv load_dotenv() import os import sys from nylas import Client nylas = Client( os.environ.get('NYLAS_API_KEY'), os.environ.get('NYLAS_API_URI') ) grant_id = os.environ.get("NYLAS_GRANT_ID") calendar = nylas.calendars.update( grant_id, calendar_id=os.environ.get("CALENDAR_ID"), request_body={ "name": 'Nylas DevRel Calendar', "description": 'Nylas Developer Relations' } ) print(calendar) ``` ```Ruby require 'nylas' nylas = Nylas::Client.new(api_key: "<NYLAS_API_KEY>") request_body = { "name": "\"New Test Calendar (changed)\"", "description": "\"this calendar has been updated!\"", } calendar, _request_ids = nylas.calendars.update( identifier: "<NYLAS_GRANT_ID>", calendar_id: "<CALENDAR_ID", request_body: request_body) puts calendar ``` ```API curl --request PUT \ --url https://api.us.nylas.com/v3/grants/<NYLAS_GRANT_ID>/calendars/<CALENDAR_ID> \ --header 'Accept: application/json' \ --header 'Authorization: Bearer <NYLAS_API_KEY>' \ --header 'Content-Type: application/json' \ --data '{ "name": "My New Calendar", "description": "Description of my new calendar", "location": "Location description", "timezone": "America/Los_Angeles" }' ``` ### Create a specific calendar POST /calendars → POST /v3/grants/{grant_id}/calendars ```Node import 'dotenv/config' import Nylas from 'nylas' const NylasConfig = { apiKey: process.env.NYLAS_API_KEY, apiUri: process.env.NYLAS_API_URI, } const nylas = new Nylas(NylasConfig) async function createCalendar() { try { const calendar = await nylas.calendars.create({ identifier: process.env.NYLAS_GRANT_ID, requestBody: { name: 'Nylas DevRel', description: 'Nylas Developer Relations', } }) console.log('Calendar:', calendar) } catch (error) { console.error('Error to create calendar:', error) } } createCalendar() ``` ```Java import com.nylas.NylasClient; import com.nylas.models.*; import java.util.Map; public class CreateCalendar { public static void main(String[] args) throws NylasSdkTimeoutError, NylasApiError { NylasClient nylas = new NylasClient.Builder("<NYLAS_API_KEY>").build(); CreateCalendarRequest requestBody = new CreateCalendarRequest( "My New Calendar", "Description of my new calendar", "Location description", "America/Toronto", Map.of("key1", "This is my metadata")); Response<Calendar> calendar = nylas.calendars(). create("<NYLAS_GRANT_ID>", requestBody); System.out.println(calendar.getData()); } } ``` ```Python from dotenv import load_dotenv load_dotenv() import os import sys from nylas import Client nylas = Client( os.environ.get('NYLAS_API_KEY'), os.environ.get('NYLAS_API_URI') ) grant_id = os.environ.get("NYLAS_GRANT_ID") calendar = nylas.calendars.create( grant_id, request_body={ "name": 'Nylas DevRel', "description": 'Nylas Developer Relations' } ) print(calendar) ``` ```Ruby require 'nylas' nylas = Nylas::Client.new(api_key: "<NYLAS_API_KEY>") query_params = { calendar_id: "<CALENDAR_ID>" } request_body = { "name": "My New Calendar", "description": "Description of my new calendar", "location": "Location description", "timezone": "America/Toronto", "metadata": { "key1":"This is my metadata" } } calendar, _request_ids = nylas.calendars.create( identifier: "<NYLAS_GRANT_ID>", request_body: request_body) puts calendar ``` ```API curl --request POST \ --url https://api.us.nylas.com/v3/grants/<NYLAS_GRANT_ID>/calendars \ --header 'Accept: application/json' \ --header 'Authorization: Bearer <NYLAS_API_KEY>' \ --header 'Content-Type: application/json' \ --data '{ "name": "My New Calendar", "description": "Description of my new calendar", "location": "Location description", "timezone": "America/Los_Angeles" }' ``` ### Delete a specific calendar DELETE /calendars/{id} → DELETE /v3/grants/{grant_id}/calendars/{calendar_id} ```Node import 'dotenv/config' import Nylas from 'nylas' const NylasConfig = { apiKey: process.env.NYLAS_API_KEY, apiUri: process.env.NYLAS_API_URI, } const nylas = new Nylas(NylasConfig) async function deleteCalendar() { try { const calendar = await nylas.calendars.destroy({ identifier: process.env.NYLAS_GRANT_ID, calendarId: process.env.CALENDAR_ID, }) console.log('Calendar:', calendar) } catch (error) { console.error('Error to create calendar:', error) } } deleteCalendar() ``` ```Java // Import packages import com.nylas.NylasClient; import com.nylas.models.*; public class DeleteCalendar { public static void main(String[] args) throws NylasSdkTimeoutError, NylasApiError { NylasClient nylas = new NylasClient.Builder("<NYLAS_API_KEY>").build(); // Delete the requested calendar try { nylas.calendars().destroy("<NYLAS_GRANT_ID>", "<CALENDAR_ID>"); System.out.println("Deleted successfully"); } catch(Exception e) { System.out.println("There was an error " + e); } } } ``` ```Python from dotenv import load_dotenv load_dotenv() import os import sys from nylas import Client nylas = Client( os.environ.get('NYLAS_API_KEY'), os.environ.get('NYLAS_API_URI') ) grant_id = os.environ.get("NYLAS_GRANT_ID") calendar_id = os.environ.get("CALENDAR_ID") request = nylas.calendars.destroy( grant_id, calendar_id, ) print(request) ``` ```Ruby require 'nylas' nylas = Nylas::Client.new( api_key: "<NYLAS_API_KEY>" ) calendar, = nylas.calendars.destroy(identifier: "<NYLAS_GRANT_ID>", calendar_id: "<CALENDAR_ID") puts calendar ``` ```API curl --request DELETE \ --url https://api.us.nylas.com/v3/grants/<NYLAS_GRANT_ID>/calendars/<CALENDAR_ID> \ --header 'Accept: application/json' \ --header 'Authorization: Bearer <NYLAS_API_KEY>' \ --header 'Content-Type: application/json' ``` ``` -------------------------------------------------------------------------------- /src/prompts/index.ts: -------------------------------------------------------------------------------- ```typescript import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; import { z } from "zod"; /** * Register all prompts with the MCP server */ export function registerPrompts(server: McpServer) { // Register general prompts registerGeneralPrompts(server); // Register feature-specific prompts registerFeaturePrompts(server); // Register integration prompts registerIntegrationPrompts(server); } /** * Register general prompts for Nylas API */ function registerGeneralPrompts(server: McpServer) { // Prompt for getting started with Nylas server.prompt( "nylas-getting-started", {}, () => ({ messages: [ { role: "user", content: { type: "text", text: `I want to integrate with the Nylas API. Can you help me get started and explain the basic steps?` } } ] }) ); // Prompt for understanding authentication server.prompt( "nylas-auth-guide", {}, () => ({ messages: [ { role: "user", content: { type: "text", text: `Explain how authentication works with the Nylas API. What are the steps to authenticate users and what OAuth flow should I implement?` } } ] }) ); // Prompt for API best practices server.prompt( "nylas-api-best-practices", {}, () => ({ messages: [ { role: "user", content: { type: "text", text: `What are the best practices for working with the Nylas API? Please include information about rate limits, error handling, and efficient API usage.` } } ] }) ); } /** * Register prompts for specific Nylas API features */ function registerFeaturePrompts(server: McpServer) { // Prompt for email integration server.prompt( "nylas-email-integration", { feature: z.enum(["read", "send", "threads", "search", "attachments", "drafts"]).optional() }, ({ feature }) => { let message = `I want to integrate email functionality using the Nylas API.`; if (feature) { switch (feature) { case "read": message += ` Specifically, I need to implement the ability to read and display emails from a user's inbox. How should I approach this?`; break; case "send": message += ` Specifically, I need to implement the ability to send emails through my application. How should I approach this?`; break; case "threads": message += ` Specifically, I need to implement conversation threading to group related emails. How should I approach this?`; break; case "search": message += ` Specifically, I need to implement email search functionality. How should I approach this?`; break; case "attachments": message += ` Specifically, I need to implement handling of email attachments (both uploading and downloading). How should I approach this?`; break; case "drafts": message += ` Specifically, I need to implement email draft functionality. How should I approach this?`; break; } } else { message += ` What are the key features I should know about and how should I implement them?`; } return { messages: [ { role: "user", content: { type: "text", text: message } } ] }; } ); // Prompt for calendar integration server.prompt( "nylas-calendar-integration", { feature: z.enum(["events", "availability", "recurring"]).optional() }, ({ feature }) => { let message = `I want to integrate calendar functionality using the Nylas API.`; if (feature) { switch (feature) { case "events": message += ` Specifically, I need to implement the ability to create, read, update, and delete calendar events. How should I approach this?`; break; case "availability": message += ` Specifically, I need to implement availability checking and scheduling. How should I approach this?`; break; case "recurring": message += ` Specifically, I need to implement recurring events. How should I approach this?`; break; } } else { message += ` What are the key features I should know about and how should I implement them?`; } return { messages: [ { role: "user", content: { type: "text", text: message } } ] }; } ); // Prompt for contacts integration server.prompt( "nylas-contacts-integration", {}, () => ({ messages: [ { role: "user", content: { type: "text", text: `I want to integrate contacts functionality using the Nylas API. What are the key features I should know about and how should I implement them?` } } ] }) ); // Prompt for webhooks server.prompt( "nylas-webhooks-guide", {}, () => ({ messages: [ { role: "user", content: { type: "text", text: `I want to implement webhooks with the Nylas API to get real-time updates. How do I set up and handle webhooks properly?` } } ] }) ); } /** * Register prompts for integration scenarios */ function registerIntegrationPrompts(server: McpServer) { // Prompt for debugging common issues server.prompt( "nylas-debug-common-issues", {}, () => ({ messages: [ { role: "user", content: { type: "text", text: `What are the most common issues developers face when integrating with the Nylas API, and how can I debug and resolve them?` } } ] }) ); // Prompt for implementing specific integration scenario server.prompt( "nylas-integration-scenario", { scenario: z.enum([ "email-client", "calendar-booking", "crm-integration", "customer-support", "automated-workflows" ]) }, ({ scenario }) => { let message = ""; switch (scenario) { case "email-client": message = `I want to build an email client using the Nylas API that allows users to read, send, organize, and search emails. What are the key components I need to implement and what API endpoints should I use?`; break; case "calendar-booking": message = `I want to build a calendar booking system using the Nylas API that allows users to share their availability and let others book time slots. What are the key components I need to implement and what API endpoints should I use?`; break; case "crm-integration": message = `I want to integrate Nylas with my CRM system to sync emails, calendar events, and contacts. What are the key components I need to implement and what API endpoints should I use?`; break; case "customer-support": message = `I want to build a customer support system that uses Nylas to access and respond to customer emails. What are the key components I need to implement and what API endpoints should I use?`; break; case "automated-workflows": message = `I want to implement automated workflows using Nylas, such as scheduling follow-ups, sending reminder emails, and organizing incoming messages. What are the key components I need to implement and what API endpoints should I use?`; break; } return { messages: [ { role: "user", content: { type: "text", text: message } } ] }; } ); // Prompt for SDK usage examples server.prompt( "nylas-sdk-examples", { language: z.enum(["node", "python", "java", "ruby"]) }, ({ language }) => { const normalizedLanguage = language.charAt(0).toUpperCase() + language.slice(1); return { messages: [ { role: "user", content: { type: "text", text: `Can you provide code examples for common Nylas API operations using the ${normalizedLanguage} SDK? Include examples for authentication, reading emails, sending emails, and working with calendar events.` } } ] }; } ); // Prompt for migrating from v2 to v3 server.prompt( "nylas-v3-migration", {}, () => ({ messages: [ { role: "user", content: { type: "text", text: `I'm migrating from Nylas API v2 to v3. What are the key differences and how should I update my code?` } } ] }) ); } ``` -------------------------------------------------------------------------------- /src/resources/code-samples.ts: -------------------------------------------------------------------------------- ```typescript import { McpServer, ResourceTemplate } from "@modelcontextprotocol/sdk/server/mcp.js"; import fs from 'fs/promises'; import path from 'path'; /** * Register code sample resources for the Nylas API */ export async function registerCodeSamplesResources(server: McpServer) { // Path to the code samples directory const codeSamplesPath = path.join(process.cwd(), 'nylas-code-samples'); // Register a resource for each code sample category await registerCategories(server, codeSamplesPath); // Register a resource for specific code samples by path server.resource( "code-sample-by-path", new ResourceTemplate("nylas://code-samples/{path*}", { list: undefined }), async (uri, { path: samplePath }) => { try { // Replace slashes in the path parameter with the correct path separator const normalizedPath = typeof samplePath === 'string' ? samplePath.replace(/\//g, path.sep) : samplePath.join(path.sep); // Determine if it's a directory or a file const fullPath = path.join(codeSamplesPath, normalizedPath); const stats = await fs.stat(fullPath); if (stats.isDirectory()) { // If it's a directory, list the contents const files = await fs.readdir(fullPath); const mdFiles = files.filter(file => file.endsWith('.md')); // If there's an index.md file, use that if (mdFiles.includes('index.md')) { const indexContent = await fs.readFile(path.join(fullPath, 'index.md'), 'utf-8'); return { contents: [{ uri: uri.href, text: indexContent, mimeType: "text/markdown" }] }; } // Otherwise, create a directory listing const listing = `# ${path.basename(normalizedPath)} Code Samples\n\n` + `Available samples:\n\n` + mdFiles.map(file => `- [${file.replace('.md', '')}](nylas://code-samples/${samplePath}/${file})`).join('\n'); return { contents: [{ uri: uri.href, text: listing, mimeType: "text/markdown" }] }; } else { // If it's a file, return its contents const content = await fs.readFile(fullPath, 'utf-8'); return { contents: [{ uri: uri.href, text: content, mimeType: "text/markdown" }] }; } } catch (error) { // If the file doesn't exist, return a not found message return { contents: [{ uri: uri.href, text: `# Not Found\n\nThe requested code sample "${samplePath}" was not found.`, mimeType: "text/markdown" }] }; } } ); // Register a resource to get code samples by language server.resource( "code-samples-by-language", new ResourceTemplate("nylas://code-samples/language/{language}/{category?}", { list: undefined }), async (uri, { language, category }) => { try { // Normalize language name const normalizedLanguage = normalizeLanguageName(typeof language === 'string' ? language : language[0]); // If a category is specified, search only in that category if (category) { const categoryPath = path.join(codeSamplesPath, typeof category === 'string' ? category : category[0]); const samples = await findCodeSamplesByLanguage(categoryPath, normalizedLanguage); return { contents: [{ uri: uri.href, text: formatCodeSamplesForLanguage(normalizedLanguage, typeof category === 'string' ? category : category[0], samples), mimeType: "text/markdown" }] }; } else { // Search in all categories const categories = await fs.readdir(codeSamplesPath); // Store all found code samples const allSamples: Record<string, string[]> = {}; // Find code samples in each category for (const cat of categories) { const categoryPath = path.join(codeSamplesPath, cat); // Skip if not a directory const stats = await fs.stat(categoryPath); if (!stats.isDirectory()) continue; const samples = await findCodeSamplesByLanguage(categoryPath, normalizedLanguage); if (samples.length > 0) { allSamples[cat] = samples; } } return { contents: [{ uri: uri.href, text: formatAllCodeSamplesForLanguage(normalizedLanguage, allSamples), mimeType: "text/markdown" }] }; } } catch (error) { // If there's an error, return an error message return { contents: [{ uri: uri.href, text: `# Error\n\nAn error occurred while searching for ${language} code samples${category ? ` in ${category}` : ''}.`, mimeType: "text/markdown" }] }; } } ); } /** * Register resources for all categories of code samples */ async function registerCategories(server: McpServer, codeSamplesPath: string) { // Register a resource listing all categories server.resource( "code-samples-categories", "nylas://code-samples", async (uri) => { try { const categories = await fs.readdir(codeSamplesPath); const dirCategories = await Promise.all( categories.map(async (category) => { const categoryPath = path.join(codeSamplesPath, category); const stats = await fs.stat(categoryPath); return stats.isDirectory() ? category : null; }) ); const validCategories = dirCategories.filter(Boolean) as string[]; const content = `# Nylas API Code Samples This resource provides code samples for integrating with the Nylas API in various programming languages. ## Available Categories ${validCategories.map(category => `- [${category}](nylas://code-samples/${category})`).join('\n')} ## Samples by Language You can also view samples filtered by programming language: - [Node.js](nylas://code-samples/language/node) - [Python](nylas://code-samples/language/python) - [Java](nylas://code-samples/language/java) - [Ruby](nylas://code-samples/language/ruby) - [API (curl)](nylas://code-samples/language/api)`; return { contents: [{ uri: uri.href, text: content, mimeType: "text/markdown" }] }; } catch (error) { return { contents: [{ uri: uri.href, text: "# Error\n\nFailed to load code sample categories.", mimeType: "text/markdown" }] }; } } ); } /** * Recursively find code samples for a specific language in a directory */ async function findCodeSamplesByLanguage(dirPath: string, language: string): Promise<string[]> { const results: string[] = []; try { const entries = await fs.readdir(dirPath, { withFileTypes: true }); for (const entry of entries) { const fullPath = path.join(dirPath, entry.name); if (entry.isDirectory()) { // Recursively search in subdirectories const subResults = await findCodeSamplesByLanguage(fullPath, language); results.push(...subResults); } else if (entry.name.endsWith('.md')) { // Check if the file contains code samples for the specified language const content = await fs.readFile(fullPath, 'utf-8'); if (content.includes(`\`\`\`${language}`) || (language === 'Node' && content.includes('```Node')) || (language === 'API' && content.includes('```API'))) { // Store the relative path instead of the absolute path const relativePath = fullPath.replace(process.cwd() + path.sep + 'nylas-code-samples' + path.sep, ''); results.push(relativePath); } } } } catch (error) { // Ignore errors and return what we found so far } return results; } /** * Normalize language names to match code block markers in markdown files */ function normalizeLanguageName(language: string): string { const langMap: Record<string, string> = { 'node': 'Node', 'nodejs': 'Node', 'javascript': 'Node', 'js': 'Node', 'python': 'Python', 'py': 'Python', 'java': 'Java', 'ruby': 'Ruby', 'rb': 'Ruby', 'curl': 'API', 'api': 'API', 'rest': 'API' }; return langMap[language.toLowerCase()] || language; } /** * Format code samples for a specific language in a category */ function formatCodeSamplesForLanguage(language: string, category: string, samples: string[]): string { if (samples.length === 0) { return `# No ${language} Code Samples Found\n\nNo code samples for ${language} were found in the ${category} category.`; } return `# ${language} Code Samples for ${category}\n\n` + `Found ${samples.length} code sample(s) for ${language} in the ${category} category:\n\n` + samples.map(sample => `- [${path.basename(sample, '.md')}](nylas://code-samples/${sample.replace(/\\/g, '/')})`).join('\n'); } /** * Format all code samples for a specific language across all categories */ function formatAllCodeSamplesForLanguage(language: string, samples: Record<string, string[]>): string { const categories = Object.keys(samples); if (categories.length === 0) { return `# No ${language} Code Samples Found\n\nNo code samples for ${language} were found.`; } let content = `# ${language} Code Samples\n\n`; for (const category of categories) { content += `## ${category}\n\n`; content += samples[category] .map(sample => `- [${path.basename(sample, '.md')}](nylas://code-samples/${sample.replace(/\\/g, '/')})`).join('\n'); content += '\n\n'; } return content; } ```