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 | ```