#
tokens: 47968/50000 13/85 files (page 2/3)
lines: on (toggle) GitHub
raw markdown copy reset
This is page 2 of 3. Use http://codebase.md/nylas-samples/nylas-api-mcp?lines=true&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

--------------------------------------------------------------------------------
/nylas-code-samples/Coda/webhooks.md:
--------------------------------------------------------------------------------

```markdown
 1 | 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.
 2 | 
 3 | ### New webhook utilities
 4 | 
 5 | 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.
 6 | 
 7 | - [**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.
 8 | - [**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.
 9 | - [**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.
10 | - [**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.
11 | 
12 | ### Build v3 webhook infrastructure for scale
13 | 
14 | 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).
15 | 
16 | Nylas also suggests you make the following changes to accommodate the v3 webhook infrastructure:
17 | 
18 | - 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.
19 |   - 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).
20 |   - You might want to run some tests using v3 webhooks to determine your average payload size.
21 | - 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.
22 | - 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.
23 | - 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.
24 | 
25 | ### You can now rotate webhook signatures
26 | 
27 | In previous versions of the API, Nylas webhook destinations used HMAC-SHA256 signatures with your application's `client_secret` as the signing key.
28 | 
29 | 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.
30 | 
31 | 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-).
32 | 
33 | See [Respond to webhook verification request](https://developer.nylas.com/docs/v3/notifications/webhooks/#respond-to-webhook-verification-request) for more details.
34 | 
35 | ### Failed webhooks are no longer restarted
36 | 
37 | 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).
38 | 
39 | :::warn
40 | ⚠️ **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`.
41 | :::
42 | 
43 | 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.
44 | 
45 | ### No webhook notifications for historic sync
46 | 
47 | 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.
48 | 
49 | 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.
50 | 
51 | ### Webhook notification backfill for expired grants
52 | 
53 | 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.
54 | 
55 | 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.
56 | 
57 | 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.
58 | 
```

--------------------------------------------------------------------------------
/nylas-code-samples/Email/Labels-Folders/write.md:
--------------------------------------------------------------------------------

```markdown
  1 | ### Update a specific folder
  2 | 
  3 | `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-)
  4 | 
  5 | ```Node
  6 | import 'dotenv/config'
  7 | import Nylas from 'nylas'
  8 | 
  9 | const NylasConfig = {
 10 |   apiKey: process.env.NYLAS_API_KEY,
 11 |   apiUri: process.env.NYLAS_API_URI,
 12 | }
 13 | 
 14 | const nylas = new Nylas(NylasConfig)
 15 | 
 16 | async function updateFolder() {
 17 |   try {
 18 |     const folder = await nylas.folders.update({
 19 |       identifier: process.env.NYLAS_GRANT_ID,
 20 |       folderId: process.env.FOLDER_ID,
 21 |       requestBody: {
 22 |         name: "Updated Folder Name",
 23 |         textColor: "#000000",
 24 |         backgroundColor: "#434343",
 25 |       }
 26 |     })
 27 | 
 28 |     console.log('Updated Folder:', folder)
 29 |   } catch (error) {
 30 |     console.error('Error to update folder:', error)
 31 |   }
 32 | }
 33 | 
 34 | updateFolder()
 35 | ```
 36 | 
 37 | ```Java
 38 | import com.nylas.NylasClient;
 39 | import com.nylas.models.*;
 40 | 
 41 | public class UpdateLabel {
 42 |   public static void main(String[] args) throws NylasSdkTimeoutError, NylasApiError {
 43 |     NylasClient nylas = new NylasClient.Builder("<NYLAS_API_KEY>").build();
 44 |     UpdateFolderRequest updateRequest = new UpdateFolderRequest.Builder().name("Renamed ").build();
 45 |     Response<Folder> folder = nylas.folders().update("<NYLAS_GRANT_ID>", "<FOLDER_ID>", updateRequest);
 46 |   }
 47 | }
 48 | ```
 49 | 
 50 | ```Python
 51 | from dotenv import load_dotenv
 52 | load_dotenv()
 53 | 
 54 | import os
 55 | import sys
 56 | from nylas import Client
 57 | 
 58 | nylas = Client(
 59 |     os.environ.get('NYLAS_API_KEY'),
 60 |     os.environ.get('NYLAS_API_URI')
 61 | )
 62 | 
 63 | grant_id = os.environ.get("NYLAS_GRANT_ID")
 64 | 
 65 | folder = nylas.folders.update(
 66 |     grant_id,
 67 |     folder_id=os.environ.get("FOLDER_ID"),
 68 |     request_body={
 69 |       "name": "Updated Folder Name",
 70 |       "text_color": "#000000",
 71 |     }
 72 | )
 73 | 
 74 | print(folder)
 75 | ```
 76 | 
 77 | ```Ruby
 78 | require 'nylas'
 79 | 
 80 | nylas = Nylas::Client.new(
 81 |     api_key: ENV["NYLAS_API_KEY"]
 82 | )
 83 | 
 84 | request_body = {
 85 |   name: "Renamed folder"
 86 | }
 87 | 
 88 | folder, _ = nylas.folders.update(identifier: ENV["NYLAS_GRANT_ID"],
 89 |     folder_id: "Label_19", request_body: request_body)
 90 | 
 91 | puts folder
 92 | ```
 93 | 
 94 | ```API
 95 | curl --request PUT \
 96 |   --url https://api.us.nylas.com/v3/grants/<NYLAS_GRANT_ID>/folders/<FOLDER_ID> \
 97 |   --header 'Accept: application/json' \
 98 |   --header 'Authorization: Bearer <NYLAS_API_KEY>' \
 99 |   --header 'Content-Type: application/json' \
100 |   --data '{
101 |     "name": "Renamed folder"
102 |   }'
103 | ```
104 | 
105 | ### Create a folder
106 | 
107 | `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)
108 | 
109 | ```Node
110 | import 'dotenv/config'
111 | import Nylas from 'nylas'
112 | 
113 | const NylasConfig = {
114 |   apiKey: process.env.NYLAS_API_KEY,
115 |   apiUri: process.env.NYLAS_API_URI,
116 | }
117 | 
118 | const nylas = new Nylas(NylasConfig)
119 | const identifier = process.env.NYLAS_GRANT_ID
120 | 
121 | const createFolder = async () => {
122 |   try {
123 |     const folder = await nylas.folders.create({
124 |       identifier,
125 |       requestBody: {
126 |         name: 'New Folder'
127 |       }
128 |     })
129 | 
130 |     console.log('Folder created:', folder)
131 |   } catch (error) {
132 |     console.error('Error creating folder:', error)
133 |   }
134 | }
135 | 
136 | createFolder()
137 | ```
138 | 
139 | ```Java
140 | import com.nylas.NylasClient;
141 | import com.nylas.models.*;
142 | 
143 | public class CreateFolder {
144 |   public static void main(String[] args) throws NylasSdkTimeoutError, NylasApiError {
145 |     NylasClient nylas = new NylasClient.Builder("<NYLAS_API_KEY>").build();
146 |     CreateFolderRequest request = new CreateFolderRequest("My Custom folder", "", "", "");
147 |     Response<Folder> label = nylas.folders().create("<NYLAS_GRANT_ID>", request);
148 |   }
149 | }
150 | ```
151 | 
152 | ```Python
153 | from dotenv import load_dotenv
154 | load_dotenv()
155 | 
156 | import os
157 | import sys
158 | from nylas import Client
159 | 
160 | nylas = Client(
161 |     os.environ.get('NYLAS_API_KEY'),
162 |     os.environ.get('NYLAS_API_URI')
163 | )
164 | 
165 | grant_id = os.environ.get("NYLAS_GRANT_ID")
166 | 
167 | folder = nylas.folders.create(
168 |     grant_id,
169 |     request_body={
170 |       "name": 'New Folder',
171 |       "parent": None,
172 |     }
173 | )
174 | 
175 | print(folder)
176 | ```
177 | 
178 | ```Ruby
179 | require 'nylas'
180 | 
181 | nylas = Nylas::Client.new(
182 |     api_key: "<NYLAS_API_KEY>"
183 | )
184 | 
185 | request_body = {
186 |   name: "My Custom label"
187 | }
188 | 
189 | folder = nylas.folders.create(identifier: "<NYLAS_GRANT_ID>",
190 |     request_body: request_body)
191 | ```
192 | 
193 | ```API
194 | curl --location 'https://api.us.nylas.com/v3/grants/<NYLAS_GRANT_ID>/folders' \
195 |   --header 'Content-Type: application/json' \
196 |   --header 'Accept: application/json' \
197 |   --header 'Authorization: Bearer <NYLAS_API_KEY>' \
198 |   --data '{
199 |     "text_color": "#000000",
200 |     "name": "new folder",
201 |     "background_color": "#434343"
202 |   }'
203 | ```
204 | 
205 | ### Delete a specific folder
206 | 
207 | `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-)
208 | 
209 | ```Node
210 | import 'dotenv/config'
211 | import Nylas from 'nylas'
212 | 
213 | const NylasConfig = {
214 |   apiKey: process.env.NYLAS_API_KEY,
215 |   apiUri: process.env.NYLAS_API_URI,
216 | }
217 | 
218 | const nylas = new Nylas(NylasConfig)
219 | const identifier = process.env.NYLAS_GRANT_ID
220 | const folderId = process.env.FOLDER_ID
221 | 
222 | const deleteFolder = async () => {
223 |   try {
224 |     await nylas.folders.destroy({ identifier, folderId })
225 |     console.log(`Folder with ID ${folderId} deleted successfully.`)
226 |   } catch (error) {
227 |     console.error(`Error deleting folder with ID ${folderId}:`, error)
228 |   }
229 | }
230 | 
231 | deleteFolder()
232 | ```
233 | 
234 | ```Java
235 | import com.nylas.NylasClient;
236 | import com.nylas.models.*;
237 | 
238 | public class DestroyLabel {
239 |   public static void main(String[] args) throws NylasSdkTimeoutError, NylasApiError {
240 |     NylasClient nylas = new NylasClient.Builder("<NYLAS_API_KEY>").build();
241 |     DeleteResponse folder = nylas.folders().destroy("<NYLAS_GRANT_ID>", "<FOLDER_ID>");
242 |   }
243 | }
244 | ```
245 | 
246 | ```Python
247 | from dotenv import load_dotenv
248 | load_dotenv()
249 | 
250 | import os
251 | import sys
252 | from nylas import Client
253 | 
254 | nylas = Client(
255 |     os.environ.get('NYLAS_API_KEY'),
256 |     os.environ.get('NYLAS_API_URI')
257 | )
258 | 
259 | grant_id = os.environ.get("NYLAS_GRANT_ID")
260 | folder_id = os.environ.get("FOLDER_ID")
261 | 
262 | request = nylas.folders.destroy(
263 |     grant_id,
264 |     folder_id,
265 | )
266 | 
267 | print(request)
268 | ```
269 | 
270 | ```Ruby
271 | require 'nylas'
272 | 
273 | nylas = Nylas::Client.new(
274 |     api_key: "<NYLAS_API_KEY>"
275 | )
276 | 
277 | folder, _ = nylas.folders.destroy(identifier: "<NYLAS_GRANT_ID>",
278 |     folder_id: "<FOLDER_ID>")
279 | 
280 | puts folder
281 | ```
282 | 
283 | ```API
284 | curl --location --request DELETE 'https://api.us.nylas.com/v3/grants/<NYLAS_GRANT_ID>/folders/<FOLDER_ID>' \
285 |   --header 'Accept: application/json' \
286 |   --header 'Authorization: Bearer <NYLAS_API_KEY>'
287 | ```
288 | 
```

--------------------------------------------------------------------------------
/nylas-code-samples/Calendar/Events/read.md:
--------------------------------------------------------------------------------

```markdown
  1 | ### Get all events
  2 | 
  3 | GET /events → GET /v3/grants/{grant_id}/events
  4 | 
  5 | ```Node
  6 | import 'dotenv/config'
  7 | import Nylas from 'nylas'
  8 | 
  9 | const NylasConfig = {
 10 |   apiKey: process.env.NYLAS_API_KEY,
 11 |   apiUri: process.env.NYLAS_API_URI
 12 | }
 13 | 
 14 | const nylas = new Nylas(NylasConfig)
 15 | 
 16 | async function fetchAllEventsFromCalendar() { 
 17 |   try {
 18 |     const events = await nylas.events.list({
 19 |       identifier: process.env.NYLAS_GRANT_ID,
 20 |       queryParams: {
 21 |         calendarId: process.env.CALENDAR_ID,
 22 |       }
 23 |     })
 24 |   
 25 |     console.log('Events:', events)
 26 |   } catch (error) {
 27 |     console.error('Error fetching calendars:', error)
 28 |   }
 29 | }
 30 | 
 31 | fetchAllEventsFromCalendar()
 32 | ```
 33 | 
 34 | ```Java
 35 | import com.nylas.NylasClient;
 36 | import com.nylas.models.When;
 37 | 
 38 | import com.nylas.models.*;
 39 | import java.text.SimpleDateFormat;
 40 | import java.util.List;
 41 | import java.util.Objects;
 42 | 
 43 | public class read_calendar_events {
 44 |   public static void main(String[] args) throws NylasSdkTimeoutError, NylasApiError {
 45 |     NylasClient nylas = new NylasClient.Builder("<NYLAS_API_KEY>").build();
 46 | 
 47 |     ListEventQueryParams listEventQueryParams = new ListEventQueryParams.Builder("<CALENDAR_ID>").build();
 48 | 
 49 |     List<Event> events = nylas.events().list("<NYLAS_GRANT_ID>", listEventQueryParams).getData();
 50 | 
 51 |     for (Event event : events) {
 52 |       System.out.print("Id: " + event.getId() + " | ");
 53 |       System.out.print("Title: " + event.getTitle());
 54 | 
 55 |       switch (Objects.requireNonNull(event.getWhen().getObject()).getValue()) {
 56 |         case "datespan" -> {
 57 |           When.Datespan date = (When.Datespan) event.getWhen();
 58 | 
 59 |           System.out.print(" | The date of the event is from: " + 
 60 |               date.getStartDate() + " to " + 
 61 |               date.getEndDate());
 62 |         }
 63 |         case "date" -> {
 64 |           When.Date date = (When.Date) event.getWhen();
 65 |           
 66 |           System.out.print(" | The date of the event is: " +date.getDate());
 67 |         }
 68 |         case "timespan" -> {
 69 |           When.Timespan timespan = (When.Timespan) event.getWhen();
 70 | 
 71 |           String initDate = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").
 72 |           format(new java.util.Date((timespan.getStartTime() * 1000L)));
 73 | 
 74 |           String endDate = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").
 75 |           format(new java.util.Date((timespan.getEndTime() * 1000L)));
 76 | 
 77 |           System.out.print(" | The time of the event is from: " + 
 78 |           initDate + " to " + endDate);
 79 |         }
 80 |       }
 81 | 
 82 |       System.out.print(" | Participants: ");
 83 | 
 84 |       for(Participant participant : event.getParticipants()){
 85 |         System.out.print(" Email: " + participant.getEmail() +
 86 |             " Name: " + participant.getName() +
 87 |             " Status: " + participant.getStatus());
 88 |       }
 89 | 
 90 |       System.out.println("\n");
 91 |     }
 92 |   }
 93 | }
 94 | ```
 95 | 
 96 | ```Python
 97 | from dotenv import load_dotenv
 98 | load_dotenv()
 99 | 
100 | import os
101 | import sys
102 | from nylas import Client
103 | 
104 | nylas = Client(
105 |     os.environ.get('NYLAS_API_KEY'),
106 |     os.environ.get('NYLAS_API_URI')
107 | )
108 | 
109 | grant_id = os.environ.get("NYLAS_GRANT_ID")
110 | 
111 | events = nylas.events.list(
112 |     grant_id,
113 |     query_params={
114 |       "calendar_id": os.environ.get("CALENDAR_ID")
115 |     }
116 | )
117 | 
118 | print(events)
119 | ```
120 | 
121 | ```Ruby
122 | # Load gems
123 | require 'nylas'
124 | 
125 | # Initialize Nylas client
126 | nylas = Nylas::Client.new(
127 |  api_key: "<NYLAS_API_KEY>"
128 | )
129 | 
130 | # Query parameters
131 | query_params = {
132 |     calendar_id: "<NYLAS_GRANT_ID>"
133 | }
134 | 
135 | # Read events from our main calendar in the specified date and time
136 | events, _request_ids = nylas.events.list(identifier: "<NYLAS_GRANT_ID>", 
137 | query_params: query_params)
138 | 
139 | # Loop events
140 | events.each {|event|
141 |  case event[:when][:object]
142 |   when 'timespan'
143 |    start_time = Time.at(event[:when][:start_time]).strftime("%d/%m/%Y at %H:%M:%S")
144 |    end_time = Time.at(event[:when][:end_time]).strftime("%d/%m/%Y at %H:%M:%S")
145 |    event_date = "The time of the event is from: #{start_time} to #{end_time}"
146 |   when 'datespan'
147 |    start_time = event[:when][:start_date]
148 |    end_time = event[:when][:end_date]
149 |    event_date = "The date of the event is from: #{start_time} to: #{end_time}"
150 |   when 'date'
151 |    start_time = event[:when][:date]
152 |    event_date = "The date of the event is: #{start_time}"
153 |   end
154 |   event[:participants].each {|participant|
155 |    participant_details += "Email: #{participant[:email]} " \
156 |    "Name: #{participant[:name]} Status: #{participant[:status]} - "
157 |   }
158 |   print "Id: #{event[:id]} | Title: #{event[:title]} | #{event_date} | " 
159 |   puts "Participants: #{participant_details.chomp(' - ')}"
160 |   puts "\n"
161 | }
162 | ```
163 | 
164 | ```API
165 | curl --request GET \
166 |   --url https://api.us.nylas.com/v3/grants/<NYLAS_GRANT_ID>/events?calendar_id=<CALENDAR_ID>&start=<EPOCH_TIMESTAMP>&end=<EPOCH_TIMESTAMP> \
167 |   --header 'Accept: application/json' \
168 |   --header 'Authorization: Bearer <NYLAS_API_KEY>' \
169 |   --header 'Content-Type: application/json'
170 | ```
171 | 
172 | ### Get a specific event
173 | 
174 | GET /events/{id} → GET /v3/grants/{grant_id}/events/{event_id}
175 | 
176 | ```Node
177 | import 'dotenv/config'
178 | import Nylas from 'nylas'
179 | 
180 | const NylasConfig = {
181 |  apiKey: process.env.NYLAS_API_KEY,
182 |  apiUri: process.env.NYLAS_API_URI,
183 | }
184 | 
185 | const nylas = new Nylas(NylasConfig)
186 | 
187 | async function fetchEventById() { 
188 |   try {
189 |     const events = await nylas.events.find({
190 |       identifier: process.env.NYLAS_GRANT_ID,
191 |       eventId: process.env.EVENT_ID,
192 |       queryParams: {
193 |         calendarId: process.env.CALENDAR_ID,
194 |       }
195 |     })
196 |   
197 |     console.log('Events:', events)
198 |   } catch (error) {
199 |     console.error('Error fetching calendars:', error)
200 |   }
201 | }
202 | 
203 | fetchEventById()
204 | ```
205 | 
206 | ```Java
207 | // Import Nylas packages
208 | import com.nylas.NylasClient;
209 | import com.nylas.models.When;
210 | import com.nylas.models.*;
211 | 
212 | public class ReadEvent {
213 |   public static void main(String[] args) throws NylasSdkTimeoutError, NylasApiError {
214 |     NylasClient nylas = new NylasClient.Builder("<NYLAS_API_KEY>").build();
215 |     FindEventQueryParams queryParams = new FindEventQueryParams("<CALENDAR_ID>");
216 |     Response<Event> event = nylas.events().find("<NYLAS_GRANT_ID>", "<EVENT_ID>", queryParams);
217 |   }
218 | }
219 | ```
220 | 
221 | ```Python
222 | from dotenv import load_dotenv
223 | load_dotenv()
224 | 
225 | import os
226 | import sys
227 | from nylas import Client
228 | 
229 | nylas = Client(
230 |     os.environ.get('NYLAS_API_KEY'),
231 |     os.environ.get('NYLAS_API_URI')
232 | )
233 | 
234 | grant_id = os.environ.get("NYLAS_GRANT_ID")
235 | event_id = os.environ.get("EVENT_ID")
236 | calendar_id = os.environ.get("CALENDAR_ID")
237 | 
238 | event = nylas.events.find(
239 |   grant_id,
240 |   event_id,
241 |   query_params={
242 |     "calendar_id": calendar_id
243 |   }
244 | )
245 | 
246 | print(event)
247 | ```
248 | 
249 | ```Ruby
250 | # Load gems
251 | require 'nylas'
252 | 
253 | # Initialize Nylas client
254 | nylas = Nylas::Client.new(
255 |  api_key: "<NYLAS_API_KEY>"
256 | )
257 | 
258 | # Query parameters
259 | query_params = {
260 |     calendar_id: "<NYLAS_GRANT_ID>"
261 | }
262 | 
263 | # Read events from our main calendar in the specified date and time
264 | events, _request_ids = nylas.events.find(identifier: "<NYLAS_GRANT_ID>", 
265 | event_id: "<EVENT_ID>",  query_params: query_params)
266 | 
267 | # Loop events
268 | events.each {|event|
269 |  puts event
270 | }
271 | ```
272 | 
273 | ```API
274 | curl --request GET \
275 |   --url https://api.us.nylas.com/v3/grants/<NYLAS_GRANT_ID>/events/<EVENT_ID>?calendar_id=<CALENDAR_ID> \
276 |   --header 'Accept: application/json' \
277 |   --header 'Authorization: Bearer <NYLAS_API_KEY>' \
278 |   --header 'Content-Type: application/json'
279 | ```
280 | 
```

--------------------------------------------------------------------------------
/nylas-code-samples/Calendar/Calendars/write.md:
--------------------------------------------------------------------------------

