Nylas supports Hosted OAuth to get the user’s authorization for scopes and create their grant. You can then use their grant ID and your application-specific API key to access their data and make other requests. This allows you to use the same request method for everything in your project, including endpoints that don’t specify a grant (for example, the webhook endpoints).
How Hosted OAuth works
Section titled “How Hosted OAuth works”How Hosted OAuth with an API key creates a grant
code with your API key and keeps only the grant ID. Nylas stores and refreshes the user's provider tokens, so every later request uses your API key and that grant ID.Diagram as text
- You, once: Tell Nylas where to send users back: Register your app's callback URL once, so Nylas knows where to return users after they sign in. Do it in the Dashboard (Hosted authentication > Callback URIs) or with
POST /, platformv3/ applications/ redirect-uris web. - Your app: Send the user to sign in: Your app redirects the user to a Nylas sign-in page that says which app is asking and where to send the user afterwards.
- Request details:
GET /v3/connect/authwithclient_id,redirect_uri, andresponse_type=code. Optional: - Request details:
provider: skip the provider picker, for examplegoogle. - Request details:
login_hint: prefill the user's email address. - Request details:
scope: request specific scopes. Without it, Nylas uses the connector's default scopes. - Request details:
state: a value Nylas returns to you unchanged, up to 256 characters. - URI not registered → Request rejected: Nylas only sends users back to callback URLs you registered. The
redirect_urimust match one exactly.
- Request details:
- User: Signs in: On the provider's consent screen (Google, Microsoft, Yahoo, Zoom), or in a Nylas-hosted form for IMAP, iCloud, and EWS passwords.
- Nylas: Send the user back with a code: Once the user approves, Nylas returns them to your callback URL with a one-time
code. The code proves the user said yes, but it doesn't give access by itself. - Your backend: Trade the code for a grant ID: Your backend sends the code to Nylas together with your API key. Nylas checks both, finishes connecting the user's account (a grant), and returns its grant ID.
- Request details:
POST /v3/connect/tokenwithclient_id, your API key asclient_secret,grant_type=authorization_code, thecode, and the sameredirect_urias before. - Code already used, or
redirect_uridiffers → Exchange fails: A code works only once. Send the user through sign-in again to get a newcode.
- Request details:
- Your backend: Save the grant ID and use it: Store the grant ID with your user's record. To read or send their email and calendar, call the API with that grant ID and your API key. Nylas keeps the user's provider tokens refreshed, so you never handle them.
- Request details:
/v3/grants/{grant_id}/...withAuthorization: Bearer <NYLAS_API_KEY>.
- Request details:
- The user clicks a link or button in your project to start an authorization request.
- Nylas forwards the user to their provider where they complete the authorization flow.
- The provider directs the user to the Nylas callback URI and includes URL parameters that indicate whether the authorization succeeded or failed, along with other information.
- If the authorization succeeded, Nylas creates an unverified grant record.
- Nylas forwards the user to your project’s callback URI and includes a one-time
codeas a URL parameter. - Your project uses the
codeto perform a token exchange with Nylas. - When the token exchange completes successfully, Nylas marks the grant record as verified and creates a grant ID for the user.
Start an authorization request
Section titled “Start an authorization request”The first step of the authentication process is to start an authorization request. Usually this is a button or link that the user clicks.
Your project redirects the user to the authorization request endpoint and includes their information as a set of query parameters, as in the example below. When the user goes to this URL, Nylas starts a secure authentication session and redirects them to their provider’s website.
If you’ve registered a custom hostname, swap api.<region>.nylas.com for it in this URL and everything else about the flow stays the same. See whitelabel Hosted Authentication.
Each provider displays their authorization consent and approval steps differently. The steps are visible only to the user.
Pass user information in state parameter
Section titled “Pass user information in state parameter”Nylas Hosted OAuth supports the optional state parameter. If you include it in an authorization request, Nylas returns the unmodified value to your project. You can use this as a verification check, or to track information about the user that you need when creating a grant or logging them in.
For more information about the state parameter, see the OAuth 2.0 specification and the official OAuth 2.0 documentation.
Accept authorization response
Section titled “Accept authorization response”After the user completes the authorization process, their provider sends them to Nylas’ redirect URI (https://api.us.nylas.com/v3/connect/callback) and includes URL parameters with information about the user. Nylas uses the information in the parameters to find your application using its client ID and, if the authentication succeeded, create an unverified grant record for the user.
Nylas then uses your application’s callback URI to direct the user back to your project, along with a one-time code.
https://myapp.com/callback-handler?code=<CODE>If you specified a state in the initial authorization request, Nylas includes it as a URL parameter.
Exchange code for access token
Section titled “Exchange code for access token”Make a POST /v3/connect/token request to exchange the user’s code for an access token. Nylas returns an access token and other information about the user.
POST /token HTTP/1.1Host: /v3/connect/tokenContent-Type: application/json
{ "client_id": "<NYLAS_CLIENT_ID>", "client_secret": "<NYLAS_API_KEY>", "grant_type": "authorization_code", "code": "<CODE>", "redirect_uri": "<CALLBACK_URI>"}{ "access_token": "<ACCESS_TOKEN>", "token_type": "Bearer", "id_token": "<ID_TOKEN>", "grant_id": "<NYLAS_GRANT_ID>"}app.get("/oauth/exchange", async (req, res) => { const code = req.query.code;
if (!code) { res.status(400).send("No authorization code returned from Nylas");
return; }
try { const response = await nylas.auth.exchangeCodeForToken({ clientId: config.clientId, redirectUri: config.callbackUri, code, });
const { grantId } = response;
res.status(200).send(grantId); } catch (error) { res.status(500).send("Failed to exchange authorization code for token"); }});@flask_app.route("/oauth/exchange", methods=["GET"])
def exchange_code_for_token(): code_exchange_response = nylas.auth.exchange_code_for_token( request={ "code": request.args.get('code'), "client_id": os.environ.get("NYLAS_CLIENT_ID"), "redirect_uri": REDIRECT_CLIENT_URI } )
return { 'grant_id': code_exchange_response.grant_id }get '/oauth/exchange' do code = params[:code] status 404 if code.nil?
begin response = nylas.auth.exchange_code_for_token({ client_id: '<NYLAS_CLIENT_ID>', redirect_uri: 'http://localhost:4567/oauth/exchange', code: code }) rescue StandardError status 500 else responde_data = response[:grant_id] "#{response_data}" endendhttp.get("/oauth/exchange") { val code : String = request.queryParams("code")
if(code == "") { response.status(401) }
val codeRequest : CodeExchangeRequest = CodeExchangeRequest( "http://localhost:4567/oauth/exchange", code, "<NYLAS_CLIENT_ID>", "nylas" )
try { val codeResponse : CodeExchangeResponse = nylas.auth().exchangeCodeForToken(codeRequest)
codeResponse } catch (e : Exception) { e }}get("/oauth/exchange", (request, response) -> { String code = request.queryParams("code");
if(code == null) { response.status(401);} assert code != null;
CodeExchangeRequest codeRequest = new CodeExchangeRequest( "http://localhost:4567/oauth/exchange", code, "<NYLAS_CLIENT_ID>", "nylas" );
try { CodeExchangeResponse codeResponse = nylas.auth().exchangeCodeForToken(codeRequest);
return "%s".formatted(codeResponse); } catch(Exception e) { return "%s".formatted(e); }});Nylas marks the user’s grant as verified and sends you their grant ID and email address.
Verify your setup
Section titled “Verify your setup”After you complete the OAuth flow and receive a grant ID, verify that your authentication is working before building it into production code. The commands below use the Nylas CLI — install it first if you haven’t already.
List all connected grants to confirm the authentication succeeded with nylas auth list:
nylas auth listTest a simple API call with your grant ID and API key using nylas email list:
nylas email list --limit 1If both commands succeed, your grant is verified and ready to use. If you see errors, double-check that:
- Your
NYLAS_API_KEYis set correctly in your environment - The grant ID matches the one returned from the OAuth flow
- The user completed the authorization process successfully
Make requests with API key
Section titled “Make requests with API key”Now that you have a grant ID for your user, you can make requests on their behalf with your application’s API key and their grant ID.
curl --compressed --request POST \ --url 'https://api.us.nylas.com/v3/grants/<NYLAS_GRANT_ID>/events?calendar_id=<CALENDAR_ID>' \ --header 'Accept: application/json' \ --header 'Authorization: Bearer <NYLAS_API_KEY>' \ --header 'Content-Type: application/json' \ --data '{ "title": "Annual Philosophy Club Meeting", "busy": true, "conferencing": { "provider": "Zoom Meeting", "autocreate": { "conf_grant_id": "<NYLAS_GRANT_ID>", "conf_settings": { "settings": { "join_before_host": true, "waiting_room": false, "mute_upon_entry": false, "auto_recording": "none" } } } }, "participants": [ { "name": "Leyah Miller", "email": "[email protected]" }, { "name": "Nyla", "email": "[email protected]" } ], "resources": [{ "name": "Conference room", "email": "[email protected]" }], "description": "Come ready to talk philosophy!", "when": { "start_time": 1674604800, "end_time": 1722382420, "start_timezone": "America/New_York", "end_timezone": "America/New_York" }, "location": "New York Public Library, Cave room", "recurrence": [ "RRULE:FREQ=WEEKLY;BYDAY=MO", "EXDATE:20211011T000000Z" ],}'import Nylas from "nylas";
const nylas = new Nylas({ apiKey: "<NYLAS_API_KEY>", apiUri: "<NYLAS_API_URI>",});
const now = Math.floor(Date.now() / 1000); // Time in Unix timestamp format (in seconds)
async function createAnEvent() { try { const event = await nylas.events.create({ identifier: "<NYLAS_GRANT_ID>", requestBody: { title: "Build With Nylas", when: { startTime: now, endTime: now + 3600, }, }, queryParams: { calendarId: "<CALENDAR_ID>", }, });
console.log("Event:", event); } catch (error) { console.error("Error creating event:", error); }}
createAnEvent();from nylas import Client
nylas = Client( "<NYLAS_API_KEY>", "<NYLAS_API_URI>")
grant_id = "<NYLAS_GRANT_ID>"
events = nylas.events.create( grant_id, request_body={ "title": 'Build With Nylas', "when": { "start_time": 1609372800, "end_time": 1609376400 }, }, query_params={ "calendar_id": "<CALENDAR_ID>" })
print(events)require 'nylas'require 'date'
nylas = Nylas::Client.new(api_key: "<NYLAS_API_KEY>")
query_params = { calendar_id: "<CALENDAR_ID>"}
today = Date.todaystart_time = Time.local(today.year, today.month, today.day, 13, 0, 0).to_iend_time = Time.local(today.year, today.month, today.day, 13, 30, 0).to_i
request_body = { when: { start_time: start_time, end_time: end_time }, title: "Let's learn some Nylas Ruby SDK!", location: "Nylas' Headquarters", description: "Using the Nylas API with the Ruby SDK is easy.", participants: [{ name: "Blag", status: 'noreply' }]}
event, _request_id = nylas.events.create( identifier: "<NYLAS_GRANT_ID>", query_params: query_params, request_body: request_body)
puts eventimport com.nylas.NylasClientimport com.nylas.models.*
import java.time.LocalDateTimeimport java.time.ZoneOffset
fun main(args: Array<String>) { val nylas: NylasClient = NylasClient(apiKey = "<NYLAS_API_KEY>") var startDate = LocalDateTime.now()
// Set the time. Because we're using UTC, we need to add the difference in hours from our own timezone. startDate = startDate.withHour(13); startDate = startDate.withMinute(0); startDate = startDate.withSecond(0); val endDate = startDate.withMinute(30);
// Convert the dates from Unix timestamp format to integer. val iStartDate: Int = startDate.toEpochSecond(ZoneOffset.UTC).toInt() val iEndDate: Int = endDate.toEpochSecond(ZoneOffset.UTC).toInt()
// Create the timespan for the event. val eventWhenObj: CreateEventRequest.When = CreateEventRequest.When. Timespan(iStartDate, iEndDate);
// Define the title, location, and description of the event. val title: String = "Let's learn about the Nylas Kotlin/Java SDK!" val location: String = "Blag's Den!" val description: String = "Using the Nylas API with the Kotlin/Java SDK is easy."
// Create the list of participants. val participants: List<CreateEventRequest.Participant> = listOf(CreateEventRequest. Participant("<PARTICIPANT_EMAIL>", ParticipantStatus.NOREPLY, "<PARTICIPANT_NAME>"))
// Create the event request. This adds date/time, title, location, description, and participants. val eventRequest: CreateEventRequest = CreateEventRequest(eventWhenObj, title, location, description, participants)
// Set the event parameters. val eventQueryParams: CreateEventQueryParams = CreateEventQueryParams("<CALENDAR_ID>")
val event: Response<Event> = nylas.events().create("<NYLAS_GRANT_ID>", eventRequest, eventQueryParams)}import com.nylas.NylasClient;import com.nylas.models.*;
import java.time.Instant;import java.time.LocalDate;import java.time.ZoneOffset;import java.time.temporal.ChronoUnit;import java.util.*;
public class create_calendar_events { public static void main(String[] args) throws NylasSdkTimeoutError, NylasApiError { NylasClient nylas = new NylasClient.Builder("<NYLAS_API_KEY>").build();
// Get today's date LocalDate today = LocalDate.now();
// Set time. Because we're using UTC we need to add the hours in difference from our own timezone. Instant sixPmUtc = today.atTime(13, 0).toInstant(ZoneOffset.UTC);
// Set the date and time for the event. We add 30 minutes to the starting time. Instant sixPmUtcPlus = sixPmUtc.plus(30, ChronoUnit.MINUTES);
// Get the Date and Time as a Unix timestamp long startTime = sixPmUtc.getEpochSecond(); long endTime = sixPmUtcPlus.getEpochSecond();
// Define title, location, and description of the event String title = "Let's learn some about the Nylas Java SDK!"; String location = "Nylas Headquarters"; String description = "Using the Nylas API with the Java SDK is easy.";
// Create the timespan for the event CreateEventRequest.When.Timespan timespan = new CreateEventRequest. When.Timespan. Builder(Math.toIntExact(startTime), Math.toIntExact(endTime)). build();
// Create the list of participants. List<CreateEventRequest.Participant> participants_list = new ArrayList<>();
participants_list.add(new CreateEventRequest. "John Doe", "", ""));
// Build the event details. CreateEventRequest createEventRequest = new CreateEventRequest.Builder(timespan) .participants(participants_list) .title(title) .location(location) .description(description) .build();
// Build the event parameters. In this case, the Calendar ID. CreateEventQueryParams createEventQueryParams = new CreateEventQueryParams.Builder("<CALENDAR_ID>").build();
// Create the event itself Event event = nylas.events().create( "<NYLAS_GRANT_ID>", createEventRequest, createEventQueryParams).getData(); }}