```markdown
  1 | ### Update a specific calendar
  2 | 
  3 | PUT /calendars/{id} → PUT /v3/grants/{grant_id}/calendars/{calendar_id}
  4 | 
  5 | ```Node
  6 | import 'dotenv/config'
  7 | import Nylas from 'nylas'
  8 | 
  9 | const NylasConfig = {
 10 |   apiKey: process.env.NYLAS_API_KEY,
 11 |   apiUri: process.env.NYLAS_API_URI,
 12 | }
 13 | 
 14 | const nylas = new Nylas(NylasConfig)
 15 | 
 16 | async function updateCalendar() {
 17 |   try {
 18 |     const calendar = await nylas.calendars.update({
 19 |       identifier: process.env.NYLAS_GRANT_ID,
 20 |       calendarId: process.env.CALENDAR_ID,
 21 |       requestBody: {
 22 |         name: 'Nylas DevRel Calendar',
 23 |         description: 'Nylas Developer Relations',
 24 |       }
 25 |     })
 26 | 
 27 |     console.log('Updated Calendar:', calendar)
 28 |   } catch (error) {
 29 |     console.error('Error to update calendar:', error)
 30 |   }
 31 | }
 32 | 
 33 | updateCalendar()
 34 | ```
 35 | 
 36 | ```Java
 37 | import com.nylas.NylasClient;
 38 | import com.nylas.models.*;
 39 | 
 40 | public class UpdateCalendar {
 41 |   public static void main(String[] args) throws NylasSdkTimeoutError, NylasApiError {
 42 |     NylasClient nylas = new NylasClient.Builder("<NYLAS_API_KEY>").build();
 43 |       
 44 |     UpdateCalendarRequest requestBody = new UpdateCalendarRequest.Builder().
 45 |         name("My New Calendar").
 46 |         description("Description of my new calendar").
 47 |         location("Location description").
 48 |         timezone("America/Los_Angeles").
 49 |         build();
 50 |       
 51 |     Response<Calendar> calendar = nylas.calendars().update(
 52 |         "<CALENDAR_ID>",
 53 |         "<CALENDAR_ID>", 
 54 |         requestBody);
 55 | 
 56 |     System.out.println(calendar.getData());        
 57 |   }
 58 | }
 59 | ```
 60 | 
 61 | ```Python
 62 | from dotenv import load_dotenv
 63 | load_dotenv()
 64 | 
 65 | import os
 66 | import sys
 67 | from nylas import Client
 68 | 
 69 | nylas = Client(
 70 |     os.environ.get('NYLAS_API_KEY'),
 71 |     os.environ.get('NYLAS_API_URI')
 72 | )
 73 | 
 74 | grant_id = os.environ.get("NYLAS_GRANT_ID")
 75 | 
 76 | calendar = nylas.calendars.update(
 77 |     grant_id,
 78 |     calendar_id=os.environ.get("CALENDAR_ID"),
 79 |     request_body={
 80 |       "name": 'Nylas DevRel Calendar',
 81 |       "description": 'Nylas Developer Relations'
 82 |     }
 83 | )
 84 | 
 85 | print(calendar)
 86 | ```
 87 | 
 88 | ```Ruby
 89 | require 'nylas' 
 90 | 
 91 | nylas = Nylas::Client.new(api_key: "<NYLAS_API_KEY>")
 92 | 
 93 | request_body = {
 94 |  "name": "\"New Test Calendar (changed)\"",
 95 |  "description": "\"this calendar has been updated!\"",
 96 | }
 97 | 
 98 | calendar, _request_ids = nylas.calendars.update(
 99 |   identifier: "<NYLAS_GRANT_ID>", 
100 |   calendar_id: "<CALENDAR_ID", 
101 |   request_body: request_body)
102 | 
103 | puts calendar
104 | ```
105 | 
106 | ```API
107 | curl --request PUT \
108 |   --url https://api.us.nylas.com/v3/grants/<NYLAS_GRANT_ID>/calendars/<CALENDAR_ID> \
109 |   --header 'Accept: application/json' \
110 |   --header 'Authorization: Bearer <NYLAS_API_KEY>' \
111 |   --header 'Content-Type: application/json' \
112 |   --data '{
113 |     "name": "My New Calendar",
114 |     "description": "Description of my new calendar",
115 |     "location": "Location description",
116 |     "timezone": "America/Los_Angeles"
117 | }'
118 | ```
119 | 
120 | ### Create a specific calendar
121 | 
122 | POST /calendars → POST /v3/grants/{grant_id}/calendars
123 | 
124 | ```Node
125 | import 'dotenv/config'
126 | import Nylas from 'nylas'
127 | 
128 | const NylasConfig = {
129 |   apiKey: process.env.NYLAS_API_KEY,
130 |   apiUri: process.env.NYLAS_API_URI,
131 | }
132 | 
133 | const nylas = new Nylas(NylasConfig)
134 | 
135 | async function createCalendar() {
136 |   try {
137 |     const calendar = await nylas.calendars.create({
138 |       identifier: process.env.NYLAS_GRANT_ID,
139 |       requestBody: {
140 |         name: 'Nylas DevRel',
141 |         description: 'Nylas Developer Relations',
142 |       }
143 |     })
144 | 
145 |     console.log('Calendar:', calendar)
146 |   } catch (error) {
147 |     console.error('Error to create calendar:', error)
148 |   }
149 | }
150 | 
151 | createCalendar()
152 | ```
153 | 
154 | ```Java
155 | import com.nylas.NylasClient;
156 | import com.nylas.models.*;
157 | import java.util.Map;
158 | 
159 | public class CreateCalendar {
160 |   public static void main(String[] args) throws NylasSdkTimeoutError, NylasApiError {
161 |     NylasClient nylas = new NylasClient.Builder("<NYLAS_API_KEY>").build();
162 | 
163 |     CreateCalendarRequest requestBody = new CreateCalendarRequest(
164 |         "My New Calendar",
165 |         "Description of my new calendar",
166 |         "Location description",
167 |         "America/Toronto",
168 |         Map.of("key1", "This is my metadata"));
169 | 
170 |     Response<Calendar> calendar = nylas.calendars().
171 |         create("<NYLAS_GRANT_ID>", requestBody);
172 | 
173 |     System.out.println(calendar.getData());
174 |   }
175 | }
176 | ```
177 | 
178 | ```Python
179 | from dotenv import load_dotenv
180 | load_dotenv()
181 | 
182 | import os
183 | import sys
184 | from nylas import Client
185 | 
186 | nylas = Client(
187 |     os.environ.get('NYLAS_API_KEY'),
188 |     os.environ.get('NYLAS_API_URI')
189 | )
190 | 
191 | grant_id = os.environ.get("NYLAS_GRANT_ID")
192 | 
193 | calendar = nylas.calendars.create(
194 |     grant_id,
195 |     request_body={
196 |       "name": 'Nylas DevRel',
197 |       "description": 'Nylas Developer Relations'
198 |     }
199 | )
200 | 
201 | print(calendar)
202 | ```
203 | 
204 | ```Ruby
205 | require 'nylas' 
206 | 
207 | nylas = Nylas::Client.new(api_key: "<NYLAS_API_KEY>")
208 | 
209 | query_params = {
210 |  calendar_id: "<CALENDAR_ID>"
211 | }
212 | 
213 | request_body = {
214 |  "name": "My New Calendar",
215 |  "description": "Description of my new calendar",
216 |  "location": "Location description",
217 |  "timezone": "America/Toronto",
218 |  "metadata": { "key1":"This is my metadata" }
219 | }
220 | 
221 | calendar, _request_ids = nylas.calendars.create(
222 |   identifier: "<NYLAS_GRANT_ID>", 
223 |   request_body: request_body)
224 | 
225 | puts calendar
226 | ```
227 | 
228 | ```API
229 | curl --request POST \
230 |   --url https://api.us.nylas.com/v3/grants/<NYLAS_GRANT_ID>/calendars \
231 |   --header 'Accept: application/json' \
232 |   --header 'Authorization: Bearer <NYLAS_API_KEY>' \
233 |   --header 'Content-Type: application/json' \
234 |   --data '{
235 |     "name": "My New Calendar",
236 |     "description": "Description of my new calendar",
237 |     "location": "Location description",
238 |     "timezone": "America/Los_Angeles"
239 | }'
240 | ```
241 | 
242 | ### Delete a specific calendar
243 | 
244 | DELETE /calendars/{id} → DELETE /v3/grants/{grant_id}/calendars/{calendar_id}
245 | 
246 | ```Node
247 | import 'dotenv/config'
248 | import Nylas from 'nylas'
249 | 
250 | const NylasConfig = {
251 |  apiKey: process.env.NYLAS_API_KEY,
252 |  apiUri: process.env.NYLAS_API_URI,
253 | }
254 | 
255 | const nylas = new Nylas(NylasConfig)
256 | 
257 | async function deleteCalendar() {
258 |   try {
259 |     const calendar = await nylas.calendars.destroy({
260 |       identifier: process.env.NYLAS_GRANT_ID,
261 |       calendarId: process.env.CALENDAR_ID,
262 |     })
263 | 
264 |     console.log('Calendar:', calendar)
265 |   } catch (error) {
266 |     console.error('Error to create calendar:', error)
267 |   }
268 | }
269 | 
270 | deleteCalendar()
271 | ```
272 | 
273 | ```Java
274 | // Import packages
275 | import com.nylas.NylasClient;
276 | import com.nylas.models.*;
277 | 
278 | public class DeleteCalendar {
279 |   public static void main(String[] args) throws NylasSdkTimeoutError, NylasApiError {
280 |     NylasClient nylas = new NylasClient.Builder("<NYLAS_API_KEY>").build();
281 | 
282 |     // Delete the requested calendar
283 |     try {
284 |       nylas.calendars().destroy("<NYLAS_GRANT_ID>", "<CALENDAR_ID>");
285 | 
286 |       System.out.println("Deleted successfully");
287 |     }
288 |     catch(Exception e) {
289 |       System.out.println("There was an error " + e);
290 |     }
291 |   }
292 | }
293 | ```
294 | 
295 | ```Python
296 | from dotenv import load_dotenv
297 | load_dotenv()
298 | 
299 | import os
300 | import sys
301 | from nylas import Client
302 | 
303 | nylas = Client(
304 |     os.environ.get('NYLAS_API_KEY'),
305 |     os.environ.get('NYLAS_API_URI')
306 | )
307 | 
308 | grant_id = os.environ.get("NYLAS_GRANT_ID")
309 | calendar_id = os.environ.get("CALENDAR_ID")
310 | 
311 | request = nylas.calendars.destroy(
312 |   grant_id,
313 |   calendar_id,
314 | )
315 | 
316 | print(request)
317 | ```
318 | 
319 | ```Ruby
320 | require 'nylas'
321 | 
322 | nylas = Nylas::Client.new(
323 |     api_key: "<NYLAS_API_KEY>"
324 | )
325 | 
326 | calendar, = nylas.calendars.destroy(identifier: "<NYLAS_GRANT_ID>",
327 | calendar_id: "<CALENDAR_ID")
328 | 
329 | puts calendar
330 | ```
331 | 
332 | ```API
333 | curl --request DELETE \
334 |   --url https://api.us.nylas.com/v3/grants/<NYLAS_GRANT_ID>/calendars/<CALENDAR_ID> \
335 |   --header 'Accept: application/json' \
336 |   --header 'Authorization: Bearer <NYLAS_API_KEY>' \
337 |   --header 'Content-Type: application/json'
338 | ```
339 | 
```

--------------------------------------------------------------------------------
/src/prompts/index.ts:
--------------------------------------------------------------------------------

```typescript
  1 | import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
  2 | import { z } from "zod";
  3 | 
  4 | /**
  5 |  * Register all prompts with the MCP server
  6 |  */
  7 | export function registerPrompts(server: McpServer) {
  8 |   // Register general prompts
  9 |   registerGeneralPrompts(server);
 10 |   
 11 |   // Register feature-specific prompts
 12 |   registerFeaturePrompts(server);
 13 |   
 14 |   // Register integration prompts
 15 |   registerIntegrationPrompts(server);
 16 | }
 17 | 
 18 | /**
 19 |  * Register general prompts for Nylas API
 20 |  */
 21 | function registerGeneralPrompts(server: McpServer) {
 22 |   // Prompt for getting started with Nylas
 23 |   server.prompt(
 24 |     "nylas-getting-started",
 25 |     {},
 26 |     () => ({
 27 |       messages: [
 28 |         {
 29 |           role: "user",
 30 |           content: {
 31 |             type: "text",
 32 |             text: `I want to integrate with the Nylas API. Can you help me get started and explain the basic steps?`
 33 |           }
 34 |         }
 35 |       ]
 36 |     })
 37 |   );
 38 |   
 39 |   // Prompt for understanding authentication
 40 |   server.prompt(
 41 |     "nylas-auth-guide",
 42 |     {},
 43 |     () => ({
 44 |       messages: [
 45 |         {
 46 |           role: "user",
 47 |           content: {
 48 |             type: "text",
 49 |             text: `Explain how authentication works with the Nylas API. What are the steps to authenticate users and what OAuth flow should I implement?`
 50 |           }
 51 |         }
 52 |       ]
 53 |     })
 54 |   );
 55 |   
 56 |   // Prompt for API best practices
 57 |   server.prompt(
 58 |     "nylas-api-best-practices",
 59 |     {},
 60 |     () => ({
 61 |       messages: [
 62 |         {
 63 |           role: "user",
 64 |           content: {
 65 |             type: "text",
 66 |             text: `What are the best practices for working with the Nylas API? Please include information about rate limits, error handling, and efficient API usage.`
 67 |           }
 68 |         }
 69 |       ]
 70 |     })
 71 |   );
 72 | }
 73 | 
 74 | /**
 75 |  * Register prompts for specific Nylas API features
 76 |  */
 77 | function registerFeaturePrompts(server: McpServer) {
 78 |   // Prompt for email integration
 79 |   server.prompt(
 80 |     "nylas-email-integration",
 81 |     {
 82 |       feature: z.enum(["read", "send", "threads", "search", "attachments", "drafts"]).optional()
 83 |     },
 84 |     ({ feature }) => {
 85 |       let message = `I want to integrate email functionality using the Nylas API.`;
 86 |       
 87 |       if (feature) {
 88 |         switch (feature) {
 89 |           case "read":
 90 |             message += ` Specifically, I need to implement the ability to read and display emails from a user's inbox. How should I approach this?`;
 91 |             break;
 92 |           case "send":
 93 |             message += ` Specifically, I need to implement the ability to send emails through my application. How should I approach this?`;
 94 |             break;
 95 |           case "threads":
 96 |             message += ` Specifically, I need to implement conversation threading to group related emails. How should I approach this?`;
 97 |             break;
 98 |           case "search":
 99 |             message += ` Specifically, I need to implement email search functionality. How should I approach this?`;
100 |             break;
101 |           case "attachments":
102 |             message += ` Specifically, I need to implement handling of email attachments (both uploading and downloading). How should I approach this?`;
103 |             break;
104 |           case "drafts":
105 |             message += ` Specifically, I need to implement email draft functionality. How should I approach this?`;
106 |             break;
107 |         }
108 |       } else {
109 |         message += ` What are the key features I should know about and how should I implement them?`;
110 |       }
111 |       
112 |       return {
113 |         messages: [
114 |           {
115 |             role: "user",
116 |             content: {
117 |               type: "text",
118 |               text: message
119 |             }
120 |           }
121 |         ]
122 |       };
123 |     }
124 |   );
125 |   
126 |   // Prompt for calendar integration
127 |   server.prompt(
128 |     "nylas-calendar-integration",
129 |     {
130 |       feature: z.enum(["events", "availability", "recurring"]).optional()
131 |     },
132 |     ({ feature }) => {
133 |       let message = `I want to integrate calendar functionality using the Nylas API.`;
134 |       
135 |       if (feature) {
136 |         switch (feature) {
137 |           case "events":
138 |             message += ` Specifically, I need to implement the ability to create, read, update, and delete calendar events. How should I approach this?`;
139 |             break;
140 |           case "availability":
141 |             message += ` Specifically, I need to implement availability checking and scheduling. How should I approach this?`;
142 |             break;
143 |           case "recurring":
144 |             message += ` Specifically, I need to implement recurring events. How should I approach this?`;
145 |             break;
146 |         }
147 |       } else {
148 |         message += ` What are the key features I should know about and how should I implement them?`;
149 |       }
150 |       
151 |       return {
152 |         messages: [
153 |           {
154 |             role: "user",
155 |             content: {
156 |               type: "text",
157 |               text: message
158 |             }
159 |           }
160 |         ]
161 |       };
162 |     }
163 |   );
164 |   
165 |   // Prompt for contacts integration
166 |   server.prompt(
167 |     "nylas-contacts-integration",
168 |     {},
169 |     () => ({
170 |       messages: [
171 |         {
172 |           role: "user",
173 |           content: {
174 |             type: "text",
175 |             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?`
176 |           }
177 |         }
178 |       ]
179 |     })
180 |   );
181 |   
182 |   // Prompt for webhooks
183 |   server.prompt(
184 |     "nylas-webhooks-guide",
185 |     {},
186 |     () => ({
187 |       messages: [
188 |         {
189 |           role: "user",
190 |           content: {
191 |             type: "text",
192 |             text: `I want to implement webhooks with the Nylas API to get real-time updates. How do I set up and handle webhooks properly?`
193 |           }
194 |         }
195 |       ]
196 |     })
197 |   );
198 | }
199 | 
200 | /**
201 |  * Register prompts for integration scenarios
202 |  */
203 | function registerIntegrationPrompts(server: McpServer) {
204 |   // Prompt for debugging common issues
205 |   server.prompt(
206 |     "nylas-debug-common-issues",
207 |     {},
208 |     () => ({
209 |       messages: [
210 |         {
211 |           role: "user",
212 |           content: {
213 |             type: "text",
214 |             text: `What are the most common issues developers face when integrating with the Nylas API, and how can I debug and resolve them?`
215 |           }
216 |         }
217 |       ]
218 |     })
219 |   );
220 |   
221 |   // Prompt for implementing specific integration scenario
222 |   server.prompt(
223 |     "nylas-integration-scenario",
224 |     {
225 |       scenario: z.enum([
226 |         "email-client", 
227 |         "calendar-booking", 
228 |         "crm-integration", 
229 |         "customer-support", 
230 |         "automated-workflows"
231 |       ])
232 |     },
233 |     ({ scenario }) => {
234 |       let message = "";
235 |       
236 |       switch (scenario) {
237 |         case "email-client":
238 |           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?`;
239 |           break;
240 |         case "calendar-booking":
241 |           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?`;
242 |           break;
243 |         case "crm-integration":
244 |           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?`;
245 |           break;
246 |         case "customer-support":
247 |           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?`;
248 |           break;
249 |         case "automated-workflows":
250 |           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?`;
251 |           break;
252 |       }
253 |       
254 |       return {
255 |         messages: [
256 |           {
257 |             role: "user",
258 |             content: {
259 |               type: "text",
260 |               text: message
261 |             }
262 |           }
263 |         ]
264 |       };
265 |     }
266 |   );
267 |   
268 |   // Prompt for SDK usage examples
269 |   server.prompt(
270 |     "nylas-sdk-examples",
271 |     {
272 |       language: z.enum(["node", "python", "java", "ruby"])
273 |     },
274 |     ({ language }) => {
275 |       const normalizedLanguage = language.charAt(0).toUpperCase() + language.slice(1);
276 |       
277 |       return {
278 |         messages: [
279 |           {
280 |             role: "user",
281 |             content: {
282 |               type: "text",
283 |               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.`
284 |             }
285 |           }
286 |         ]
287 |       };
288 |     }
289 |   );
290 |   
291 |   // Prompt for migrating from v2 to v3
292 |   server.prompt(
293 |     "nylas-v3-migration",
294 |     {},
295 |     () => ({
296 |       messages: [
297 |         {
298 |           role: "user",
299 |           content: {
300 |             type: "text",
301 |             text: `I'm migrating from Nylas API v2 to v3. What are the key differences and how should I update my code?`
302 |           }
303 |         }
304 |       ]
305 |     })
306 |   );
307 | }
```

--------------------------------------------------------------------------------
/src/resources/code-samples.ts:
--------------------------------------------------------------------------------

```typescript
  1 | import { McpServer, ResourceTemplate } from "@modelcontextprotocol/sdk/server/mcp.js";
  2 | import fs from 'fs/promises';
  3 | import path from 'path';
  4 | 
  5 | /**
  6 |  * Register code sample resources for the Nylas API
  7 |  */
  8 | export async function registerCodeSamplesResources(server: McpServer) {
  9 |   // Path to the code samples directory
 10 |   const codeSamplesPath = path.join(process.cwd(), 'nylas-code-samples');
 11 |   
 12 |   // Register a resource for each code sample category
 13 |   await registerCategories(server, codeSamplesPath);
 14 |   
 15 |   // Register a resource for specific code samples by path
 16 |   server.resource(
 17 |     "code-sample-by-path",
 18 |     new ResourceTemplate("nylas://code-samples/{path*}", { list: undefined }),
 19 |     async (uri, { path: samplePath }) => {
 20 |       try {
 21 |         // Replace slashes in the path parameter with the correct path separator
 22 |         const normalizedPath = typeof samplePath === 'string' ? samplePath.replace(/\//g, path.sep) : samplePath.join(path.sep);
 23 |         
 24 |         // Determine if it's a directory or a file
 25 |         const fullPath = path.join(codeSamplesPath, normalizedPath);
 26 |         const stats = await fs.stat(fullPath);
 27 |         
 28 |         if (stats.isDirectory()) {
 29 |           // If it's a directory, list the contents
 30 |           const files = await fs.readdir(fullPath);
 31 |           const mdFiles = files.filter(file => file.endsWith('.md'));
 32 |           
 33 |           // If there's an index.md file, use that
 34 |           if (mdFiles.includes('index.md')) {
 35 |             const indexContent = await fs.readFile(path.join(fullPath, 'index.md'), 'utf-8');
 36 |             return {
 37 |               contents: [{
 38 |                 uri: uri.href,
 39 |                 text: indexContent,
 40 |                 mimeType: "text/markdown"
 41 |               }]
 42 |             };
 43 |           }
 44 |           
 45 |           // Otherwise, create a directory listing
 46 |           const listing = `# ${path.basename(normalizedPath)} Code Samples\n\n` +
 47 |             `Available samples:\n\n` +
 48 |             mdFiles.map(file => `- [${file.replace('.md', '')}](nylas://code-samples/${samplePath}/${file})`).join('\n');
 49 |           
 50 |           return {
 51 |             contents: [{
 52 |               uri: uri.href,
 53 |               text: listing,
 54 |               mimeType: "text/markdown"
 55 |             }]
 56 |           };
 57 |         } else {
 58 |           // If it's a file, return its contents
 59 |           const content = await fs.readFile(fullPath, 'utf-8');
 60 |           return {
 61 |             contents: [{
 62 |               uri: uri.href,
 63 |               text: content,
 64 |               mimeType: "text/markdown"
 65 |             }]
 66 |           };
 67 |         }
 68 |       } catch (error) {
 69 |         // If the file doesn't exist, return a not found message
 70 |         return {
 71 |           contents: [{
 72 |             uri: uri.href,
 73 |             text: `# Not Found\n\nThe requested code sample "${samplePath}" was not found.`,
 74 |             mimeType: "text/markdown"
 75 |           }]
 76 |         };
 77 |       }
 78 |     }
 79 |   );
 80 |   
 81 |   // Register a resource to get code samples by language
 82 |   server.resource(
 83 |     "code-samples-by-language",
 84 |     new ResourceTemplate("nylas://code-samples/language/{language}/{category?}", { list: undefined }),
 85 |     async (uri, { language, category }) => {
 86 |       try {
 87 |         // Normalize language name
 88 |         const normalizedLanguage = normalizeLanguageName(typeof language === 'string' ? language : language[0]);
 89 |         
 90 |         // If a category is specified, search only in that category
 91 |         if (category) {
 92 |           const categoryPath = path.join(codeSamplesPath, typeof category === 'string' ? category : category[0]);
 93 |           const samples = await findCodeSamplesByLanguage(categoryPath, normalizedLanguage);
 94 |           
 95 |           return {
 96 |             contents: [{
 97 |               uri: uri.href,
 98 |               text: formatCodeSamplesForLanguage(normalizedLanguage, typeof category === 'string' ? category : category[0], samples),
 99 |               mimeType: "text/markdown"
100 |             }]
101 |           };
102 |         } else {
103 |           // Search in all categories
104 |           const categories = await fs.readdir(codeSamplesPath);
105 |           
106 |           // Store all found code samples
107 |           const allSamples: Record<string, string[]> = {};
108 |           
109 |           // Find code samples in each category
110 |           for (const cat of categories) {
111 |             const categoryPath = path.join(codeSamplesPath, cat);
112 |             
113 |             // Skip if not a directory
114 |             const stats = await fs.stat(categoryPath);
115 |             if (!stats.isDirectory()) continue;
116 |             
117 |             const samples = await findCodeSamplesByLanguage(categoryPath, normalizedLanguage);
118 |             if (samples.length > 0) {
119 |               allSamples[cat] = samples;
120 |             }
121 |           }
122 |           
123 |           return {
124 |             contents: [{
125 |               uri: uri.href,
126 |               text: formatAllCodeSamplesForLanguage(normalizedLanguage, allSamples),
127 |               mimeType: "text/markdown"
128 |             }]
129 |           };
130 |         }
131 |       } catch (error) {
132 |         // If there's an error, return an error message
133 |         return {
134 |           contents: [{
135 |             uri: uri.href,
136 |             text: `# Error\n\nAn error occurred while searching for ${language} code samples${category ? ` in ${category}` : ''}.`,
137 |             mimeType: "text/markdown"
138 |           }]
139 |         };
140 |       }
141 |     }
142 |   );
143 | }
144 | 
145 | /**
146 |  * Register resources for all categories of code samples
147 |  */
148 | async function registerCategories(server: McpServer, codeSamplesPath: string) {
149 |   // Register a resource listing all categories
150 |   server.resource(
151 |     "code-samples-categories",
152 |     "nylas://code-samples",
153 |     async (uri) => {
154 |       try {
155 |         const categories = await fs.readdir(codeSamplesPath);
156 |         const dirCategories = await Promise.all(
157 |           categories.map(async (category) => {
158 |             const categoryPath = path.join(codeSamplesPath, category);
159 |             const stats = await fs.stat(categoryPath);
160 |             return stats.isDirectory() ? category : null;
161 |           })
162 |         );
163 |         
164 |         const validCategories = dirCategories.filter(Boolean) as string[];
165 |         
166 |         const content = `# Nylas API Code Samples
167 | 
168 | This resource provides code samples for integrating with the Nylas API in various programming languages.
169 | 
170 | ## Available Categories
171 | 
172 | ${validCategories.map(category => `- [${category}](nylas://code-samples/${category})`).join('\n')}
173 | 
174 | ## Samples by Language
175 | 
176 | You can also view samples filtered by programming language:
177 | 
178 | - [Node.js](nylas://code-samples/language/node)
179 | - [Python](nylas://code-samples/language/python)
180 | - [Java](nylas://code-samples/language/java)
181 | - [Ruby](nylas://code-samples/language/ruby)
182 | - [API (curl)](nylas://code-samples/language/api)`;
183 |         
184 |         return {
185 |           contents: [{
186 |             uri: uri.href,
187 |             text: content,
188 |             mimeType: "text/markdown"
189 |           }]
190 |         };
191 |       } catch (error) {
192 |         return {
193 |           contents: [{
194 |             uri: uri.href,
195 |             text: "# Error\n\nFailed to load code sample categories.",
196 |             mimeType: "text/markdown"
197 |           }]
198 |         };
199 |       }
200 |     }
201 |   );
202 | }
203 | 
204 | /**
205 |  * Recursively find code samples for a specific language in a directory
206 |  */
207 | async function findCodeSamplesByLanguage(dirPath: string, language: string): Promise<string[]> {
208 |   const results: string[] = [];
209 |   
210 |   try {
211 |     const entries = await fs.readdir(dirPath, { withFileTypes: true });
212 |     
213 |     for (const entry of entries) {
214 |       const fullPath = path.join(dirPath, entry.name);
215 |       
216 |       if (entry.isDirectory()) {
217 |         // Recursively search in subdirectories
218 |         const subResults = await findCodeSamplesByLanguage(fullPath, language);
219 |         results.push(...subResults);
220 |       } else if (entry.name.endsWith('.md')) {
221 |         // Check if the file contains code samples for the specified language
222 |         const content = await fs.readFile(fullPath, 'utf-8');
223 |         
224 |         if (content.includes(`\`\`\`${language}`) || 
225 |             (language === 'Node' && content.includes('```Node')) ||
226 |             (language === 'API' && content.includes('```API'))) {
227 |           // Store the relative path instead of the absolute path
228 |           const relativePath = fullPath.replace(process.cwd() + path.sep + 'nylas-code-samples' + path.sep, '');
229 |           results.push(relativePath);
230 |         }
231 |       }
232 |     }
233 |   } catch (error) {
234 |     // Ignore errors and return what we found so far
235 |   }
236 |   
237 |   return results;
238 | }
239 | 
240 | /**
241 |  * Normalize language names to match code block markers in markdown files
242 |  */
243 | function normalizeLanguageName(language: string): string {
244 |   const langMap: Record<string, string> = {
245 |     'node': 'Node',
246 |     'nodejs': 'Node',
247 |     'javascript': 'Node',
248 |     'js': 'Node',
249 |     'python': 'Python',
250 |     'py': 'Python',
251 |     'java': 'Java',
252 |     'ruby': 'Ruby',
253 |     'rb': 'Ruby',
254 |     'curl': 'API',
255 |     'api': 'API',
256 |     'rest': 'API'
257 |   };
258 |   
259 |   return langMap[language.toLowerCase()] || language;
260 | }
261 | 
262 | /**
263 |  * Format code samples for a specific language in a category
264 |  */
265 | function formatCodeSamplesForLanguage(language: string, category: string, samples: string[]): string {
266 |   if (samples.length === 0) {
267 |     return `# No ${language} Code Samples Found\n\nNo code samples for ${language} were found in the ${category} category.`;
268 |   }
269 |   
270 |   return `# ${language} Code Samples for ${category}\n\n` +
271 |     `Found ${samples.length} code sample(s) for ${language} in the ${category} category:\n\n` +
272 |     samples.map(sample => `- [${path.basename(sample, '.md')}](nylas://code-samples/${sample.replace(/\\/g, '/')})`).join('\n');
273 | }
274 | 
275 | /**
276 |  * Format all code samples for a specific language across all categories
277 |  */
278 | function formatAllCodeSamplesForLanguage(language: string, samples: Record<string, string[]>): string {
279 |   const categories = Object.keys(samples);
280 |   
281 |   if (categories.length === 0) {
282 |     return `# No ${language} Code Samples Found\n\nNo code samples for ${language} were found.`;
283 |   }
284 |   
285 |   let content = `# ${language} Code Samples\n\n`;
286 |   
287 |   for (const category of categories) {
288 |     content += `## ${category}\n\n`;
289 |     
290 |     content += samples[category]
291 |       .map(sample => `- [${path.basename(sample, '.md')}](nylas://code-samples/${sample.replace(/\\/g, '/')})`).join('\n');
292 |     
293 |     content += '\n\n';
294 |   }
295 |   
296 |   return content;
297 | }
```

--------------------------------------------------------------------------------
/nylas-code-samples/Email/Messages/write.md:
--------------------------------------------------------------------------------

```markdown
  1 | ### Update a specific message
  2 | 
  3 | `PUT /messages/<MESSAGE_ID>` → `PUT /v3/grants/<NYLAS_GRANT_ID>/messages/<MESSAGE_ID>`
  4 | 
  5 | ```Node
  6 | import 'dotenv/config'
  7 | import Nylas from 'nylas'
  8 | 
  9 | const NylasConfig = {
 10 |   apiKey: process.env.NYLAS_API_KEY,
 11 |   apiUri: process.env.NYLAS_API_URI,
 12 | }
 13 | 
 14 | const nylas = new Nylas(NylasConfig)
 15 | const identifier = process.env.NYLAS_GRANT_ID
 16 | const folderId = process.env.FOLDER_ID
 17 | const messageId = process.env.MESSAGE_ID
 18 | 
 19 | const updateMessageFolder = async () => {
 20 |   try {
 21 |     const updatedMessage = await nylas.messages.update({
 22 |       identifier,
 23 |       messageId,
 24 |       requestBody: {
 25 |           folders: [folderId]
 26 |       }
 27 |     })
 28 | 
 29 |     console.log('Message updated:', updatedMessage)
 30 |   } catch (error) {
 31 |     console.error('Error updating message folder:', error)
 32 |   }
 33 | }
 34 | 
 35 | updateMessageFolder()
 36 | ```
 37 | 
 38 | ```Java
 39 | import com.nylas.NylasClient;
 40 | import com.nylas.models.*;
 41 | import java.util.List;
 42 | 
 43 | public class UpdateMessage {
 44 |   public static void main(String[] args) throws NylasSdkTimeoutError, NylasApiError {
 45 |     NylasClient nylas = new NylasClient.Builder("<NYLAS_API_KEY>").build();
 46 | 
 47 |     UpdateMessageRequest request = new UpdateMessageRequest.Builder().
 48 |         unread(true).
 49 |         starred(true).
 50 |         build();
 51 | 
 52 |     Response<Message> message = nylas.messages().
 53 |         update(dotenv.get("NYLAS_GRANT_ID"), "<MESSAGE_ID>", request);
 54 | 
 55 |     System.out.println(message);
 56 |   }
 57 | }
 58 | ```
 59 | 
 60 | ```Python
 61 | from dotenv import load_dotenv
 62 | load_dotenv()
 63 | 
 64 | import os
 65 | import sys
 66 | from nylas import Client
 67 | 
 68 | nylas = Client(
 69 |     os.environ.get('NYLAS_API_KEY'),
 70 |     os.environ.get('NYLAS_API_URI')
 71 | )
 72 | 
 73 | grant_id = os.environ.get("NYLAS_GRANT_ID")
 74 | folder_id = os.environ.get("FOLDER_ID")
 75 | message_id = os.environ.get("MESSAGE_ID")
 76 | 
 77 | message = nylas.messages.update(
 78 |     grant_id,
 79 |     message_id,
 80 |     request_body={
 81 |       "folders": [folder_id]
 82 |     }
 83 | )
 84 | 
 85 | print(message)
 86 | ```
 87 | 
 88 | ```Ruby
 89 | require 'nylas'
 90 | 
 91 | nylas = Nylas::Client.new(
 92 |     api_key: "<NYLAS_API_KEY>"
 93 | )
 94 | 
 95 | request_body = {
 96 |   unread: true,
 97 |   starred: true
 98 | }
 99 | 
100 | message, _ = nylas.messages.update(identifier: "<NYLAS_GRANT_ID>",
101 |     message_id: "<MESSAGE_ID>",
102 |     request_body: request_body)
103 | 
104 | puts message
105 | ```
106 | 
107 | ```API
108 | curl --location --request PUT 'https://api.us.nylas.com/v3/grants/<NYLAS_GRANT_ID>/messages/<MESSAGE_ID>' \
109 |   --header 'Content-Type: application/json' \
110 |   --header 'Accept: application/json' \
111 |   --header 'Authorization: Bearer <NYLAS_API_KEY>' \
112 |   --data '{
113 |     "folders": [
114 |       "<FOLDER_ID>"
115 |     ]
116 |   }'
117 | ```
118 | 
119 | ### Send a message
120 | 
121 | `POST /send` → `POST /v3/grants/<NYLAS_GRANT_ID>/messages/send`
122 | 
123 | ```Node
124 | app.get("/nylas/send-email", async (req, res) => {
125 |   try {
126 |     const sentMessage = await nylas.messages.send({
127 |       identifier: process.env.USER_GRANT_ID,
128 |       requestBody: {
129 |         to: [{ name: "Name", email: process.env.EMAIL }],
130 |         replyTo: [{ name: "Name", email: process.env.EMAIL }],
131 |         subject: "Your Subject Here",
132 |         body: "Your email body here.",
133 |       },
134 |     });
135 | 
136 |     res.json(sentMessage);
137 |   } catch (error) {
138 |     console.error("Error sending email:", error);
139 |   }
140 | });
141 | ```
142 | 
143 | ```Java
144 | import com.nylas.NylasClient;
145 | import com.nylas.models.*;
146 | import java.util.ArrayList;
147 | import java.util.List;
148 | 
149 | public class SendEmails {
150 |   public static void main(String[] args) throws NylasSdkTimeoutError, NylasApiError {
151 |     NylasClient nylas = new NylasClient.Builder("<NYLAS_API_KEY>").build();
152 | 
153 |     List<EmailName> emailNames = new ArrayList<>();
154 |     emailNames.add(new EmailName("[email protected]", "John Doe"));
155 | 
156 |     TrackingOptions options = new TrackingOptions("hey just testing", true, true, true);
157 | 
158 |     SendMessageRequest requestBody = new SendMessageRequest.Builder(emailNames).
159 |         trackingOptions(options).
160 |         subject("Hey Reaching Out with Nylas").
161 |         body("Hey I would like to track this link <a href='https://espn.com'>My Example Link</a>.").
162 |         build();
163 | 
164 |     Response<Message> email = nylas.messages().send("<NYLAS_GRANT_ID>", requestBody);
165 | 
166 |     System.out.println(email.getData());
167 |   }
168 | }
169 | ```
170 | 
171 | ```Python
172 | from dotenv import load_dotenv
173 | load_dotenv()
174 | 
175 | import os
176 | import sys
177 | from nylas import Client
178 | 
179 | nylas = Client(
180 |     os.environ.get('NYLAS_API_KEY'),
181 |     os.environ.get('NYLAS_API_URI')
182 | )
183 | 
184 | grant_id = os.environ.get("NYLAS_GRANT_ID")
185 | email = os.environ.get("EMAIL")
186 | 
187 | message = nylas.messages.send(
188 |     grant_id,
189 |     request_body={
190 |       "to": [{ "name": "Name", "email": email }],
191 |       "reply_to": [{ "name": "Name", "email": email }],
192 |       "subject": "Your Subject Here",
193 |       "body": "Your email body here.",
194 |     }
195 | )
196 | 
197 | print(message)
198 | ```
199 | 
200 | ```Ruby
201 | require 'nylas'
202 | 
203 | nylas = Nylas::Client.new(
204 |     api_key: "<NYLAS_API_KEY>"
205 | )
206 | 
207 | request_body = {
208 |   subject: "Hey Reaching Out with Nylas",
209 |   body: "Hey I would like to track this link <a href='https://espn.com'>My Example Link</a>",
210 |   to: [{name: "John Doe", email: "[email protected]"}],
211 |   tracking_options: {label: "hey just testing",
212 |     opens: true,
213 |     links: true,
214 |     thread_replies: true}
215 | }
216 | 
217 | email, _ = nylas.messages.send(identifier: "<NYLAS_GRANT_ID>", request_body: request_body)
218 | 
219 | puts "Message \"#{email[:subject]}\" was sent with ID #{email[:id]}"
220 | ```
221 | 
222 | ```API
223 | curl --request POST \
224 |   --url https://api.us.nylas.com/v3/grants/<NYLAS_GRANT_ID>/messages/send \
225 |   --header 'Authorization: Bearer <NYLAS_API_KEY>' \
226 |   --header 'Content-Type: application/json' \
227 |   --data '{
228 |     "subject": "Hey Reaching Out with Nylas",
229 |     "body": "Hey I would like to track this link <a href='https://espn.com'>My Example Link</a>",
230 |     "to": [
231 |       {
232 |       "name": "John Doe",
233 |       "email": "[email protected]"
234 |       }
235 |     ],
236 |     "tracking_options": {
237 |       "opens": true,
238 |       "links": true,
239 |       "thread_replies": true,
240 |       "label": "hey just testing"
241 |     }
242 |   }'
243 | ```
244 | 
245 | ### Delete a specific message
246 | 
247 | `DELETE /messages/<MESSAGE_ID>` → `DELETE /v3/grants/<NYLAS_GRANT_ID>/messages/<MESSAGE_ID>`
248 | 
249 | ```Node
250 | import 'dotenv/config'
251 | import Nylas from 'nylas'
252 | 
253 | const NylasConfig = {
254 |   apiKey: process.env.NYLAS_API_KEY,
255 |   apiUri: process.env.NYLAS_API_URI,
256 | }
257 | 
258 | const nylas = new Nylas(NylasConfig)
259 | 
260 | async function deleteMessage() { 
261 |   try {
262 |     const result = await nylas.messages.destroy({
263 |       identifier: process.env.NYLAS_GRANT_ID,
264 |       messageId: process.env.MESSAGE_ID,
265 |     })
266 |   
267 |     console.log('Result:', result)
268 |   } catch (error) {
269 |     console.error('Error deleting message:', error)
270 |   }
271 | }
272 | 
273 | deleteMessage()
274 | ```
275 | 
276 | ```Java
277 | import com.nylas.NylasClient;
278 | import com.nylas.models.*;
279 | 
280 | public class ReturnMessage {
281 |   public static void main(String[] args) throws NylasSdkTimeoutError, NylasApiError {
282 |     NylasClient nylas = new NylasClient.Builder("<NYLAS_API_KEY>").build();
283 |     DeleteResponse message = nylas.messages().destroy("<NYLAS_GRANT_ID>", "<MESSAGE_ID>");
284 | 
285 |     System.out.println(message);
286 |   }
287 | }
288 | ```
289 | 
290 | ```Python
291 | from dotenv import load_dotenv
292 | load_dotenv()
293 | 
294 | import os
295 | import sys
296 | from nylas import Client
297 | 
298 | nylas = Client(
299 |     os.environ.get('NYLAS_API_KEY'),
300 |     os.environ.get('NYLAS_API_URI')
301 | )
302 | 
303 | grant_id = os.environ.get("NYLAS_GRANT_ID")
304 | message_id = os.environ.get("MESSAGE_ID")
305 | 
306 | result = nylas.messages.destroy(
307 |     grant_id,
308 |     message_id,
309 | )
310 | 
311 | print(result)
312 | ```
313 | 
314 | ```Ruby
315 | require 'nylas' 
316 | 
317 | nylas = Nylas::Client.new(
318 |     api_key: "<NYLAS_API_KEY>"
319 | )
320 | 
321 | status, _ = nylas.messages.destroy(identifier: "<NYLAS_GRANT_ID>", message_id: "<MESSAGE_ID>")
322 | 
323 | puts status
324 | ```
325 | 
326 | ```API
327 | curl --request DELETE \
328 |   --url https://api.nylas.com/messages/<MESSAGE_ID> \
329 |   --header 'Accept: application/json' \
330 |   --header 'Authorization: Bearer <NYLAS_ACCESS_TOKEN>' \
331 |   --header 'Content-Type: application/json'
332 | ```
333 | 
334 | ### Compose a message using AI
335 | 
336 | You can use the [`POST /v3/grants/<NYLAS_GRANT_ID>/messages/smart-compose` endpoint](https://developer.nylas.com/docs/api/v3/ecc/#post-/v3/grants/-grant_id-/messages/smart-compose) to generate an email message based on a text prompt.
337 | 
338 | ```API
339 | curl --request POST \
340 |   --url 'https://api.us.nylas.com/v3/grants/<NYLAS_GRANT_ID>/messages/smart-compose' \
341 |   --header 'Accept: application/json' \
342 |   --header 'Authorization: Bearer <NYLAS_API_KEY>' \
343 |   --header 'Content-Type: application/json'
344 | ```
345 | 
346 | ```Node
347 | import 'dotenv/config'
348 | import Nylas from 'nylas'
349 | 
350 | const NylasConfig = {
351 |   apiKey: process.env.NYLAS_API_KEY,
352 |   apiUri: process.env.NYLAS_API_URI,
353 | }
354 | 
355 | const nylas = new Nylas(NylasConfig)
356 | 
357 | async function composeEmail() {
358 |   try {
359 |     const message = await nylas.messages.smartCompose.composeMessage({
360 |         identifier: process.env.NYLAS_GRANT_ID,
361 |         requestBody: {
362 |           prompt: 'Tell my colleague how we can use Nylas APIs',
363 |         }
364 |     })
365 |     
366 |     console.log('Message created:', message)
367 |   } catch (error) {
368 |     console.error('Error creating message:', error)
369 |   }
370 | }
371 | 
372 | composeEmail()
373 | ```
374 | 
375 | ```Ruby
376 | require 'nylas' 
377 | 
378 | nylas = Nylas::Client.new(
379 |    api_key: "<NYLAS_API_KEY>"
380 | )
381 | 
382 | request_body = {
383 |   prompt: 'Let''s talk about Nylas'
384 | }
385 | 
386 | message, _ = nylasnylas.messages.smart_compose.compose_message(identifier: "<NYLAS_GRANT_ID>", 
387 |     request_body: request_body)
388 | 
389 | puts message[:suggestion]
390 | ```
391 | 
392 | ```Python
393 | from dotenv import load_dotenv
394 | load_dotenv()
395 | 
396 | import os
397 | import sys
398 | from nylas import Client
399 | 
400 | nylas = Client(
401 |     os.environ.get('NYLAS_API_KEY'),
402 |     os.environ.get('NYLAS_API_URI')
403 | )
404 | 
405 | grant_id = os.environ.get("NYLAS_GRANT_ID")
406 | email = os.environ.get("EMAIL")
407 | 
408 | message = nylas.messages.smart_compose.compose_message(
409 |     grant_id,
410 |     request_body={
411 |       "prompt": "Tell my colleague how we can use Nylas APIs",
412 |     }
413 | )
414 | 
415 | print(message)
416 | ```
417 | 
418 | ```Java
419 | import com.nylas.NylasClient;
420 | import com.nylas.models.*;
421 | 
422 | public class SmartCompose {
423 |   public static void main(String[] args) throws NylasSdkTimeoutError, NylasApiError {
424 |     NylasClient nylas = new NylasClient.Builder("<NYLAS_API_KEY>").build();
425 |     ComposeMessageRequest requestBody = new ComposeMessageRequest("Let's talk about Nylas");
426 |     Response<ComposeMessageResponse> message = nylas.messages().smartCompose().composeMessage("<NYLAS_GRANT_ID>", requestBody);
427 |     
428 |     System.out.println(message.getData().getSuggestion());
429 |   }
430 | }
431 | ```
432 | 
433 | ### Compose a reply to a specific message using AI
434 | 
435 | You can use the [`POST /v3/grants/<NYLAS_GRANT_ID>/messages/<MESSAGE_ID>/smart-compose` endpoint](https://developer.nylas.com/docs/api/v3/ecc/#post-/v3/grants/-grant_id-/messages/-message_id-/smart-compose) to generate a reply to a specific email message.
436 | 
```

--------------------------------------------------------------------------------
/nylas-code-samples/Email/Drafts/write.md:
--------------------------------------------------------------------------------

```markdown
  1 | ### Update a specific draft
  2 | 
  3 | `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-)
  4 | 
  5 | ```API
  6 | curl --request PUT \
  7 |   --url https://api.us.nylas.com/v3/grants/<NYLAS_GRANT_ID>/drafts/<DRAFT_ID> \
  8 |   --header 'Accept: application/json' \
  9 |   --header 'Authorization: Bearer <NYLAS_API_KEY>' \
 10 |   --header 'Content-Type: application/json' \
 11 |   --data '{
 12 |     "unread": true,
 13 |     "starred": true
 14 |   }'
 15 | ```
 16 | 
 17 | ```Node
 18 | import 'dotenv/config'
 19 | import Nylas from 'nylas'
 20 | 
 21 | const NylasConfig = {
 22 |   apiKey: process.env.NYLAS_API_KEY,
 23 |   apiUri: process.env.NYLAS_API_URI,
 24 | }
 25 | 
 26 | const nylas = new Nylas(NylasConfig)
 27 | 
 28 | async function updateDraft() {
 29 |   try {
 30 |     const calendar = await nylas.drafts.update({
 31 |       identifier: process.env.NYLAS_GRANT_ID,
 32 |       draftId: process.env.DRAFT_ID,
 33 |       requestBody: {
 34 |         starred: true
 35 |       }
 36 |     })
 37 | 
 38 |     console.log('Updated Draft:', calendar)
 39 |   } catch (error) {
 40 |     console.error('Error to update draft:', error)
 41 |   }
 42 | }
 43 | 
 44 | updateDraft()
 45 | ```
 46 | 
 47 | ```Ruby
 48 | require 'nylas'
 49 | 
 50 | nylas = Nylas::Client.new(
 51 |     api_key: "<NYLAS_API_KEY>"
 52 | )
 53 | 
 54 | request_body = {
 55 |   unread: true,
 56 |   starred: true
 57 | }
 58 | 
 59 | draft, _ = nylas.drafts.update(identifier: "<NYLAS_GRANT_ID>",
 60 |     draft_id: "<DRAFT_ID>",
 61 |     request_body: request_body)
 62 | 
 63 | puts draft
 64 | ```
 65 | 
 66 | ```Python
 67 | from dotenv import load_dotenv
 68 | load_dotenv()
 69 | 
 70 | import os
 71 | import sys
 72 | from nylas import Client
 73 | 
 74 | nylas = Client(
 75 |     os.environ.get('NYLAS_API_KEY'),
 76 |     os.environ.get('NYLAS_API_URI')
 77 | )
 78 | 
 79 | grant_id = os.environ.get("NYLAS_GRANT_ID")
 80 | 
 81 | thread = nylas.drafts.update(
 82 |     grant_id,
 83 |     draft_id=os.environ.get("DRAFT_ID"),
 84 |     request_body={
 85 |       "scope": ["mail.ready"]
 86 |     }
 87 | )
 88 | 
 89 | print(thread)
 90 | ```
 91 | 
 92 | ```Java
 93 | import com.nylas.NylasClient;
 94 | import com.nylas.models.*;
 95 | 
 96 | public class UpdateDraft {
 97 |   public static void main(String[] args) throws NylasSdkTimeoutError, NylasApiError {
 98 |     NylasClient nylas = new NylasClient.Builder("<NYLAS_API_KEY>").build();
 99 | 
100 |     UpdateDraftRequest requestBody = new UpdateDraftRequest.
101 |         Builder().
102 |         unread(true).
103 |         starred(true).
104 |         build();
105 | 
106 |     Response<Draft> draft = nylas.drafts().update("<NYLAS_GRANT_ID>", "<DRAFT_ID>", requestBody);
107 | 
108 |     System.out.printf("%s%s%s%n",
109 |         draft.getData().getId(),
110 |         draft.getData().getUnread(),
111 |         draft.getData().getStarred());
112 |   }
113 | }
114 | ```
115 | 
116 | ### Create a draft
117 | 
118 | `POST /drafts` → [`POST /v3/grants/<NYLAS_GRANT_ID>/drafts`](https://developer.nylas.com/docs/api/v3/ecc/#post-/v3/grants/-grant_id-/drafts)
119 | 
120 | ```API
121 | curl --request POST \
122 |   --url https://api.us.nylas.com/v3/grants/<NYLAS_GRANT_ID>/drafts \
123 |   --header 'Authorization: Bearer <NYLAS_API_KEY>' \
124 |   --header 'Content-Type: application/json' \
125 |   --data '{
126 |     "subject": "With Love, From Nylas",
127 |     "to": [{
128 |       "email": "[email protected]",
129 |       "name": "Dorothy Vaughan"
130 |     }],
131 |     "cc": [{
132 |       "email": "George Washington Carver",
133 |       "name": "[email protected]"
134 |     }],
135 |     "bcc": [{
136 |       "email": "Albert Einstein",
137 |       "name": "[email protected]"
138 |     }],
139 |     "reply_to": [{
140 |       "email": "[email protected]",
141 |       "name": "Stephanie Kwolek"
142 |     }],
143 |     "body": "This email was sent using the Nylas Email API. Visit https://nylas.com for details.",
144 |     "tracking_options": {
145 |       "opens": true,
146 |       "links": true,
147 |       "thread_replies": true,
148 |       "label": "just testing"
149 |     }
150 |   }'
151 | ```
152 | 
153 | ```Node
154 | import 'dotenv/config'
155 | import Nylas from 'nylas'
156 | 
157 | const NylasConfig = {
158 |   apiKey: process.env.NYLAS_API_KEY,
159 |   apiUri: process.env.NYLAS_API_URI,
160 | }
161 | 
162 | const nylas = new Nylas(NylasConfig)
163 | const identifier = process.env.NYLAS_GRANT_ID
164 | 
165 | const createDraft = async () => {
166 |   try {
167 |     const draft = {
168 |       subject: "With Love, From Nylas",
169 |       to: [{ name: "Dorothy Vaughan", email: "[email protected]" }],
170 |       body: "This email was sent using the Nylas Email API. Visit https://nylas.com for details.",
171 |     }
172 | 
173 |     const createdDraft = await nylas.drafts.create({
174 |         identifier,
175 |         requestBody: draft
176 |     })
177 | 
178 |     console.log('Draft created:', createdDraft)
179 | 
180 |   } catch (error) {
181 |     console.error('Error creating draft:', error)
182 |   }
183 | }
184 | 
185 | createDraft()
186 | ```
187 | 
188 | ```Ruby
189 | require 'nylas'
190 | 
191 | nylas = Nylas::Client.new(
192 |     api_key: "<NYLAS_API_KEY>"
193 | )
194 | 
195 | request_body = {
196 |   subject: "With Love, From Nylas",
197 |   body: 'This email was sent using the Nylas Email API. ' + 
198 |       'Visit https://nylas.com for details.',
199 |   to: [{
200 |     name: "Dorothy Vaughan", 
201 |     email: "[email protected]"
202 |   }],
203 |   cc: [{
204 |     name: "George Washington Carver", 
205 |     email: "[email protected]"
206 |   }],
207 |   bcc: [{
208 |     name: "Albert Einstein", 
209 |     email: "[email protected]"
210 |   }],
211 |   reply_to: [{
212 |     name: "Stephanie Kwolek", 
213 |     email: "[email protected]"
214 |   }],
215 |   tracking_options: {
216 |     label: "just testing", 
217 |     opens: true, 
218 |     links: true,
219 |     thread_replies: true
220 |   }
221 | }
222 | 
223 | draft, _ = nylas.drafts.create(identifier: "<NYLAS_GRANT_ID>", request_body: request_body)
224 | 
225 | puts "Draft \"#{draft[:subject]}\" was created with ID: #{draft[:id]}"
226 | ```
227 | 
228 | ```Python
229 | from dotenv import load_dotenv
230 | load_dotenv()
231 | 
232 | import os
233 | import sys
234 | from nylas import Client
235 | 
236 | nylas = Client(
237 |     os.environ.get('NYLAS_API_KEY'),
238 |     os.environ.get('NYLAS_API_URI')
239 | )
240 | 
241 | grant_id = os.environ.get("NYLAS_GRANT_ID")
242 | email = os.environ.get("EMAIL")
243 | 
244 | draft = nylas.drafts.create(
245 |     grant_id,
246 |     request_body={
247 |       "to": [{ "name": "Dorothy Vaughan", "email": email }],
248 |       "reply_to": [{ "name": "Dorothy Vaughan", "email": email }],
249 |       "subject": "With Love, From Nylas",
250 |       "body": "This email was sent using the Nylas Email API. Visit https://nylas.com for details.",
251 |     }
252 | )
253 | 
254 | print(draft)
255 | ```
256 | 
257 | ```Java
258 | import com.nylas.NylasClient;
259 | import com.nylas.models.*;
260 | 
261 | import com.nylas.util.FileUtils;
262 | import io.github.cdimascio.dotenv.Dotenv;
263 | 
264 | import java.util.ArrayList;
265 | import java.util.Collections;
266 | import java.util.List;
267 | 
268 | public class CreateDraft {
269 |   public static void main(String[] args) throws NylasSdkTimeoutError, NylasApiError {
270 |     NylasClient nylas = new NylasClient.Builder("<NYLAS_API_KEY>").build();
271 | 
272 |     CreateDraftRequest requestBody = new CreateDraftRequest.Builder().
273 |         to(Collections.singletonList(new EmailName("[email protected]", "Dorothy Vaughan"))).
274 |         cc(Collections.singletonList(new EmailName("[email protected]", "George Washington Carver"))).
275 |         bcc(Collections.singletonList(new EmailName("[email protected]", "Albert Einstein"))).
276 |         subject("With Love, From Nylas").
277 |         body("This email was sent using the Nylas Email API. Visit https://nylas.com for details.").
278 |         build();
279 | 
280 |     Response<Draft> drafts = nylas.drafts().create(dotenv.get("NYLAS_GRANT_ID"), requestBody);
281 | 
282 |     System.out.println("Draft " + drafts.getData().getSubject() + 
283 |         " was created with ID " + drafts.getData().getId());
284 |   }
285 | }
286 | ```
287 | 
288 | ### Send a specific draft
289 | 
290 | You can use the [`POST /v3/grants/<NYLAS_GRANT_ID>/drafts/<DRAFT_ID>` endpoint](https://developer.nylas.com/docs/api/v3/ecc/#post-/v3/grants/-grant_id-/drafts/-draft_id-) to send a draft.
291 | 
292 | ```API
293 | curl --request POST \
294 |   --url https://api.us.nylas.com/v3/grants/<NYLAS_GRANT_ID>/drafts/<DRAFT_ID>  \
295 |   --header 'Accept: application/json' \
296 |   --header 'Authorization: Bearer <NYLAS_API_KEY>' \
297 |   --header 'Content-Type: application/json' \
298 | ```
299 | 
300 | ```Node
301 | import 'dotenv/config'
302 | import Nylas from 'nylas'
303 | 
304 | const NylasConfig = {
305 |   apiKey: process.env.NYLAS_API_KEY,
306 |   apiUri: process.env.NYLAS_API_URI,
307 | }
308 | 
309 | const nylas = new Nylas(NylasConfig)
310 | const identifier = process.env.NYLAS_GRANT_ID
311 | const draftId = process.env.DRAFT_ID
312 | 
313 | const sendDraft = async () => {
314 |   try {
315 |     const sentMessage = await nylas.drafts.send({ identifier, draftId })
316 | 
317 |     console.log('Draft sent:', sentMessage)
318 |   } catch (error) {
319 |     console.error('Error sending draft:', error)
320 |   }
321 | }
322 | 
323 | sendDraft()
324 | ```
325 | 
326 | ```Ruby
327 | require 'nylas' 
328 | 
329 | nylas = Nylas::Client.new(
330 |     api_key: "<NYLAS_API_KEY>"
331 | )
332 | 
333 | draft, _ = nylas.drafts.send(identifier: "<NYLAS_GRANT_ID>", draft_id: "<DRAFT_ID>")
334 | ```
335 | 
336 | ```Python
337 | from dotenv import load_dotenv
338 | load_dotenv()
339 | 
340 | import os
341 | import sys
342 | from nylas import Client
343 | 
344 | nylas = Client(
345 |     os.environ.get('NYLAS_API_KEY'),
346 |     os.environ.get('NYLAS_API_URI')
347 | )
348 | 
349 | grant_id = os.environ.get("NYLAS_GRANT_ID")
350 | draft_id = os.environ.get("DRAFT_ID")
351 | 
352 | draft = nylas.drafts.send(
353 |     grant_id,
354 |     draft_id
355 | )
356 | 
357 | print(draft)
358 | ```
359 | 
360 | ```Java
361 | import com.nylas.NylasClient;
362 | import com.nylas.models.*;
363 | 
364 | public class SendDraft {
365 |   public static void main(String[] args) throws NylasSdkTimeoutError, NylasApiError {
366 |     NylasClient nylas = new NylasClient.Builder("<NYLAS_API_KEY>").build();
367 |     Response<Message> draft = nylas.drafts().send("<NYLAS_GRANT_ID>", "<DRAFT_ID>");
368 | 
369 |     System.out.println(draft.getData());
370 |   }
371 | }
372 | ```
373 | 
374 | ### Delete a specific draft
375 | 
376 | `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-)
377 | 
378 | ```API
379 | curl --request DELETE \
380 |   --url https://api.us.nylas.com/v3/grants/<NYLAS_GRANT_ID>/drafts/<DRAFT_ID> \
381 |   --header 'Accept: application/json' \
382 |   --header 'Authorization: Bearer <NYLAS_API_KEY>' \
383 |   --header 'Content-Type: application/json'
384 | ```
385 | 
386 | ```Node
387 | import 'dotenv/config'
388 | import Nylas from 'nylas'
389 | 
390 | const NylasConfig = {
391 |   apiKey: process.env.NYLAS_API_KEY,
392 |   apiUri: process.env.NYLAS_API_URI,
393 | }
394 | 
395 | const nylas = new Nylas(NylasConfig)
396 | const identifier = process.env.NYLAS_GRANT_ID
397 | const draftId = process.env.DRAFT_ID
398 | 
399 | const deleteDraft = async () => {
400 |   try {
401 |     await nylas.drafts.destroy({ identifier, draftId })
402 | 
403 |     console.log(\`Draft with ID \${draftId} deleted successfully.\`)
404 |   } catch (error) {
405 |     console.error(\`Error deleting contact with ID \${draftId}:\`, error)
406 |   }
407 | }
408 | 
409 | deleteDraft()
410 | ```
411 | 
412 | ```Ruby
413 | require 'nylas'
414 | 
415 | nylas = Nylas::Client.new(
416 |    api_key: "<NYLAS_API_KEY>"
417 | )
418 | 
419 | status, _ =  nylas.drafts.destroy(identifier: "<NYLAS_GRANT_ID>", draft_id: "<DRAFT_ID>")
420 | 
421 | if status
422 |   puts "Draft successfully deleted"
423 | end
424 | ```
425 | 
426 | ```Python
427 | from nylas import Client
428 | 
429 | nylas = Client(
430 |     api_key = "<NYLAS_API_KEY>"
431 | )
432 | 
433 | drafts = nylas.drafts.destroy("<NYLAS_GRANT_ID>", "<DRAFT_ID>")
434 | 
435 | print(drafts)
436 | ```
437 | 
438 | ```Java
439 | import com.nylas.NylasClient;
440 | import com.nylas.models.*;
441 | 
442 | public class SendDraft {
443 |   public static void main(String[] args) throws NylasSdkTimeoutError, NylasApiError {
444 |     NylasClient nylas = new NylasClient.Builder("<NYLAS_API_KEY>").build();
445 |     DeleteResponse draft = nylas.drafts().destroy("<NYLAS_GRANT_ID>", "<DRAFT_ID>");
446 | 
447 |     System.out.println(draft.getRequestId());
448 |   }
449 | }
450 | ```
451 | 
```

--------------------------------------------------------------------------------
/nylas-code-samples/Contacts/write.md:
--------------------------------------------------------------------------------

```markdown
  1 | ### Update a specific contact
  2 | 
  3 | `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-)
  4 | 
  5 | ```API
  6 | curl --request PUT \
  7 |   --url https://api.us.nylas.com/v3/grants/<NYLAS_GRANT_ID>/contacts/<CONTACT_ID> \
  8 |   --header 'Accept: application/json' \
  9 |   --header 'Authorization: Bearer <NYLAS_API_KEY>' \
 10 |   --header 'Content-Type: application/json' \
 11 |   --data '{
 12 |     "birthday": "1960-12-31",
 13 |     "company_name": "Nylas",
 14 |     "emails": [
 15 |       {
 16 |         "email": "[email protected]",
 17 |         "type": "work"
 18 |       },
 19 |       {
 20 |         "email": "[email protected]",
 21 |         "type": "home"
 22 |       }
 23 |     ],
 24 |     "given_name": "John",
 25 |     "groups": [
 26 |       {
 27 |         "id": "starred"
 28 |       },
 29 |       {
 30 |         "id": "all"
 31 |       }
 32 |     ],
 33 |     "im_addresses": [
 34 |       {
 35 |         "type": "jabber",
 36 |         "im_address": "myjabberaddress"
 37 |       },
 38 |       {
 39 |         "type": "msn",
 40 |         "im_address": "mymsnaddress"
 41 |       }
 42 |     ],
 43 |     "job_title": "Software Engineer",
 44 |     "manager_name": "Bill",
 45 |     "middle_name": "Jacob",
 46 |     "nickname": "JD",
 47 |     "notes": "Loves Ramen",
 48 |     "office_location": "123 Main Street",
 49 |     "phone_numbers": [
 50 |       {
 51 |         "number": "+1-555-555-5555",
 52 |         "type": "work"
 53 |       },
 54 |       {
 55 |         "number": "+1-555-555-5556",
 56 |         "type": "home"
 57 |       }
 58 |     ],
 59 |     "physical_addresses": [
 60 |       {
 61 |         "type": "work",
 62 |         "street_address": "123 Main Street",
 63 |         "postal_code": 94107,
 64 |         "state": "CA",
 65 |         "country": "USA",
 66 |         "city": "San Francisco"
 67 |       },
 68 |       {
 69 |         "type": "home",
 70 |         "street_address": "456 Main Street",
 71 |         "postal_code": 94107,
 72 |         "state": "CA",
 73 |         "country": "USA",
 74 |         "city": "San Francisco"
 75 |       }
 76 |     ],
 77 |     "suffix": "Jr.",
 78 |     "surname": "Doe",
 79 |     "web_pages": [
 80 |       {
 81 |         "type": "work",
 82 |         "url": "http://www.linkedin.com/in/johndoe"
 83 |       },
 84 |       {
 85 |         "type": "home",
 86 |         "url": "http://www.johndoe.com"
 87 |       }
 88 |     ]
 89 |   }'
 90 | ```
 91 | 
 92 | ```Node
 93 | import 'dotenv/config'
 94 | import Nylas from 'nylas'
 95 | 
 96 | const NylasConfig = {
 97 |   apiKey: process.env.NYLAS_API_KEY,
 98 |   apiUri: process.env.NYLAS_API_URI,
 99 | }
100 | 
101 | const nylas = new Nylas(NylasConfig)
102 | 
103 | async function updateContact() {
104 |   try {
105 |     const contact = await nylas.contacts.update({
106 |       identifier: process.env.NYLAS_GRANT_ID,
107 |       contactId: process.env.CONTACT_ID,
108 |       requestBody: {
109 |         givenName: "Nyla",
110 |       }
111 |     })
112 | 
113 |     console.log('Contact:', JSON.stringify(contact))
114 |   } catch (error) {
115 |     console.error('Error to create contact:', error)
116 |   }
117 | }
118 | 
119 | updateContact()
120 | ```
121 | 
122 | ```Ruby
123 | require 'nylas' 
124 | 
125 | nylas = Nylas::Client.new(api_key: "<NYLAS_API_KEY>")
126 | 
127 | request_body = {
128 |   notes: "This is *the best* swag",
129 | }
130 | 
131 | contact, _ = nylas.contacts.update(identifier: "<NYLAS_GRANT_ID>", 
132 |     contact_id: "<CONTACT_ID>", 
133 |     request_body: request_body)
134 | 
135 | puts contact
136 | ```
137 | 
138 | ```Python
139 | from dotenv import load_dotenv
140 | load_dotenv()
141 | 
142 | import os
143 | import sys
144 | from nylas import Client
145 | 
146 | nylas = Client(
147 |     os.environ.get('NYLAS_API_KEY'),
148 |     os.environ.get('NYLAS_API_URI')
149 | )
150 | 
151 | grant_id = os.environ.get("NYLAS_GRANT_ID")
152 | contact_id = os.environ.get("CONTACT_ID")
153 | 
154 | contact = nylas.contacts.update(
155 |     grant_id,
156 |     contact_id,
157 |     request_body={
158 |       "given_name": "Nyla",
159 |     }
160 | )
161 | 
162 | print(contact)
163 | ```
164 | 
165 | ```Java
166 | import com.nylas.NylasClient;
167 | import com.nylas.models.*;
168 | 
169 | public class UpdateContact {
170 |   public static void main(String[] args) throws NylasSdkTimeoutError, NylasApiError {
171 |     NylasClient nylas = new NylasClient.Builder("<NYLAS_API_KEY>").build();
172 | 
173 |     UpdateContactRequest requestBody = new UpdateContactRequest.
174 |         Builder().
175 |         notes("This is *the best* swag").
176 |         build();
177 | 
178 |     Response<Contact> contact = nylas.contacts().update("<NYLAS_GRANT_ID>", "<CONTACT_ID>", requestBody);
179 | 
180 |     System.out.println(contact);
181 |   }
182 | }
183 | ```
184 | 
185 | ### Create a contact
186 | 
187 | `POST /contacts` → [`POST /v3/grants/<NYLAS_GRANT_ID>/contacts`](https://developer.nylas.com/docs/api/v3/ecc/#post-/v3/grants/-grant_id-/contacts)
188 | 
189 | ```API
190 | curl --request POST \
191 |   --url https://api.us.nylas.com/v3/grants/<NYLAS_GRANT_ID>/contacts \
192 |   --header 'Accept: application/json' \
193 |   --header 'Authorization: Bearer <NYLAS_API_KEY>' \
194 |   --header 'Content-Type: application/json' \
195 |   --data '{
196 |     "birthday": "1960-12-31",
197 |     "company_name": "Nylas",
198 |     "emails": [
199 |       {
200 |         "email": "[email protected]",
201 |         "type": "work"
202 |       },
203 |       {
204 |         "email": "[email protected]",
205 |         "type": "home"
206 |       }
207 |     ],
208 |     "given_name": "John",
209 |     "groups": [
210 |       {
211 |         "id": "starred"
212 |       },
213 |       {
214 |         "id": "all"
215 |       }
216 |     ],
217 |     "im_addresses": [
218 |       {
219 |         "type": "jabber",
220 |         "im_address": "myjabberaddress"
221 |       },
222 |       {
223 |         "type": "msn",
224 |         "im_address": "mymsnaddress"
225 |       }
226 |     ],
227 |     "job_title": "Software Engineer",
228 |     "manager_name": "Bill",
229 |     "middle_name": "Jacob",
230 |     "nickname": "JD",
231 |     "notes": "Loves ramen",
232 |     "office_location": "123 Main Street",
233 |     "phone_numbers": [
234 |       {
235 |         "number": "+1-555-555-5555",
236 |         "type": "work"
237 |       },
238 |       {
239 |         "number": "+1-555-555-5556",
240 |         "type": "home"
241 |       }
242 |     ],
243 |     "physical_addresses": [
244 |       {
245 |         "type": "work",
246 |         "street_address": "123 Main Street",
247 |         "postal_code": 94107,
248 |         "state": "CA",
249 |         "country": "USA",
250 |         "city": "San Francisco"
251 |       },
252 |       {
253 |         "type": "home",
254 |         "street_address": "456 Main Street",
255 |         "postal_code": 94107,
256 |         "state": "CA",
257 |         "country": "USA",
258 |         "city": "San Francisco"
259 |       }
260 |     ],
261 |     "suffix": "Jr.",
262 |     "surname": "Doe",
263 |     "web_pages": [
264 |       {
265 |         "type": "work",
266 |         "url": "http://www.linkedin.com/in/johndoe"
267 |       },
268 |       {
269 |         "type": "home",
270 |         "url": "http://www.johndoe.com"
271 |       }
272 |     ]
273 |   }'
274 | ```
275 | 
276 | ```Node
277 | import 'dotenv/config'
278 | import Nylas from 'nylas'
279 | 
280 | const NylasConfig = {
281 |   apiKey: process.env.NYLAS_API_KEY,
282 |   apiUri: process.env.NYLAS_API_URI,
283 | }
284 | 
285 | const nylas = new Nylas(NylasConfig)
286 | 
287 | async function createContact() {
288 |   try {
289 |     const contact = await nylas.contacts.create({
290 |       identifier: process.env.NYLAS_GRANT_ID,
291 |       requestBody: {
292 |         givenName: "My",
293 |         middleName: "Nylas",
294 |         surname: "Friend",
295 |         notes: "Make sure to keep in touch!",
296 |         emails: [{type: 'work', email: '[email protected]'}],
297 |         phoneNumbers: [{type: 'work', number: '(555) 555-5555'}],
298 |         webPages: [{type: 'other', url: 'nylas.com'}]
299 |       }
300 |     })
301 | 
302 |     console.log('Contact:', JSON.stringify(contact))
303 |   } catch (error) {
304 |     console.error('Error to create contact:', error)
305 |   }
306 | }
307 | 
308 | createContact()
309 | ```
310 | 
311 | ```Ruby
312 | require 'nylas' 
313 | 
314 | nylas = Nylas::Client.new(api_key: "<NYLAS_API_KEY>")
315 | 
316 | request_body = {
317 |   given_name: "My",
318 |   middle_name: "Nylas",
319 |   surname: "Friend",  
320 |   emails: [{email: "[email protected]", type: "work"}],
321 |   notes: "Make sure to keep in touch!",
322 |   phone_numbers: [{number: "555 555-5555", type: "business"}],
323 |   web_pages: [{url: "https://www.nylas.com", type: "homepage"}]
324 | }
325 | 
326 | contact, _ = nylas.contacts.create(identifier: "<NYLAS_GRANT_ID>", request_body: request_body)
327 | 
328 | puts contact
329 | ```
330 | 
331 | ```Python
332 | from dotenv import load_dotenv
333 | load_dotenv()
334 | 
335 | import os
336 | import sys
337 | from nylas import Client
338 | 
339 | nylas = Client(
340 |     os.environ.get('NYLAS_API_KEY'),
341 |     os.environ.get('NYLAS_API_URI')
342 | )
343 | 
344 | grant_id = os.environ.get("NYLAS_GRANT_ID")
345 | 
346 | contact = nylas.contacts.create(
347 |     grant_id,
348 |     request_body={
349 |       "middleName": "Nylas",
350 |       "surname": "Friend",
351 |       "notes": "Make sure to keep in touch!",
352 |       "emails": [{"type": "work", "email": "[email protected]"}],
353 |       "phoneNumbers": [{"type": "work", "number": "(555) 555-5555"}],
354 |       "webPages": [{"type": "other", "url": "nylas.com"}]
355 |     }
356 | )
357 | 
358 | print(contact)
359 | ```
360 | 
361 | ```Java
362 | import com.nylas.NylasClient;
363 | import com.nylas.models.*;
364 | import java.util.ArrayList;
365 | import java.util.List;
366 | 
367 | public class CreateAContact {
368 |   public static void main(String[] args) throws NylasSdkTimeoutError, NylasApiError {
369 |     NylasClient nylas = new NylasClient.Builder("<NYLAS_API_KEY>").build();
370 | 
371 |     List<ContactEmail> contactEmails = new ArrayList<>();
372 |     contactEmails.add(new ContactEmail("[email protected]", "work"));
373 | 
374 |     List<WebPage> contactWebpages = new ArrayList<>();
375 |     contactWebpages.add(new WebPage("https://www.nylas.com", "work"));
376 | 
377 |     CreateContactRequest requestBody = new CreateContactRequest.Builder().
378 |         emails(contactEmails).
379 |         companyName("Nylas").
380 |         givenName("Nylas' Swag").
381 |         notes("This is good swag").
382 |         webPages(contactWebpages).
383 |         build();
384 | 
385 |     Response<Contact> contact = nylas.contacts().create("<NYLAS_GRANT_ID>", requestBody);
386 | 
387 |     System.out.println(contact);
388 |   }
389 | }
390 | ```
391 | 
392 | ### Delete a specific contact
393 | 
394 | `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-)
395 | 
396 | ```API
397 | curl --request DELETE \
398 |   --url https://api.us.nylas.com/v3/grants/<NYLAS_GRANT_ID>/contacts/<CONTACT_ID> \
399 |   --header 'Accept: application/json' \
400 |   --header 'Authorization: Bearer <NYLAS_API_KEY>' \
401 |   --header 'Content-Type: application/json'
402 | ```
403 | 
404 | ```Node
405 | import 'dotenv/config'
406 | import Nylas from 'nylas'
407 | 
408 | const NylasConfig = {
409 |   apiKey: process.env.NYLAS_API_KEY,
410 |   apiUri: process.env.NYLAS_API_URI,
411 | }
412 | 
413 | const nylas = new Nylas(NylasConfig)
414 | const identifier = process.env.NYLAS_GRANT_ID
415 | const contactId = process.env.CONTACT_ID
416 | 
417 | const deleteContact = async () => {
418 |   try {
419 |     await nylas.contacts.destroy({ identifier, contactId })
420 |     console.log(`Contact with ID ${contactId} deleted successfully.`)
421 |   } catch (error) {
422 |     console.error(`Error deleting contact with ID ${contactId}:`, error)
423 |   }
424 | }
425 | 
426 | deleteContact()
427 | ```
428 | 
429 | ```Ruby
430 | require 'nylas' 
431 | 
432 | nylas = Nylas::Client.new(api_key: "<NYLAS_API_KEY>")
433 | status, _ = nylas.contacts.destroy(identifier: "<NYLAS_GRANT_ID>", contact_id: "<CONTACT_ID>")
434 | 
435 | puts status
436 | ```
437 | 
438 | ```Python
439 | from dotenv import load_dotenv
440 | load_dotenv()
441 | 
442 | import os
443 | import sys
444 | from nylas import Client
445 | 
446 | nylas = Client(
447 |     os.environ.get('NYLAS_API_KEY'),
448 |     os.environ.get('NYLAS_API_URI')
449 | )
450 | 
451 | grant_id = os.environ.get("NYLAS_GRANT_ID")
452 | contact_id = os.environ.get("CONTACT_ID")
453 | 
454 | request = nylas.contacts.destroy(
455 |     grant_id,
456 |     contact_id,
457 | )
458 | 
459 | print(request)
460 | ```
461 | 
462 | ```Java
463 | import com.nylas.NylasClient;
464 | import com.nylas.models.*;
465 | 
466 | public class DeleteAContact {
467 |   public static void main(String[] args) throws NylasSdkTimeoutError, NylasApiError {
468 |     NylasClient nylas = new NylasClient.Builder("<NYLAS_API_KEY>").build();
469 |     DeleteResponse contact = nylas.contacts().destroy("<NYLAS_GRANT_ID>", "<CONTACT_ID>");
470 | 
471 |     System.out.println(contact);
472 |   }
473 | }
474 | ```
475 | 
```

--------------------------------------------------------------------------------
/src/resources/docs.ts:
--------------------------------------------------------------------------------

```typescript
  1 | import { McpServer, ResourceTemplate } from "@modelcontextprotocol/sdk/server/mcp.js";
  2 | 
  3 | /**
  4 |  * Register documentation resources for the Nylas API
  5 |  */
  6 | export function registerDocsResources(server: McpServer) {
  7 |   // Register resource for Email API documentation
  8 |   server.resource(
  9 |     "email-api-docs",
 10 |     "nylas://docs/email",
 11 |     async (uri) => ({
 12 |       contents: [{
 13 |         uri: uri.href,
 14 |         text: `# Nylas Email API
 15 | 
 16 | The Nylas Email API allows you to read, send, and organize emails across multiple providers.
 17 | 
 18 | ## Key Features
 19 | 
 20 | - **Read Emails**: Fetch emails from the user's inbox, sent folder, or custom folders/labels
 21 | - **Send Emails**: Compose and send emails with attachments and rich formatting
 22 | - **Search**: Search emails by various criteria
 23 | - **Thread Management**: Group related emails into threads
 24 | - **Labels/Folders**: Organize emails with labels or folders
 25 | - **Attachments**: Upload and download email attachments
 26 | - **Drafts**: Create, update, and manage email drafts
 27 | 
 28 | ## Core Endpoints
 29 | 
 30 | - GET /messages - List messages
 31 | - GET /messages/{id} - Get a specific message
 32 | - POST /messages - Send a new message
 33 | - PUT /messages/{id} - Update a message
 34 | - DELETE /messages/{id} - Delete a message
 35 | - GET /threads - List threads
 36 | - GET /threads/{id} - Get a specific thread
 37 | 
 38 | ## Primary Resources
 39 | 
 40 | - **Message**: An individual email message
 41 | - **Thread**: A collection of related messages
 42 | - **Attachment**: A file attached to a message
 43 | - **Draft**: An unsent message
 44 | 
 45 | ## Webhooks and Events
 46 | 
 47 | Nylas can notify your application in real-time when emails are received, updated, or deleted through webhooks.`,
 48 |         mimeType: "text/markdown"
 49 |       }]
 50 |     })
 51 |   );
 52 | 
 53 |   // Register resource for Calendar API documentation
 54 |   server.resource(
 55 |     "calendar-api-docs",
 56 |     "nylas://docs/calendar",
 57 |     async (uri) => ({
 58 |       contents: [{
 59 |         uri: uri.href,
 60 |         text: `# Nylas Calendar API
 61 | 
 62 | The Nylas Calendar API allows you to read, create, update, and delete calendar events across multiple providers.
 63 | 
 64 | ## Key Features
 65 | 
 66 | - **Events Management**: Create, read, update, and delete calendar events
 67 | - **Calendars**: Manage multiple calendars
 68 | - **Attendees**: Invite and manage event participants
 69 | - **Availability**: Check for free/busy times
 70 | - **Recurring Events**: Create and manage recurring event patterns
 71 | - **Reminders**: Set up notifications for upcoming events
 72 | - **Time Zone Support**: Handle events across different time zones
 73 | 
 74 | ## Core Endpoints
 75 | 
 76 | - GET /events - List events
 77 | - GET /events/{id} - Get a specific event
 78 | - POST /events - Create a new event
 79 | - PUT /events/{id} - Update an event
 80 | - DELETE /events/{id} - Delete an event
 81 | - GET /calendars - List calendars
 82 | - GET /calendars/{id} - Get a specific calendar
 83 | 
 84 | ## Primary Resources
 85 | 
 86 | - **Event**: A calendar event with time, location, and participants
 87 | - **Calendar**: A collection of events
 88 | - **Participant**: An attendee of an event
 89 | - **Free/Busy**: Availability information
 90 | 
 91 | ## Webhooks and Events
 92 | 
 93 | Nylas can notify your application in real-time when calendar events are created, updated, or deleted through webhooks.`,
 94 |         mimeType: "text/markdown"
 95 |       }]
 96 |     })
 97 |   );
 98 | 
 99 |   // Register resource for Contacts API documentation
100 |   server.resource(
101 |     "contacts-api-docs",
102 |     "nylas://docs/contacts",
103 |     async (uri) => ({
104 |       contents: [{
105 |         uri: uri.href,
106 |         text: `# Nylas Contacts API
107 | 
108 | The Nylas Contacts API allows you to read, create, update, and delete contacts across multiple providers.
109 | 
110 | ## Key Features
111 | 
112 | - **Contacts Management**: Create, read, update, and delete contacts
113 | - **Groups/Lists**: Organize contacts into groups or lists
114 | - **Search**: Find contacts by name, email, or other attributes
115 | - **Sync**: Keep contacts in sync across different platforms
116 | - **Rich Data**: Store detailed contact information including email, phone, address, etc.
117 | 
118 | ## Core Endpoints
119 | 
120 | - GET /contacts - List contacts
121 | - GET /contacts/{id} - Get a specific contact
122 | - POST /contacts - Create a new contact
123 | - PUT /contacts/{id} - Update a contact
124 | - DELETE /contacts/{id} - Delete a contact
125 | 
126 | ## Primary Resources
127 | 
128 | - **Contact**: A person with contact information
129 | - **Group**: A collection of contacts (supported by some providers)
130 | 
131 | ## Webhooks and Events
132 | 
133 | Nylas can notify your application in real-time when contacts are created, updated, or deleted through webhooks.`,
134 |         mimeType: "text/markdown"
135 |       }]
136 |     })
137 |   );
138 | 
139 |   // Register resource for Webhook documentation
140 |   server.resource(
141 |     "webhooks-docs",
142 |     "nylas://docs/webhooks",
143 |     async (uri) => ({
144 |       contents: [{
145 |         uri: uri.href,
146 |         text: `# Nylas Webhooks
147 | 
148 | Nylas Webhooks allow your application to receive real-time notifications when data changes in a user's account.
149 | 
150 | ## Key Features
151 | 
152 | - **Real-time Updates**: Get notified immediately when data changes
153 | - **Filtered Notifications**: Subscribe to specific types of events
154 | - **Reduced API Calls**: Eliminate polling for changes
155 | - **Secure Delivery**: Verify webhook authenticity with signatures
156 | 
157 | ## Setting Up Webhooks
158 | 
159 | 1. **Create Webhook**: Register a webhook URL with Nylas
160 | 2. **Configure Triggers**: Specify which events should trigger notifications
161 | 3. **Handle Notifications**: Process incoming webhook payloads
162 | 4. **Verify Signatures**: Ensure webhooks are authentic using the provided signature
163 | 
164 | ## Event Types
165 | 
166 | - **message.created**: A new email message was received
167 | - **message.updated**: An email message was updated
168 | - **message.deleted**: An email message was deleted
169 | - **thread.replied**: A reply was added to a thread
170 | - **calendar.created**: A calendar was created
171 | - **event.created**: A calendar event was created
172 | - **event.updated**: A calendar event was updated
173 | - **event.deleted**: A calendar event was deleted
174 | - **contact.created**: A contact was created
175 | - **contact.updated**: A contact was updated
176 | - **contact.deleted**: A contact was deleted
177 | 
178 | ## Webhook Management
179 | 
180 | Webhooks can be created, updated, and deleted through the Nylas Dashboard or API.`,
181 |         mimeType: "text/markdown"
182 |       }]
183 |     })
184 |   );
185 | 
186 |   // Register documentation for a specific resource by name
187 |   server.resource(
188 |     "docs-by-topic",
189 |     new ResourceTemplate("nylas://docs/{topic}", { list: undefined }),
190 |     async (uri, { topic }) => {
191 |       // Convert topic to string if it's an array
192 |       const topicStr = typeof topic === 'string' ? topic : topic[0];
193 |       
194 |       // Map of topics to their content
195 |       const topics: Record<string, string> = {
196 |         "authentication": `# Nylas Authentication
197 | 
198 | Nylas uses OAuth 2.0 for authentication. This secure protocol allows users to grant your application access to their email, calendar, and contacts data without sharing their passwords.
199 | 
200 | ## Authentication Flow
201 | 
202 | 1. **Redirect to Nylas**: Your application redirects the user to the Nylas authorization page
203 | 2. **User Authorization**: The user logs in to their email provider and authorizes your application
204 | 3. **Redirect Back**: Nylas redirects back to your application with an authorization code
205 | 4. **Token Exchange**: Your server exchanges the code for an access token
206 | 5. **API Access**: Use the access token to make API requests on behalf of the user
207 | 
208 | ## Security Best Practices
209 | 
210 | - Store tokens securely, treat them like passwords
211 | - Implement token refresh logic for long-lived access
212 | - Use HTTPS for all API requests
213 | - Never expose your client secret in client-side code
214 | - Implement proper token revocation when users disconnect
215 | 
216 | ## Native Authentication
217 | 
218 | For some providers, Nylas also offers a native authentication option that allows users to enter their credentials directly in your application.`,
219 | 
220 |         "pagination": `# Pagination in Nylas API
221 | 
222 | Most Nylas API endpoints that return collections of resources support pagination to efficiently handle large result sets.
223 | 
224 | ## How Pagination Works
225 | 
226 | Nylas uses cursor-based pagination for consistent results when data is changing:
227 | 
228 | 1. **Initial Request**: Make a request without pagination parameters
229 | 2. **Response**: The API returns a batch of results and pagination metadata
230 | 3. **Next Page**: Use the \`offset\` parameter from the response to fetch the next page
231 | 4. **Continue**: Repeat until all desired results are retrieved or no more results are available
232 | 
233 | ## Pagination Parameters
234 | 
235 | - **limit**: Number of items to return (default and max values vary by endpoint)
236 | - **offset**: Cursor for the next page of results
237 | 
238 | ## Example Response with Pagination
239 | 
240 | \`\`\`json
241 | {
242 |   "data": [
243 |     // Resource objects
244 |   ],
245 |   "next_cursor": "ZGF0ZT0xNjQ2NjE0ODAwMDAwJnJvd0lkPTEwMDA=",
246 |   "has_more": true
247 | }
248 | \`\`\`
249 | 
250 | ## Efficient Pagination
251 | 
252 | - Use appropriate limit values for your use case
253 | - Only fetch as many pages as needed
254 | - Consider implementing virtual scrolling for large datasets
255 | - Cache results when appropriate`,
256 | 
257 |         "rate-limits": `# Nylas API Rate Limits
258 | 
259 | Nylas implements rate limits to ensure fair usage of the platform and maintain service stability.
260 | 
261 | ## Rate Limit Structure
262 | 
263 | Nylas uses a token bucket system for rate limiting:
264 | 
265 | - Each endpoint has a specific rate limit
266 | - Rate limits are applied per user account
267 | - Some endpoints have higher limits than others based on their usage patterns
268 | 
269 | ## Rate Limit Headers
270 | 
271 | When you make API requests, Nylas includes rate limit information in the response headers:
272 | 
273 | - **X-RateLimit-Limit**: Total number of requests allowed in the current time window
274 | - **X-RateLimit-Remaining**: Number of requests remaining in the current time window
275 | - **X-RateLimit-Reset**: Time (in seconds) until the rate limit resets
276 | 
277 | ## Handling Rate Limits
278 | 
279 | When you exceed a rate limit, Nylas returns a 429 Too Many Requests status code. Best practices for handling rate limits:
280 | 
281 | 1. Check the rate limit headers in each response
282 | 2. Implement exponential backoff when you receive a 429 response
283 | 3. Spread requests evenly over time when possible
284 | 4. For bulk operations, use batch endpoints where available
285 | 5. Cache data when appropriate to reduce API calls
286 | 
287 | ## Rate Limit Increases
288 | 
289 | If your application requires higher rate limits, you can contact Nylas support to discuss your needs and possible solutions.`,
290 | 
291 |         "errors": `# Nylas API Error Handling
292 | 
293 | Understanding and properly handling Nylas API errors will help create a robust integration.
294 | 
295 | ## Error Format
296 | 
297 | Nylas API errors follow a consistent format:
298 | 
299 | \`\`\`json
300 | {
301 |   "error": {
302 |     "type": "invalid_request_error",
303 |     "message": "A human-readable description of the error",
304 |     "param": "Optional parameter that caused the error"
305 |   }
306 | }
307 | \`\`\`
308 | 
309 | ## Common Error Types
310 | 
311 | - **invalid_request_error**: The request was malformed or contained invalid parameters
312 | - **authentication_error**: Authentication failed (invalid token, expired token, etc.)
313 | - **resource_not_found_error**: The requested resource does not exist
314 | - **rate_limit_error**: You have exceeded the rate limit for the endpoint
315 | - **provider_error**: The email provider returned an error
316 | - **internal_error**: An unexpected error occurred on the Nylas servers
317 | 
318 | ## HTTP Status Codes
319 | 
320 | - **400** Bad Request: Invalid request parameters
321 | - **401** Unauthorized: Authentication failed
322 | - **403** Forbidden: Insufficient permissions
323 | - **404** Not Found: Resource not found
324 | - **429** Too Many Requests: Rate limit exceeded
325 | - **500** Internal Server Error: Unexpected server error
326 | - **503** Service Unavailable: Temporary server unavailability
327 | 
328 | ## Error Handling Best Practices
329 | 
330 | 1. Parse the error type and message to determine the appropriate action
331 | 2. Implement retries with exponential backoff for transient errors (429, 500, 503)
332 | 3. Log errors for troubleshooting
333 | 4. Handle authentication errors by refreshing tokens or prompting for re-authentication
334 | 5. Provide clear feedback to users when errors occur`
335 |       };
336 |       
337 |       // Return the content for the requested topic, or a not found message
338 |       const content = topics[topicStr] || `# Topic Not Found\n\nThe requested topic "${topicStr}" is not available.`;
339 |       
340 |       return {
341 |         contents: [{
342 |           uri: uri.href,
343 |           text: content,
344 |           mimeType: "text/markdown"
345 |         }]
346 |       };
347 |     }
348 |   );
349 | }
```

--------------------------------------------------------------------------------
/nylas-code-samples/Auth/Hosted/index.md:
--------------------------------------------------------------------------------

```markdown
  1 | Nylas v3 introduces a Hosted OAuth system that completely replaces v2 Hosted Auth. The new Hosted OAuth system is OAuth 2.0-compliant, and offers multiple ways to authorize requests after you complete the authentication process. Nylas also offers PKCE options for use with single page applications (SPAs) and mobile apps.
  2 | 
  3 | OAuth access tokens expire after one hour, so for most applications, Nylas recommends authenticating end users with OAuth, then using an application API key to authorize requests so you don't need to worry about refresh tokens. You can also choose to receive a refresh token during the authentication process, then use it to get a new access token when an older one expires.
  4 | 
  5 | To access your end users' data using pure OAuth, pass the access token as the Bearer token in your request's auth header.
  6 | 
  7 | See [Authentication in Nylas v3](https://developer.nylas.com/docs/v3/auth/) and the [Authentication references](https://developer.nylas.com/docs/api/v3/admin/#tag--Authentication-APIs) for more details.
  8 | 
  9 | ### New Hosted OAuth endpoints
 10 | 
 11 | - Revoke an OAuth token: [`POST /v3/connect/revoke`](https://developer.nylas.com/docs/api/v3/admin/#post-/v3/connect/revoke)
 12 | 
 13 | ### Migrated Hosted auth endpoints
 14 | 
 15 | - Hosted auth - Authenticate user → Hosted OAuth - Authorization request (`GET /oauth/authorize` → [`GET /v3/connect/auth`](https://developer.nylas.com/docs/api/v3/admin/#get-/v3/connect/auth))
 16 | - Hosted auth - Send authorization code → Hosted OAuth - Token exchange (`POST /oauth/token` → [`POST /v3/connect/token`](https://developer.nylas.com/docs/api/v3/admin/#post-/v3/connect/token))
 17 | - Get token information: `POST /a/<NYLAS_CLIENT_ID>/accounts/<NYLAS_ACCOUNT_ID>/token-info` → [`GET /v3/connect/tokeninfo`](https://developer.nylas.com/docs/api/v3/admin/#get-/v3/connect/tokeninfo)
 18 | 
 19 | ### Deprecated Hosted auth endpoints
 20 | 
 21 | - Revoke all access tokens: `POST /a/<NYLAS_CLIENT_ID>/accounts/<NYLAS_ACCOUNT_ID>/revoke-all`
 22 | 
 23 | ### Start a Hosted OAuth authorization request
 24 | 
 25 | `GET /oauth/authorize` → [`GET /v3/connect/auth`](https://developer.nylas.com/docs/api/v3/admin/#get-/v3/connect/auth)
 26 | 
 27 | ```Node
 28 | import 'dotenv/config'
 29 | import express from 'express'
 30 | import Nylas from 'nylas'
 31 | 
 32 | const config = {
 33 |   clientId: process.env.NYLAS_CLIENT_ID,
 34 |   callbackUri: "http://localhost:3000/oauth/exchange",
 35 |   apiKey: process.env.NYLAS_API_KEY,
 36 |   apiUri: process.env.NYLAS_API_URI,
 37 | }
 38 | 
 39 | const nylas = new Nylas({ 
 40 |   apiKey: config.apiKey, 
 41 |   apiUri: config.apiUri,
 42 | })
 43 | 
 44 | const app = express()
 45 | const port = 3000
 46 | 
 47 | // Route to initialize authentication.
 48 | app.get('/nylas/auth', (req, res) => {
 49 |   const authUrl = nylas.auth.urlForOAuth2({
 50 |     clientId: config.clientId,
 51 |     provider: 'google',
 52 |     redirectUri: config.redirectUri,
 53 |     loginHint: 'email_to_connect',
 54 |     accessType: 'offline',
 55 |   })
 56 | 
 57 |   res.redirect(authUrl)
 58 | })   
 59 | ```
 60 | 
 61 | ```Java
 62 | get("/nylas/auth", (request, response) -> {
 63 |   List<String> scope = new ArrayList<>();
 64 |   scope.add("https://www.googleapis.com/auth/calendar");
 65 | 
 66 |   UrlForAuthenticationConfig config = new UrlForAuthenticationConfig("<NYLAS_CLIENT_ID>",
 67 |       "http://localhost:4567/oauth/exchange",
 68 |       AccessType.ONLINE,
 69 |       AuthProvider.GOOGLE,
 70 |       Prompt.DETECT,
 71 |       scope,
 72 |       true,
 73 |       "sQ6vFQN",
 74 |       "[email protected]");
 75 | 
 76 |   String url = nylas.auth().urlForOAuth2(config);
 77 |   
 78 |   response.redirect(url);
 79 |   return null;
 80 | });   
 81 |         
 82 | get("/oauth/exchange", (request, response) -> {
 83 |   String code = request.queryParams("code");
 84 | 
 85 |   if(code == null) { response.status(401);}
 86 |   assert code != null;
 87 | 
 88 |   CodeExchangeRequest codeRequest = new CodeExchangeRequest(
 89 |       "http://localhost:4567/oauth/exchange",
 90 |       code,
 91 |       "<NYLAS_CLIENT_ID>",
 92 |       null,
 93 |       null);
 94 | 
 95 |   try{
 96 |     CodeExchangeResponse codeResponse = nylas.auth().exchangeCodeForToken(codeRequest);
 97 | 
 98 |     request.session().attribute("grant_id", codeResponse.getGrantId());
 99 | 
100 |     return "%s".formatted(codeResponse.getGrantId());
101 |   }catch(Exception e){
102 |     return  "%s".formatted(e);
103 |   }
104 | });           
105 | ```
106 | 
107 | ```Python
108 | @app.route("/nylas/auth", methods=["GET"])
109 | def login():
110 |   if session.get("grant_id") is None:
111 |     config = URLForAuthenticationConfig({"client_id": "<NYLAS_CLIENT_ID>", 
112 |         "redirect_uri" : "http://localhost:5000/oauth/exchange"})
113 | 
114 |     url = nylas.auth.url_for_oauth2(config)
115 |     return redirect(url)
116 |   else:
117 |     return f'{session["grant_id"]}'
118 | 
119 | @app.route("/oauth/exchange", methods=["GET"])
120 | def authorized():
121 |   if session.get("grant_id") is None:
122 |     code = request.args.get("code")
123 | 
124 |     exchangeRequest = CodeExchangeRequest({"redirect_uri": "http://localhost:5000/oauth/exchange",
125 |         "code": code, "client_id": "<NYLAS_CLIENT_ID>"})
126 | 
127 |     exchange = nylas.auth.exchange_code_for_token(exchangeRequest)
128 |     session["grant_id"] = exchange.grant_id
129 | 
130 |     return redirect(url_for("login"))
131 | ```
132 | 
133 | ```Ruby
134 | get '/nylas/auth' do
135 |   config = {
136 |     client_id: '<NYLAS_CLIENT_ID>',
137 |     provider: 'google',
138 |     redirect_uri: 'http://localhost:4567/oauth/exchange',
139 |     login_hint: '[email protected]',
140 |     access_type: 'offline'
141 |   }
142 | 
143 |   url = nylas.auth.url_for_oauth2(config)
144 |   redirect url
145 | end   
146 | 
147 | 
148 | get '/oauth/exchange' do
149 |   code = params[:code]
150 |   status 404 if code.nil?
151 | 
152 |   begin
153 |     response = nylas.auth.exchange_code_for_token({
154 |         client_id: '<NYLAS_CLIENT_ID>',
155 |         redirect_uri: 'http://localhost:4567/oauth/exchange',
156 |         code: code})
157 |   rescue StandardError
158 |     status 500
159 |   else
160 |     response[:grant_id]
161 |     response[:email]
162 |     session[:grant_id] = response[:grant_id]
163 |   end
164 | end
165 | ```
166 | 
167 | ```API
168 | curl -X GET "https://api.us.nylas.com/v3/connect/auth?client_id=<CLIENT_ID>&provider=<PROVIDER>&redirect_uri=<REDIRECT_URI>&response_type=<RESPONSE>"
169 | ```
170 | 
171 | ### Get token information
172 | 
173 | `POST /a/<NYLAS_CLIENT_ID>/accounts/<NYLAS_ACCOUNT_ID>/token-info` → [`GET /v3/connect/tokeninfo`](https://developer.nylas.com/docs/api/v3/admin/#get-/v3/connect/tokeninfo)
174 | 
175 | ```Node
176 | import 'dotenv/config'
177 | import Nylas from 'nylas'
178 | 
179 | // Configure the Nylas SDK with your API key and server URL
180 | const NylasConfig = {
181 |   apiKey: process.env.NYLAS_API_KEY,
182 |   apiUri: process.env.NYLAS_API_URI,
183 | }
184 | 
185 | const nylas = new Nylas(NylasConfig)
186 | 
187 | const revokeToken = async () => {
188 |   try {
189 |     const token = process.env.TOKEN
190 |     const response = await nylas.auth.accessTokenInfo(token)
191 |     
192 |     console.log('Token Revoked:', response)
193 |   } catch (error) {
194 |     console.error('Error removing connector:', error)
195 |   }
196 | }
197 | 
198 | revokeToken()
199 | ```
200 | 
201 | ```Java
202 | import com.nylas.NylasClient;
203 | import com.nylas.models.*;
204 | 
205 | public class GetTokenInfo {
206 |   public static void main(String[] args) throws NylasSdkTimeoutError, NylasApiError {
207 |     NylasClient nylas = new NylasClient.Builder("<NYLAS_API_KEY>").build();
208 |     Response<TokenInfoResponse> token = nylas.auth().idTokenInfo("<ACCESS_TOKEN_ID>");
209 |     
210 |     System.out.println(token);
211 |   }
212 | }
213 | ```
214 | 
215 | ```Python
216 | import sys
217 | from nylas import Client
218 | 
219 | nylas = Client(
220 |     '<NYLAS_API_KEY>',
221 |     '<NYLAS_API_URI>'
222 | )
223 | 
224 | request = nylas.auth.id_token_info(
225 |     '<ACCESS_TOKEN_ID>',
226 | )
227 | 
228 | print(request)
229 | ```
230 | 
231 | ```Ruby
232 | require 'nylas' 
233 | 
234 | nylas = Nylas::Client.new(
235 |    api_key: "<NYLAS_API_KEY>"
236 | )
237 | 
238 | query_params = {
239 |     id_token: "<ACCESS_TOKEN_ID>"
240 | }
241 | 
242 | token_info = nylas.auth.access_token_info(query_params: query_params)
243 | 
244 | puts token_info
245 | ```
246 | 
247 | ```API
248 | curl --request GET \
249 |   --url 'https://api.us.nylas.com/v3/connect/tokeninfo?id_token=<ACCESS_TOKEN_ID>&access_token=<NYLAS_ACCESS_TOKEN>' \
250 |   --header 'Accept: application/json' \
251 |   --header 'Authorization: Bearer <NYLAS_API_KEY>' \
252 | ```
253 | 
254 | ### Exchange tokens
255 | 
256 | `POST /connect/token` → [`POST /v3/connect/token`](https://developer.nylas.com/docs/api/v3/admin/#post-/v3/connect/token)
257 | 
258 | ```Node
259 | app.get('/oauth/exchange', async (req, res) => {
260 |   console.log(res.status)
261 | 
262 |   const code = req.query.code
263 | 
264 |   if (!code) {
265 |     res.status(400).send('No authorization code returned from Nylas')
266 |     return
267 |   }
268 | 
269 |   try {
270 |     const response = await nylas.auth.exchangeCodeForToken({ 
271 |       clientId: config.clientId,
272 |       redirectUri: config.redirectUri,
273 |       codeVerifier: 'insert-code-challenge-secret-hash',
274 |       code
275 |     })
276 | 
277 |     const { grantId } = response
278 | 
279 |     res.status(200)
280 |   } catch (error) {
281 |     console.error('Error exchanging code for token:', error)
282 | 
283 |     res.status(500).send('Failed to exchange authorization code for token')
284 |   }
285 | })
286 | ```
287 | 
288 | ```Java
289 | get("/oauth/exchange", (request, response) -> {
290 |   String code = request.queryParams("code");
291 | 
292 |   if(code == null) { response.status(401); }
293 | 
294 |   assert code != null;
295 | 
296 |   CodeExchangeRequest codeRequest = new CodeExchangeRequest(
297 |       "http://localhost:4567/oauth/exchange",
298 |       code,
299 |       "<NYLAS_CLIENT_ID>",
300 |       "nylas"
301 |   );
302 | 
303 |   try {
304 |     CodeExchangeResponse codeResponse = nylas.auth().exchangeCodeForToken(codeRequest);
305 | 
306 |     return "%s".formatted(codeResponse);
307 |   } catch(Exception e) {
308 |     return  "%s".formatted(e);
309 |   }
310 | });
311 | ```
312 | 
313 | ```Python
314 | import json
315 | import os
316 | from functools import wraps
317 | from io import BytesIO
318 | from flask import Flask
319 | from nylas import Client
320 | 
321 | nylas = Client(
322 |     "<NYLAS_CLIENT_ID>",
323 |     "<NYLAS_API_URI>"
324 | )
325 | 
326 | REDIRECT_CLIENT_URI = 'http://localhost:9000/oauth/exchange'
327 | 
328 | @flask_app.route("/oauth/exchange", methods=["GET"])
329 | 
330 | def exchange_code_for_token():
331 |   code_exchange_response = nylas.auth.exchange_code_for_token(
332 |       request={
333 |         "code": request.args.get('code'),
334 |         "client_id": "<NYLAS_CLIENT_ID>",
335 |         "redirect_uri": REDIRECT_CLIENT_URI
336 |       }
337 |   )
338 | 
339 |   return {
340 |     'email_address': code_exchange_response.email,
341 |     'grant_id': code_exchange_response.grant_id
342 |   }
343 | ```
344 | 
345 | ```Ruby
346 | get '/oauth/exchange' do
347 |   code = params[:code]
348 |   status 404 if code.nil?
349 | 
350 |   begin
351 |     response = nylas.auth.exchange_code_for_token({
352 |       client_id: "<NYLAS_CLIENT_ID>",
353 |       redirect_uri: 'http://localhost:4567/oauth/exchange',
354 |       code: code
355 |     })
356 |   rescue StandardError
357 |     status 500
358 |   else
359 |     grant_id = response[:grant_id]
360 |     email = response[:email]
361 | 
362 |     "Grant_Id: #{grant_id} \n Email: #{email}"
363 |   end
364 | end
365 | ```
366 | 
367 | ```API
368 | curl -X POST "https://api.us.nylas.com/v3/connect/token" \
369 |  -H "accept: application/json"\
370 |  -H "content-type: application/json" \
371 |  -d '{"client_id":"<NYLAS_CLIENT_ID>","client_secret":"<NYLAS_API_KEY>","grant_type":"authorization_code","code":"string","redirect_uri":"https://example.com/callback-handler","code_verifier":"nylas"}' \
372 | ```
373 | 
374 | ### Revoke a specific token
375 | 
376 | You can use the [`POST /v3/connect/revoke` endpoint](https://developer.nylas.com/docs/api/v3/admin/#post-/v3/connect/revoke) to revoke a specific access token.
377 | 
378 | ```Node
379 | import 'dotenv/config'
380 | import Nylas from 'nylas'
381 | 
382 | // Configure the Nylas SDK with your API key and server URL
383 | const NylasConfig = {
384 |   apiKey: process.env.NYLAS_API_KEY,
385 |   apiUri: process.env.NYLAS_API_URI,
386 | }
387 | 
388 | const nylas = new Nylas(NylasConfig)
389 | 
390 | const revokeToken = async () => {
391 |   try {
392 |     const token = process.env.TOKEN
393 |     const response = await nylas.auth.revoke(token)
394 |     
395 |     console.log('Token Revoked:', response)
396 |   } catch (error) {
397 |     console.error('Error removing connector:', error)
398 |   }
399 | }
400 | 
401 | revokeToken()
402 | ```
403 | 
404 | ```Java
405 | package org.example;
406 | 
407 | import com.nylas.NylasClient;
408 | import com.nylas.models.TokenParams;
409 | 
410 | public class Revoke_Auth_Token {
411 |     public static void main(String[] args) {
412 |         Dotenv dotenv = Dotenv.load();
413 |         NylasClient nylas = new NylasClient.Builder("<V3_TOKEN_API>").build();
414 |         TokenParams token = new TokenParams("<NYLAS_AUTH_TOKEN>");
415 | 
416 |         try{
417 |         boolean token_status = nylas.auth().revoke(token);
418 |             if(token_status){
419 |                 System.out.println("The token was successfully removed");
420 |             }else{
421 |                 System.out.println("The token cannot be removed");
422 |             }
423 |         }catch (Exception e){
424 |             System.out.println("Invalid token cannot be removed");
425 |         }
426 |     }
427 | }
428 | ```
429 | 
430 | ```Python
431 | from dotenv import load_dotenv
432 | import os
433 | from nylas import Client
434 | 
435 | load_dotenv()
436 | 
437 | nylas = Client(
438 |     os.environ.get('NYLAS_API_KEY')
439 | )
440 | 
441 | try:
442 |     token_state = nylas.auth.revoke("")
443 |     if token_state:
444 |         print("The token was successfully removed")
445 |     else:
446 |         print("The token cannot be removed")
447 | except:
448 |     print("Invalid token cannot be removed")
449 | ```
450 | 
451 | ```Ruby
452 | require 'nylas'
453 | 
454 | nylas = Nylas::Client.new(
455 |   api_key: "<ACCESS_TOKEN_ID>"
456 | )
457 | 
458 | token_state = nylas.auth.revoke("<NYLAS_AUTH_TOKEN>")
459 | if(token_state)
460 |   puts "The token was successfully removed"
461 | else
462 |   puts "Invalid token cannot be removed"
463 | end
464 | ```
465 | 
466 | ```API
467 | curl -X POST "https://api.us.nylas.com/v3/connect/revoke?token=<NYLAS_AUTH_TOKEN>" \
468 |  -H "accept: application/json" \
469 | ```
470 | 
```

--------------------------------------------------------------------------------
/nylas-code-samples/Calendar/Events/write.md:
--------------------------------------------------------------------------------

```markdown
  1 | ### Update a specific event
  2 | 
  3 | PUT /events/{id} → PUT /v3/grants/{grant_id}/events/{event_id}
  4 | 
  5 | ```Node
  6 | import 'dotenv/config'
  7 | import Nylas from 'nylas'
  8 | 
  9 | const NylasConfig = {
 10 |   apiKey: process.env.NYLAS_API_KEY,
 11 |   apiUri: process.env.NYLAS_API_URI
 12 | }
 13 | 
 14 | const nylas = new Nylas(NylasConfig)
 15 | 
 16 | async function addParticipantToAndEvent() {
 17 |   try {
 18 |     const event = await nylas.events.update({
 19 |       identifier: process.env.NYLAS_GRANT_ID,
 20 |       eventId: process.env.EVENT_ID,
 21 |       requestBody: {
 22 |         participants: [{
 23 |           name: 'Nylas DevRel',
 24 |           email: '[email protected]'
 25 |         }]
 26 |       },
 27 |       queryParams: {
 28 |         calendarId: process.env.CALENDAR_ID,
 29 |       },
 30 |     })
 31 |   } catch (error) {
 32 |     console.error('Error adding participant to event:', error)
 33 |   }
 34 | }
 35 | 
 36 | addParticipantToAndEvent()
 37 | ```
 38 | 
 39 | ```Java
 40 | import com.nylas.NylasClient;
 41 | import com.nylas.models.*;
 42 | 
 43 | public class update_calendar_events {
 44 |   public static void main(String[] args) throws NylasSdkTimeoutError, NylasApiError {
 45 |     NylasClient nylas = new NylasClient.Builder("<NYLAS_API_KEY>").build();
 46 |     UpdateEventQueryParams params = new UpdateEventQueryParams("<CALENDAR_ID>", Boolean.FALSE);
 47 |     UpdateEventRequest requestBody = new UpdateEventRequest.Builder().location("Nylas' Theatre'").build();
 48 | 
 49 |     Response<Event> event = nylas.events().update(
 50 |       "<NYLAS_GRANT_ID>",
 51 |       "<EVENT_ID>",
 52 |       requestBody,
 53 |       params);
 54 |   }
 55 | }
 56 | ```
 57 | 
 58 | ```Python
 59 | from dotenv import load_dotenv
 60 | load_dotenv()
 61 | 
 62 | import os
 63 | import sys
 64 | from nylas import Client
 65 | 
 66 | nylas = Client(
 67 |     os.environ.get('NYLAS_API_KEY'),
 68 |     os.environ.get('NYLAS_API_URI')
 69 | )
 70 | 
 71 | grant_id = os.environ.get("NYLAS_GRANT_ID")
 72 | event_id = os.environ.get("EVENT_ID")
 73 | 
 74 | event = nylas.events.update(
 75 |     grant_id,
 76 |     event_id,
 77 |     request_body={
 78 |       "participants": [{
 79 |         "name": "Nylas DevRel",
 80 |         "email": "[email protected]"
 81 |       }]
 82 |     },
 83 |     query_params={
 84 |       "calendar_id": os.environ.get("CALENDAR_ID")
 85 |     }
 86 | )
 87 | 
 88 | print(event)
 89 | ```
 90 | 
 91 | ```Ruby
 92 | require 'nylas'
 93 | 
 94 | nylas = Nylas::Client.new(api_key: "<NYLAS_API_KEY>")
 95 | 
 96 | request_body = {
 97 |     location: "Nylas' Theatre",
 98 | }
 99 | 
100 | query_params = {
101 |     calendar_id: "<NYLAS_GRANT_ID>"
102 | }
103 | 
104 | events, _request_ids = nylas.events.update(
105 |         identifier: "<NYLAS_GRANT_ID>", 
106 |         event_id: "<EVENT_ID>", 
107 |         query_params: query_params,
108 |         request_body: request_body)
109 | ```
110 | 
111 | ```API
112 | curl --request PUT \
113 |   --url https://api.us.nylas.com/v3/grants/<NYLAS_GRANT_ID>/events/<EVENT_ID>?calendar_id=<CALENDAR_ID> \
114 |   --header 'Accept: application/json' \
115 |   --header 'Authorization: Bearer <NYLAS_API_KEY>' \
116 |   --header 'Content-Type: application/json' \
117 |   --data '{
118 |     "title": "Birthday Party",
119 |     "busy": true,
120 |     "participants": [{
121 |       "name": "Dorothy Vaughan",
122 |       "email": "[email protected]",
123 |       "comment": "string"
124 |     }],
125 |     "description": "Come ready to skate",
126 |     "when": {
127 |       "time": 1408875644,
128 |       "timezone": "America/New_York"
129 |     },
130 |     "location": "Roller Rink",
131 |     "recurrence": [
132 |       "RRULE:FREQ=WEEKLY;BYDAY=MO",
133 |       "EXDATE:20211011T000000Z"
134 |     ],
135 |     "conferencing": {
136 |       "provider": "WebEx",
137 |       "details": {
138 |         "password": "string",
139 |         "pin": "string",
140 |         "url": "string"
141 |       }
142 |     },
143 |     "reminder_minutes": "[20]",
144 |     "reminder_method": "popup"
145 | }'
146 | ```
147 | 
148 | ### Create a specific event
149 | 
150 | POST /events → POST /v3/grants/{grant_id}/events
151 | 
152 | ```Node
153 | import 'dotenv/config'
154 | import Nylas from 'nylas'
155 | 
156 | const NylasConfig = {
157 |   apiKey: process.env.NYLAS_API_KEY,
158 |   apiUri: process.env.NYLAS_API_URI,
159 | }
160 | 
161 | const nylas = new Nylas(NylasConfig)
162 | 
163 | const now = Math.floor(Date.now() / 1000)
164 | 
165 | async function createAnEvent() {
166 |   try {
167 |     const event = await nylas.events.create({
168 |       identifier: process.env.NYLAS_GRANT_ID,
169 |       requestBody: {
170 |         title: 'Build With Nylas',
171 |         when: {
172 |           startTime: now,
173 |           endTime: now + 3600,
174 |         }
175 |       },
176 |       queryParams: {
177 |         calendarId: process.env.CALENDAR_ID,
178 |       },
179 |     })
180 | 
181 |     console.log('Event:', event);
182 |   } catch (error) {
183 |     console.error('Error creating event:', error)
184 |   }
185 | }
186 | 
187 | createAnEvent()
188 | ```
189 | 
190 | ```Java
191 | import com.nylas.NylasClient;
192 | import com.nylas.models.*;
193 | 
194 | import java.time.Instant;
195 | import java.time.LocalDate;
196 | import java.time.ZoneOffset;
197 | import java.time.temporal.ChronoUnit;
198 | import java.util.*;
199 | 
200 | public class create_calendar_events {
201 |   public static void main(String[] args) throws NylasSdkTimeoutError, NylasApiError {
202 |     NylasClient nylas = new NylasClient.Builder("<NYLAS_API_KEY>").build();
203 | 
204 |     LocalDate today = LocalDate.now();
205 | 
206 |     Instant sixPmUtc = today.atTime(13, 0).toInstant(ZoneOffset.UTC);
207 | 
208 |     Instant sixPmUtcPlus = sixPmUtc.plus(30, ChronoUnit.MINUTES);
209 | 
210 |     long startTime = sixPmUtc.getEpochSecond();
211 |     long endTime = sixPmUtcPlus.getEpochSecond();
212 | 
213 |     String title = "Let's learn some about the Nylas Java SDK!";
214 |     String location = "Nylas Headquarters";
215 |     String description = "Using the Nylas API with the Java SDK is easy.";
216 | 
217 |     CreateEventRequest.When.Timespan timespan = new CreateEventRequest.
218 |         When.Timespan.
219 |         Builder(Math.toIntExact(startTime), Math.toIntExact(endTime)).
220 |         build();
221 | 
222 |     List<CreateEventRequest.Participant> participants_list = new ArrayList<>();
223 | 
224 |     participants_list.add(new CreateEventRequest.
225 |         Participant("[email protected]", ParticipantStatus.NOREPLY,
226 |         "John Doe", "", ""));
227 | 
228 |     CreateEventRequest createEventRequest = new CreateEventRequest.Builder(timespan)
229 |         .participants(participants_list)
230 |         .title(title)
231 |         .location(location)
232 |         .description(description)
233 |         .build();
234 | 
235 |     CreateEventQueryParams createEventQueryParams = new CreateEventQueryParams.Builder("<CALENDAR_ID>").build();
236 | 
237 |     Event event = nylas.events().create(
238 |         "<NYLAS_GRANT_ID>",
239 |         createEventRequest,
240 |         createEventQueryParams).getData();
241 |   }
242 | }
243 | ```
244 | 
245 | ```Python
246 | from dotenv import load_dotenv
247 | load_dotenv()
248 | 
249 | import os
250 | import sys
251 | from nylas import Client
252 | 
253 | nylas = Client(
254 |     os.environ.get('NYLAS_API_KEY'),
255 |     os.environ.get('NYLAS_API_URI')
256 | )
257 | 
258 | grant_id = os.environ.get("NYLAS_GRANT_ID")
259 | 
260 | events = nylas.events.create(
261 |   grant_id,
262 |   request_body={
263 |     "title": 'Build With Nylas',
264 |     "when": {
265 |       "start_time": 1609372800,
266 |       "end_time": 1609376400
267 |     },
268 |   },
269 |   query_params={
270 |     "calendar_id": os.environ.get("CALENDAR_ID")
271 |   }
272 | )
273 | 
274 | print(events)
275 | ```
276 | 
277 | ```Ruby
278 | require 'nylas'
279 | 
280 | nylas = Nylas::Client.new(api_key: "<NYLAS_API_KEY>")
281 | 
282 | query_params = {
283 |     calendar_id: "<NYLAS_GRANT_ID>"
284 | }
285 | 
286 | today = Date.today
287 | start_time = Time.local(today.year, today.month, today.day, 13, 0, 0).strftime("%s")
288 | end_time = Time.local(today.year, today.month, today.day, 13, 30, 0).strftime("%s")
289 | 
290 | request_body = {
291 |     when: {
292 |         start_time: start_time.to_i,
293 |         end_time: end_time.to_i
294 |     },
295 |     title: "Let's learn some Nylas Ruby SDK!",
296 |     location: "Nylas' Headquarters",
297 |     description: "Using the Nylas API with the Ruby SDK is easy.",
298 |     participants: [{
299 |         name: "Blag",
300 |         email: "[email protected]", 
301 |         status: 'noreply'
302 |     }]
303 | }
304 | 
305 | events, _request_ids = nylas.events.create(
306 |         identifier: "<NYLAS_GRANT_ID>", 
307 |         query_params: query_params,
308 |         request_body: request_body)
309 | ```
310 | 
311 | ```API
312 | curl --request POST \
313 |   --url https://api.us.nylas.com/v3/grants/<NYLAS_GRANT_ID>/events?calendar_id=<CALENDAR_ID> \
314 |   --header 'Accept: application/json' \
315 |   --header 'Authorization: Bearer <NYLAS_API_KEY>' \
316 |   --header 'Content-Type: application/json' \
317 |   --data '{
318 |     "title": "Annual Philosophy Club Meeting",
319 |     "status": "confirmed",
320 |     "busy": true,
321 |     "participants": [
322 |       {
323 |         "name": "Aristotle",
324 |         "email": "[email protected]"
325 |       },
326 |       {
327 |         "name": "Jane Stephens",
328 |         "email": "[email protected]"
329 |       }
330 |     ],
331 |     "description": "Come ready to talk philosophy!",
332 |     "when": {
333 |       "time": 1700060400
334 |     },
335 |     "location": "New York Public Library, Cave room",
336 |     "recurrence": [
337 |       "RRULE:FREQ=WEEKLY;BYDAY=MO",
338 |       "EXDATE:20211011T000000Z"
339 |   ],
340 | }'
341 | ```
342 | 
343 | ### Delete a specific event
344 | 
345 | DELETE /events/{id} → DELETE /v3/grants/{grant_id}/events/{event_id}
346 | 
347 | ```Node
348 | import 'dotenv/config'
349 | import Nylas from 'nylas'
350 | 
351 | const NylasConfig = {
352 |   apiKey: process.env.NYLAS_API_KEY,
353 |   apiUri: process.env.NYLAS_API_URI
354 | }
355 | 
356 | const nylas = new Nylas(NylasConfig)
357 | 
358 | async function deleteEvent() {
359 |   try {
360 |     const event = await nylas.events.destroy({
361 |       identifier: process.env.NYLAS_GRANT_ID,
362 |       eventId: process.env.EVENT_ID,
363 |       queryParams: {
364 |         calendarId: process.env.CALENDAR_ID,
365 |       },
366 |     })
367 | 
368 |     console.log('Event deleted:', event)
369 |   } catch (error) {
370 |     console.error('Error to delete event:', error)
371 |   }
372 | }
373 | 
374 | deleteEvent()
375 | ```
376 | 
377 | ```Java
378 | import com.nylas.NylasClient;
379 | import com.nylas.models.*;
380 | 
381 | public class delete_calendar_events {
382 |   public static void main(String[] args) throws NylasSdkTimeoutError, NylasApiError {
383 |     NylasClient nylas = new NylasClient.Builder("<NYLAS_API_KEY>").build();
384 |     DestroyEventQueryParams queryParams = new DestroyEventQueryParams("<CALENDAR_ID>", Boolean.FALSE);
385 | 
386 |     DeleteResponse event = nylas.events().destroy(
387 |         "<NYLAS_GRANT_ID>",
388 |         "<EVENT_ID>",
389 |         queryParams);
390 |   }
391 | }
392 | ```
393 | 
394 | ```Python
395 | from dotenv import load_dotenv
396 | load_dotenv()
397 | 
398 | import os
399 | import sys
400 | from nylas import Client
401 | 
402 | nylas = Client(
403 |     os.environ.get('NYLAS_API_KEY'),
404 |     os.environ.get('NYLAS_API_URI')
405 | )
406 | 
407 | grant_id = os.environ.get("NYLAS_GRANT_ID")
408 | event_id = os.environ.get("EVENT_ID")
409 | 
410 | event = nylas.events.destroy(
411 |     grant_id,
412 |     event_id,
413 |     query_params={
414 |       "calendar_id": os.environ.get("CALENDAR_ID")
415 |     }
416 | )
417 | 
418 | print(event)
419 | ```
420 | 
421 | ```Ruby
422 | require 'nylas' 
423 | 
424 | nylas = Nylas::Client.new(api_key: "<NYLAS_API_KEY>")
425 | 
426 | query_params = {
427 |   calendar_id: "<NYLAS_GRANT_ID>"
428 | }
429 | 
430 | result, _request_ids = nylas.events.destroy(
431 |     identifier: "<NYLAS_GRANT_ID>", 
432 |     event_id: "<EVENT_ID>",
433 |     query_params: query_params)
434 | 
435 | puts result
436 | ```
437 | 
438 | ```API
439 | curl --request DELETE \
440 |   --url https://api.us.nylas.com/v3/grants/<NYLAS_GRANT_ID>/events/<EVENT_ID>?calendar_id=<CALENDAR_ID> \
441 |   --header 'Accept: application/json' \
442 |   --header 'Authorization: Bearer <NYLAS_API_KEY>' \
443 |   --header 'Content-Type: application/json'
444 | ```
445 | 
446 | ### Send RSVP
447 | 
448 | POST /send-rsvp → POST /v3/grants/{grant_id}/events/{event_id}/send-rsvp
449 | 
450 | ```Node
451 | import 'dotenv/config'
452 | import Nylas from 'nylas'
453 | 
454 | const NylasConfig = {
455 |   apiKey: process.env.NYLAS_API_KEY,
456 |   apiUri: process.env.NYLAS_API_URI
457 | }
458 | 
459 | const nylas = new Nylas(NylasConfig)
460 | 
461 | async function sendEventRSVP() {
462 |   try {
463 |     const event = await nylas.events.update({
464 |       identifier: process.env.NYLAS_GRANT_ID,
465 |       eventId: process.env.EVENT_ID,
466 |       requestBody: {
467 |         participants: [{
468 |           name: 'Nylas DevRel',
469 |           email: '[email protected]',
470 |           status: 'yes',
471 |         }]
472 |       },
473 |       queryParams: {
474 |         calendarId: process.env.CALENDAR_ID,
475 |       },
476 |     })
477 | 
478 |     console.log('Event RSVP:', event)
479 |   } catch (error) {
480 |     console.error('Error to RSVP participant for event:', error)
481 |   }
482 | }
483 | 
484 | sendEventRSVP()
485 | ```
486 | 
487 | ```Java
488 | import com.nylas.NylasClient;
489 | import com.nylas.models.*;
490 | 
491 | public class rsvp {
492 |   public static void main(String[] args) throws NylasSdkTimeoutError, NylasApiError {
493 |     NylasClient nylas = new NylasClient.Builder("<NYLAS_API_KEY>").build();
494 |     SendRsvpRequest requestBody = new SendRsvpRequest(RsvpStatus.YES);
495 |     SendRsvpQueryParams queryParams = new SendRsvpQueryParams("<CALENDAR_ID>");
496 | 
497 |     DeleteResponse rsvp = nylas.events().sendRsvp(
498 |         "<NYLAS_GRANT_ID>",
499 |         "<EVENT_ID>", 
500 |         requestBody,
501 |         queryParams);
502 |         
503 |     System.out.println(rsvp);
504 |   }
505 | }
506 | ```
507 | 
508 | ```Python
509 | from dotenv import load_dotenv
510 | load_dotenv()
511 | 
512 | import os
513 | import sys
514 | from nylas import Client
515 | 
516 | nylas = Client(
517 |     os.environ.get('NYLAS_API_KEY'),
518 |     os.environ.get('NYLAS_API_URI')
519 | )
520 | 
521 | grant_id = os.environ.get("NYLAS_GRANT_ID")
522 | event_id = os.environ.get("EVENT_ID")
523 | 
524 | event = nylas.events.update(
525 |     grant_id,
526 |     event_id,
527 |     request_body={
528 |       "participants": [{
529 |         "name": "Nylas DevRel",
530 |         "email": "[email protected]",
531 |         "status": "yes"
532 |       }]
533 |     },
534 |     query_params={
535 |       "calendar_id": os.environ.get("CALENDAR_ID")
536 |     }
537 | )
538 | 
539 | print(event)
540 | ```
541 | 
542 | ```Ruby
543 | require 'nylas' 
544 | 
545 | nylas = Nylas::Client.new(api_key: "<NYLAS_API_KEY>")
546 | 
547 | request_body = {
548 |   status: "yes"
549 | }
550 | 
551 | query_params = {
552 |   calendar_id: "<CALENDAR_ID>"
553 | }
554 | 
555 | event_rsvp = nylas.events.send_rsvp(
556 |     identifier: "<NYLAS_GRANT_ID>",
557 |     event_id: "<EVENT_ID>", 
558 |     request_body: request_body,
559 |     query_params: query_params)
560 |     
561 | puts event_rsvp
562 | ```
563 | 
564 | ```API
565 | curl --request POST \
566 |   --url https://api.us.nylas.com/v3/grants/<NYLAS_GRANT_ID>/events/<EVENT_ID>/send-rsvp?calendar_id=<CALENDAR_ID> \
567 |   --header 'Accept: application/json' \
568 |   --header 'Authorization: Bearer <NYLAS_API_KEY>' \
569 |   --header 'Content-Type: application/json' \
570 |   --data '{
571 |     "status": "string"
572 | }'
573 | ```
574 | 
```

--------------------------------------------------------------------------------
/nylas-code-samples/Webhooks/index.md:
--------------------------------------------------------------------------------

```markdown
  1 | Follow this general process to upgrade your v2 webhook implementation to v3:
  2 | 
  3 | 1. On your v2 app, make a v2 [`GET /a/<NYLAS_CLIENT_ID>/webhooks` request](https://developer.nylas.com/docs/api/v2/#get-/a/-client_id-/webhooks) to get a list of your existing webhook subscriptions.
  4 | 2. Set up a v3 environment as in the guide above, and make sure your auth systems include [any new scopes for v3 webhook triggers](https://developer.nylas.com/docs/v3/notifications/notification-scopes/) you plan to test.
  5 | 3. Create a grant to test your webhook subscriptions.
  6 | 4. Set up a webhook URL to receive test data.
  7 |     - This endpoint must be able to listen for a [new-subscription verification request](https://developer.nylas.com/docs/v3/notifications/webhooks/#respond-to-webhook-verification-request) from Nylas, and generate a response.
  8 |     - If you don't want to run a full development stack, you can use [VS Code port forwarding](https://code.visualstudio.com/docs/editor/port-forwarding) or a service like [Hookdeck](https://hookdeck.com/?referrer=hi-from-the-nylas-docs) instead.
  9 | 5. Make a v3 [`POST /v3/webhooks` request](https://developer.nylas.com/docs/api/v3/admin/#post-/v3/webhooks) to set up your webhook destination, and include the triggers you want to subscribe to.
 10 | 6. Test some actions and observe incoming data. You can use the new [Send test event API](https://developer.nylas.com/docs/api/v3/admin/#post-/v3/webhooks/send-test-event) to generate webhook messages.
 11 | 
 12 | When your tests meet your requirements, upgrade any code that handles v2 webhooks.
 13 | 
 14 | ### Changes to webhooks
 15 | 
 16 | Nylas v3 includes the following changes to webhooks:
 17 | 
 18 | - Webhook notifications now send object data in a single JSON payload, instead of an array of object IDs.
 19 |   - Nylas v3 returns truncated webhook notifications for payloads greater than 1MB. Nylas adds the `.truncated` suffix (for example, `message.updated.truncated`) and omits large fields. When this happens, you'll need to re-query the Nylas APIs if you want the complete object.
 20 | - You can now set the `description` on a webhook subscription. Use this field as notes to yourself, and information about the subscription and its use.
 21 | - You can now set the `notification_email_addresses` on a webhook subscription. Use this to define a list of email addresses Nylas should notify if the webhook has [deliverability issues](https://developer.nylas.com/docs/v3/notifications/webhooks/#failed-webhooks).
 22 | - The `callback_uri` used by webhook subscriptions is now called `webhook_url`.
 23 | - Webhooks in Nylas v3 are _not_ compatible with Ngrok due to throughput limiting concerns.
 24 | 
 25 | ### Unchanged webhook triggers
 26 | 
 27 | - `calendar.created`
 28 | - `calendar.deleted`
 29 | - `calendar.updated`
 30 | - `contact.deleted`
 31 | - `contact.updated`
 32 | - `event.created`
 33 | - `event.deleted`
 34 | - `event.updated`
 35 | - `folder.created`
 36 | - `folder.deleted`
 37 | - `folder.updated`
 38 | - `message.created`
 39 | - `message.updated`
 40 | 
 41 | ### New webhook triggers
 42 | 
 43 | - `grant.deleted`
 44 | - `grant.updated` (Includes re-authentication.)
 45 | - `grant.expired`
 46 | - `message.send_success` (Scheduled email messages only.)
 47 | - `message.send_failed` (Scheduled email messages only.)
 48 | - `message.bounce_detected` (Scheduled email messages only.)
 49 | - `message.opened` (Tracked email messages only.)
 50 | - `message.link_clicked` (Tracked email messages only.)
 51 | - `thread.replied` (Tracked email messages only.)
 52 | 
 53 | ### Migrated webhook triggers
 54 | 
 55 | - `account.connected` → `grant.created`
 56 | 
 57 | ### Deprecated webhook triggers
 58 | 
 59 | - `account.invalid` (Use `grant.expired` instead.)
 60 | - `account.running`
 61 | - `account.stopped`
 62 | - `account.sync_error`
 63 | - `contact.created` (Use `contact.updated` instead.)
 64 | - `job.successful`
 65 | - `job.failed`
 66 | 
 67 | ### New webhooks endpoints
 68 | 
 69 | - Get a mock payload: [`POST /v3/webhooks/mock-payload`](https://developer.nylas.com/docs/api/v3/admin/#post-/v3/webhooks/mock-payload)
 70 | - Send a test event: [`POST /v3/webhooks/send-test-event`](https://developer.nylas.com/docs/api/v3/admin/#post-/v3/webhooks/send-test-event)
 71 | - Rotate a webhook secret: [`POST /v3/webhooks/rotate-secret/<WEBHOOK_ID>`](https://developer.nylas.com/docs/api/v3/admin/#post-/v3/webhooks/rotate-secret/-id-)
 72 | 
 73 | ### Migrated webhooks endpoints
 74 | 
 75 | - Return all webhook destinations for an application: `GET /a/<NYLAS_CLIENT_ID>/webhooks` → [`GET /v3/webhooks`](https://developer.nylas.com/docs/api/v3/admin/#get-/v3/webhooks)
 76 | - Create a webhook destination: `POST /a/<NYLAS_CLIENT_ID>/webhooks` → [`POST /v3/webhooks`](https://developer.nylas.com/docs/api/v3/admin/#post-/v3/webhooks)
 77 | - Return info for a specific webhook destination: `GET /a/<NYLAS_CLIENT_ID>/webhooks/<WEBHOOK_ID>` → [`GET /v3/webhooks/<WEBHOOK_ID>`](https://developer.nylas.com/docs/api/v3/admin/#get-/v3/webhooks/-id-)
 78 | - Update a specific webhook destination: `PUT /a/<NYLAS_CLIENT_ID>/webhooks/<WEBHOOK_ID>` → [`PUT /v3/webhooks/<WEBHOOK_ID>`](https://developer.nylas.com/docs/api/v3/admin/#put-/v3/webhooks/-id-)
 79 | - Delete a specific webhook destination: `DELETE /a/<NYLAS_CLIENT_ID>/webhooks/<WEBHOOK_ID>` → [`DELETE /v3/webhooks/<WEBHOOK_ID>`](https://developer.nylas.com/docs/api/v3/admin/#delete-/v3/webhooks/-id-)
 80 | - Return application IP addresses → Get webhook source IP addresses (`/a/<NYLAS_CLIENT_ID>/ip_addresses` → [`GET /v3/webhooks/ip-addresses`](https://developer.nylas.com/docs/api/v3/admin/#get-/v3/webhooks/ip-addresses))
 81 | 
 82 | ### Create a webhook
 83 | 
 84 | `POST /webhooks` → `POST /v3/webhooks`
 85 | 
 86 | ```Node
 87 | import 'dotenv/config'
 88 | import Nylas from "nylas"
 89 | 
 90 | const NylasConfig = {
 91 |   apiKey: process.env.NYLAS_API_KEY,
 92 |   apiUri: process.env.NYLAS_API_URI,
 93 | }
 94 | 
 95 | const nylas = new Nylas(NylasConfig)
 96 | 
 97 | const createWebhook = async () => {
 98 |   try {
 99 |     const webhook = await nylas.webhooks.create({
100 |       requestBody: {
101 |         triggerTypes: [WebhookTriggers.EventCreated],
102 |         webhookUrl: process.env.WEBHOOK_URL,
103 |         description: "My first webhook",
104 |         notificationEmailAddress: process.env.EMAIL,
105 |       }
106 |     })
107 | 
108 |     console.log("Webhook created:", webhook)
109 |   } catch (error) {
110 |     console.error("Error creating webhook:", error)
111 |   }
112 | }
113 | 
114 | createWebhook()
115 | ```
116 | 
117 | ```Java
118 | import com.nylas.NylasClient;
119 | import com.nylas.models.*;
120 | import com.nylas.resources.Webhooks;
121 | import com.nylas.models.WebhookTriggers;
122 | import java.util.*;
123 | 
124 | public class webhooks {
125 |   public static void main(String[] args) throws NylasSdkTimeoutError, NylasApiError {
126 |     NylasClient nylas = new NylasClient.Builder("<NYLAS_API_KEY>").build();
127 | 
128 |     List<WebhookTriggers> triggers = new ArrayList<>();
129 |     triggers.add(WebhookTriggers.EVENT_CREATED);
130 | 
131 |     CreateWebhookRequest webhookRequest = new CreateWebhookRequest(triggers, "<WEBHOOK_URL>",
132 |         "My first webhook", "<EMAIL_ADDRESS>");
133 | 
134 |     try {
135 |       Response<WebhookWithSecret> webhook = new Webhooks(nylas).create(webhookRequest);
136 | 
137 |       System.out.println(webhook.getData());
138 |     } catch (Exception e) {
139 |       System.out.println("Error: " + e);
140 |     }
141 |   }
142 | }
143 | ```
144 | 
145 | ```Python
146 | from dotenv import load_dotenv
147 | load_dotenv()
148 | 
149 | import os
150 | import sys
151 | from nylas import Client
152 | from nylas.models.webhooks import WebhookTriggers
153 | 
154 | nylas = Client(
155 |   os.environ.get('NYLAS_API_KEY'),
156 |   os.environ.get('NYLAS_API_URI')
157 | )
158 | 
159 | grant_id = os.environ.get("NYLAS_GRANT_ID")
160 | webhook_url = os.environ.get("WEBHOOK_URL")
161 | email = os.environ.get("EMAIL")
162 | 
163 | webhook = nylas.webhooks.create(
164 |   request_body={
165 |     "trigger_types": [WebhookTriggers.EVENT_CREATED],
166 |     "webhook_url": webhook_url,
167 |     "description": "My first webhook",
168 |     "notification_email_address": email,
169 |   }
170 | )
171 | 
172 | print(webhook)
173 | ```
174 | 
175 | ```Ruby
176 | require 'nylas'
177 | 
178 | nylas = Nylas::Client.new(api_key: "<NYLAS_API_KEY>")
179 | 
180 | request_body = {
181 |   trigger_types: [Nylas::WebhookTrigger::EVENT_CREATED],
182 |   webhook_url: "<WEBHOOK_URL>",
183 |   description: 'My first webhook',
184 |   notification_email_address: ["EMAIL_ADDRESS"]
185 | }
186 | 
187 | begin
188 |   webhooks, = nylas.webhooks.create(request_body: request_body)
189 |   
190 |   puts "Webhook created: #{webhooks}"
191 | rescue StandardError => ex
192 |   puts "Error creating webhook: #{ex}"
193 | end
194 | ```
195 | 
196 | ```API
197 | curl --location 'https://api.us.nylas.com/v3/webhooks/' \
198 | --header 'Content-Type: application/json' \
199 | --header 'Authorization: Bearer <NYLAS_API_KEY>' \
200 | --data-raw '{
201 |   "trigger_types": [
202 |     "grant.created",
203 |     "grant.deleted",
204 |     "grant.expired"
205 |   ],
206 |   "description": "local",
207 |   "webhook_url": "<your webhook url>",
208 |   "notification_email_addresses": ["[email protected]", "[email protected]"]
209 | }'
210 | ```
211 | 
212 | ### Receive Webhook
213 | 
214 | POST /webhook → POST /v3/webhook
215 | 
216 | ```Node
217 | import "dotenv/config";
218 | import express from "express";
219 | import Nylas from "nylas";
220 | import crypto from 'crypto';
221 | 
222 | const config = {
223 |   clientId: process.env.NYLAS_CLIENT_ID,
224 |   apiKey: process.env.NYLAS_API_KEY,
225 |   apiUri: process.env.NYLAS_API_URI,
226 | };
227 | 
228 | const nylas = new Nylas({
229 |   apiKey: config.apiKey,
230 |   apiUri: config.apiUri,
231 | });
232 | 
233 | const app = express();
234 | const port = 3000;
235 | 
236 | app.listen(port, () => {
237 |   console.log(`Server is running on port ${port}`);
238 | });
239 | 
240 | app.get("/callback/nylas", (req, res) => {
241 |   if (req.query.challenge) {
242 |     console.log(`Received challenge code! - ${req.query.challenge}`);
243 |     console.log(`Now returning challenge code! - ${req.query.challenge}`);
244 |     
245 |     return res.send(req.query.challenge);
246 |   }
247 | });
248 | ```
249 | 
250 | ```Java
251 | //webhook_info.java
252 | import lombok.Data;
253 | 
254 | @Data
255 | public class Webhook_Info {
256 |   private String id;
257 |   private String date;
258 |   private String subject;
259 |   private String from_email;
260 |   private String from_name;
261 | }
262 | 
263 | import spark.ModelAndView;
264 | import static spark.Spark.*;
265 | import spark.template.mustache.MustacheTemplateEngine;
266 | import com.fasterxml.jackson.databind.JsonNode;
267 | import com.fasterxml.jackson.databind.ObjectMapper;
268 | 
269 | import java.net.URLEncoder;
270 | import java.text.SimpleDateFormat;
271 | import java.util.ArrayList;
272 | import java.util.HashMap;
273 | import java.util.Map;
274 | 
275 | import org.apache.commons.codec.digest.HmacUtils;
276 | 
277 | public class ReadWebhooks {
278 |   public static String getHmac(String data, String key) {
279 |     return new HmacUtils("HmacSHA256", key).hmacHex(data);
280 |   }
281 | 
282 |   public static void main(String[] args) {
283 |     ArrayList<Webhook_Info> array = new ArrayList<Webhook_Info>();
284 | 
285 |     get("/", (request, response) -> {
286 |       Map<String, Object> model = new HashMap<>();
287 |       model.put("webhooks", array);
288 |       return new ModelAndView(model, "show_webhooks.mustache");
289 |     }, new MustacheTemplateEngine());
290 | 
291 |     get("/webhook", (request, response) -> request.queryParams("challenge"));
292 | 
293 |     post("/webhook", (request, response) -> {
294 |       ObjectMapper mapper = new ObjectMapper();
295 | 
296 |       JsonNode incoming_webhook = mapper.readValue(request.body(), JsonNode.class);
297 | 
298 |       if (getHmac(request.body(), URLEncoder.
299 |           encode(System.getenv("WEBHOOK_SECRET"), "UTF-8")).
300 |           equals(request.headers("X-Nylas-Signature"))) {
301 |             Webhook_Info new_webhook = new Webhook_Info();
302 | 
303 |             System.out.println(incoming_webhook.get("data").get("object"));
304 | 
305 |             new_webhook.setId(incoming_webhook.get("data").
306 |                 get("object").get("id").textValue());
307 | 
308 |             new_webhook.setDate(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").
309 |                 format(new java.util.Date((incoming_webhook.get("data").
310 |                 get("object").get("date").asLong() * 1000L))));
311 | 
312 |             new_webhook.setSubject(incoming_webhook.get("data").
313 |                 get("object").get("subject").textValue());
314 | 
315 |             new_webhook.setFrom_email(incoming_webhook.get("data").
316 |                 get("object").get("from").get(0).get("email").textValue());
317 | 
318 |             new_webhook.setFrom_name(incoming_webhook.get("data").
319 |                 get("object").get("from").get(0).get("name").textValue());
320 | 
321 |             array.add(new_webhook);
322 |       }
323 |       response.status(200);
324 |       return "Webhook Received";
325 |     });
326 |   }
327 | }   
328 | ```
329 | 
330 | ```Python
331 | # Import packages
332 | from flask import Flask, request, render_template
333 | import hmac
334 | import hashlib
335 | import os
336 | from dataclasses import dataclass
337 | import pendulum
338 | 
339 | # Array to hold webhook dataclass
340 | webhooks = []
341 | 
342 | # Webhook dataclass
343 | @dataclass
344 | class Webhook:
345 |   _id: str
346 |   date: str
347 |   subject: str
348 |   from_email: str
349 |   from_name: str
350 | 
351 | # Get today’s date
352 | today = pendulum.now()
353 | 
354 | # Create the Flask app and load the configuration
355 | app = Flask(__name__)
356 | 
357 | # Read and insert webhook data
358 | @app.route("/webhook", methods=["GET", "POST"])
359 | def webhook():
360 |   # We are connected to Nylas, let’s return the challenge parameter.
361 |   if request.method == "GET" and "challenge" in request.args:
362 |     print(" * Nylas connected to the webhook!")
363 |     return request.args["challenge"]
364 | 
365 |   if request.method == "POST":
366 |     is_genuine = verify_signature(
367 |         message=request.data,
368 |         key=os.environ["WEBHOOK_SECRET"].encode("utf8"),
369 |         signature=request.headers.get("X-Nylas-Signature"),
370 |     )
371 | 
372 |     if not is_genuine:
373 |       return "Signature verification failed!", 401
374 |     data = request.get_json()
375 | 
376 |     hook = Webhook(
377 |         data["data"]["object"]["id"],
378 |         pendulum.from_timestamp(
379 |         data["data"]["object"]["date"], today.timezone.name
380 |         ).strftime("%d/%m/%Y %H:%M:%S"),
381 |         data["data"]["object"]["subject"],
382 |         data["data"]["object"]["from"][0]["email"],
383 |         data["data"]["object"]["from"][0]["name"],
384 |     )
385 | 
386 |     webhooks.append(hook)
387 | 
388 |     return "Webhook received", 200
389 | 
390 | # Main page
391 | @app.route("/")
392 | def index():
393 |   return render_template("main.html", webhooks=webhooks)
394 | 
395 | # Signature verification
396 | def verify_signature(message, key, signature):
397 |   digest = hmac.new(key, msg=message, digestmod=hashlib.sha256).hexdigest()
398 | 
399 |   return hmac.compare_digest(digest, signature)
400 | 
401 | # Run our application
402 | if __name__ == "__main__":
403 |   app.run()
404 | ```
405 | 
406 | ```Ruby
407 | # frozen_string_literal: true
408 | 
409 | # Load gems
410 | require 'nylas'
411 | require 'sinatra'
412 | require 'sinatra/config_file'
413 | 
414 | webhook = Data.define(:id, :date, :subject, :from_email, :from_name)
415 | webhooks = []
416 | 
417 | get '/webhook' do
418 |   params['challenge'].to_s if params.include? 'challenge'
419 | end
420 | 
421 | post '/webhook' do
422 |   # We need to verify that the signature comes from Nylas
423 |   is_genuine = verify_signature(request.body.read, ENV['WEBHOOK_SECRET'],
424 |       request.env['HTTP_X_NYLAS_SIGNATURE'])
425 |   unless is_genuine
426 |     status 401
427 |     'Signature verification failed!'
428 |   end
429 | 
430 |   # Read the webhook information and store it on the data class.
431 |   request.body.rewind
432 | 
433 |   model = JSON.parse(request.body.read)
434 | 
435 |   puts(model["data"]["object"])
436 | 
437 |   hook = webhook.new(model["data"]["object"]["id"], 
438 |       Time.at(model["data"]["object"]["date"]).strftime("%d/%m/%Y %H:%M:%S"), 
439 |       model["data"]["object"]["subject"], model["data"]["object"]["from"][0]["email"], 
440 |       model["data"]["object"]["from"][0]["name"])
441 | 
442 |   webhooks.append(hook)
443 | 
444 |   status 200
445 |   'Webhook received'
446 | end
447 | 
448 | get '/' do
449 |   puts webhooks
450 |   erb :main, locals: { webhooks: webhooks }
451 | end
452 | 
453 | # Generate a signature with our client secret and compare it with the one from Nylas.
454 | def verify_signature(message, key, signature)
455 |   digest = OpenSSL::Digest.new('sha256')
456 |   digest = OpenSSL::HMAC.hexdigest(digest, key, message)
457 | 
458 |   secure_compare(digest, signature)
459 | end
460 | 
461 | # Compare the keys to see if they are the same
462 | def secure_compare(a_key, b_key)
463 |   return false if a_key.empty? || b_key.empty? || a_key.bytesize != b_key.bytesize
464 | 
465 |   l = a_key.unpack "C#{a_key.bytesize}"
466 |   res = 0
467 | 
468 |   b_key.each_byte { |byte| res |= byte ^ l.shift }
469 | 
470 |   res.zero?
471 | end
472 | ```
473 | 
474 | ### Update a webhook
475 | 
476 | `PUT /webhooks/<webhook_id>` → `PUT /v3/webhooks/<webhook_id>`
477 | 
478 | ```Node
479 | import 'dotenv/config'
480 | import Nylas from 'nylas'
481 | 
482 | const NylasConfig = {
483 |  apiKey: process.env.NYLAS_API_KEY,
484 |  apiUri: process.env.NYLAS_API_URI,
485 | }
486 | 
487 | const nylas = new Nylas(NylasConfig)
488 | const webhookId = process.env.WEBHOOK_ID
489 | 
490 | async function updateWebhook() {
491 |   try {
492 |     const folder = await nylas.webhooks.update({
493 |       webhookId,
494 |       requestBody: {
495 |         notificationEmailAddresses: [process.env.EMAIL],
496 |       }
497 |     })
498 | 
499 |     console.log('Updated Folder:', folder)
500 |   } catch (error) {
501 |     console.error('Error to update folder:', error)
502 |   }
503 | }
504 | 
505 | updateWebhook()
506 | ```
507 | 
508 | ```Java
509 | import com.nylas.NylasClient;
510 | import com.nylas.models.*;
511 | 
512 | public class webhooks {
513 |     public static void main(String[] args) throws 
514 |     NylasSdkTimeoutError, NylasApiError {
515 | 
516 |         NylasClient nylas = new NylasClient.Builder("<NYLAS_API_KEY>").build();
517 | 
518 |         UpdateWebhookRequest webhookRequest = new
519 |                 UpdateWebhookRequest.Builder().
520 |                 description("My updated webhook").
521 |                 build();
522 | 
523 |         Response<Webhook> webhook = nylas.webhooks().update("<WEBHOOK_ID>", 
524 |         webhookRequest);
525 |         System.out.println(webhook.getData());
526 |     }
527 | }
528 | ```
529 | 
530 | ```Python
531 | from dotenv import load_dotenv
532 | load_dotenv()
533 | 
534 | import os
535 | import sys
536 | from nylas import Client
537 | 
538 | nylas = Client(
539 |     os.environ.get('NYLAS_API_KEY'),
540 |     os.environ.get('NYLAS_API_URI')
541 | )
542 | 
543 | webhook_id = os.environ.get("WEBHOOK_ID")
544 | email = os.environ.get("EMAIL")
545 | 
546 | webhook = nylas.webhooks.update(
547 |   webhook_id,
548 |   request_body={
549 |     "notification_email_addresses": [email],
550 |   }
551 | )
552 | 
553 | print(webhook)
554 | ```
555 | 
556 | ```Ruby
557 | require 'nylas'
558 | 
559 | nylas = Nylas::Client.new(
560 |   api_key: "<NYLAS_API_KEY>"
561 | )
562 | 
563 | request_body = {
564 |     description: 'My updated webhook'
565 | }
566 | 
567 | webhooks = nylas.webhooks.update(webhook_id: "<WEBHOOK_ID>", 
568 | request_body: request_body)
569 | puts webhooks
570 | ```
571 | 
572 | ```API
573 | curl --request PUT \
574 |   --url 'https://api.us.nylas.com/v3/webhooks/<WEBHOOK_ID>' \  
575 |   --header 'Content-Type: application/json' \
576 |   --header 'Accept: application/json' \
577 |   --header 'Authorization: Bearer <NYLAS_API_KEY>' \
578 |   --data '{
579 |     "notification_email_addresses": [
580 |       "EMAIL_ADDRESS_1",
581 |     ]
582 |   }'
583 | ```
584 | 
585 | ### Delete a webhook
586 | 
587 | `DELETE /webhooks/{id}` → `DELETE /v3/webhooks/{id}`
588 | 
589 | ```Node
590 | import 'dotenv/config'
591 | import Nylas from 'nylas'
592 | 
593 | const NylasConfig = {
594 |   apiKey: process.env.NYLAS_API_KEY,
595 |   apiUri: process.env.NYLAS_API_URI,
596 | }
597 | 
598 | const nylas = new Nylas(NylasConfig)
599 | const webhookId = process.env.WEBHOOK_ID
600 | 
601 | const deleteWebhook = async () => {
602 |   try {
603 |     await nylas.webhooks.destroy({ webhookId })
604 |     console.log(`Webhook with ID ${webhookId} deleted successfully.`)
605 |   } catch (error) {
606 |     console.error(`Error deleting webhook with ID ${webhookId}:`, error)
607 |   }
608 | }
609 | 
610 | 
611 | deleteWebhook()
612 | ```
613 | 
614 | ```Java
615 | import com.nylas.NylasClient;
616 | import com.nylas.models.*;
617 | 
618 | public class webhooks {
619 |     public static void main(String[] args) throws 
620 |     NylasSdkTimeoutError, NylasApiError {
621 | 
622 |         NylasClient nylas = new NylasClient.Builder("<NYLAS_API_KEY>").build();
623 | 
624 |         WebhookDeleteResponse deleteResponse = 
625 |         nylas.webhooks().destroy("<WEBHOOK_ID>");
626 |         System.out.println(deleteResponse);
627 |     }
628 | }
629 | ```
630 | 
631 | ```Python
632 | from dotenv import load_dotenv
633 | load_dotenv()
634 | 
635 | import os
636 | import sys
637 | from nylas import Client
638 | 
639 | nylas = Client(
640 |     os.environ.get('NYLAS_API_KEY'),
641 |     os.environ.get('NYLAS_API_URI')
642 | )
643 | 
644 | webhook_id = os.environ.get("WEBHOOK_ID")
645 | 
646 | request = nylas.webhooks.destroy(
647 |   webhook_id,
648 | )
649 | 
650 | print(request)
651 | ```
652 | 
653 | ```Ruby
654 | require 'nylas'
655 | 
656 | nylas = Nylas::Client.new(
657 |   api_key: "<NYLAS_API_KEY>"
658 | )
659 | 
660 | status = nylas.webhooks.destroy(webhook_id: "<WEBHOOK_ID>")
661 | puts status
662 | ```
663 | 
664 | ```API
665 | curl --request DELETE \
666 |   --url 'https://api.us.nylas.com/v3/webhooks/<WEBHOOK_ID>' \
667 |   --header 'Accept: application/json' \
668 |   --header 'Authorization: Bearer <NYLAS_API_KEY>' \
669 |   --header 'Content-Type: application/json'
670 | ```
671 | 
```
Page 2/3FirstPrevNextLast