# Creating grants with IMAP authentication

Source: https://developer.nylas.com/docs/v3/auth/imap/

This page explains how to authenticate users to your Nylas application using IMAP authentication. IMAP connections don't require a provider auth app, and they don't include calendar functionality.

For more information about working with IMAP accounts in Nylas, see [Use IMAP accounts and data with Nylas](/docs/provider-guides/imap/).

## New options for Yahoo, Exchange, and iCloud users

If you're authenticating Yahoo, Exchange on-prem, or iCloud users, you can either connect them using IMAP auth or use the dedicated Nylas connectors for those providers. If you authenticate them using IMAP, you can access _email data only_, even if the service also provides calendar features.

If you plan to authenticate Yahoo users, you should use the [Yahoo OAuth method](/docs/provider-guides/yahoo-authentication/#set-up-yahoo-oauth-and-nylas-bring-your-own-authentication) _instead_ of IMAP auth to improve reliability.

## Create an IMAP connector

> **Info:** 
> **IMAP doesn't support the concept of scopes**, so you don't need to list any during the authentication process.

Make a [`POST /v3/connectors` request](/docs/reference/api/connectors-integrations/create_connector/) and set `provider` to `imap` to create an IMAP connector.

```json
curl --request POST \
  --url https://api.us.nylas.com/v3/connectors \
  --header 'Content-Type: application/json' \
  --header 'Accept: application/json' \
  --header 'Authorization: Bearer <NYLAS_API_KEY>' \
  --data '{
    "provider": "imap"
  }'
```

```js [createConnector-Node.js SDK]


const config = {
  apiKey: process.env.NYLAS_API_KEY,
  apiUri: process.env.NYLAS_API_URI,
};

const nylas = new Nylas(config);

async function createConnector() {
  try {
    const connector = await nylas.connectors.create({
      requestBody: {
        provider: "imap",
      },
    });

    console.log("Connector created:", connector);
  } catch (error) {
    console.error("Error creating connector:", error);
  }
}

createConnector();
```

```python [createConnector-Python SDK]
from dotenv import load_dotenv
load_dotenv()


from nylas import Client

nylas = Client(
    os.environ.get('NYLAS_API_KEY'),
    os.environ.get('NYLAS_API_URI')
)

connector = nylas.connectors.create(
    request_body={
      "provider": "imap"
    }
)
```

```ruby [createConnector-Ruby SDK]
require 'nylas'

nylas = Nylas::Client.new(api_key: "<NYLAS_API_KEY>")

request_body = {
  provider: "imap"
}

connector = nylas.connectors.create(request_body: request_body)

puts connector
```

```java [createConnector-Java SDK]


public class connector {
  public static void main(String[] args) throws NylasSdkTimeoutError, NylasApiError {
    NylasClient nylas = new NylasClient.Builder("<NYLAS_API_KEY>").build();
    CreateConnectorRequest request = new CreateConnectorRequest.Imap();

    nylas.connectors().create(request);
  }
}
```

```kt [createConnector-Kotlin SDK]


fun main(args: Array<String>) {
  val nylas: NylasClient = NylasClient(apiKey = "<NYLAS_API_KEY>")
  val request : CreateConnectorRequest = CreateConnectorRequest.Imap();

  nylas.connectors().create(request)
}
```

You can also create the connector in the [Nylas Dashboard](https://dashboard-v3.nylas.com) by navigating to **Connectors** and clicking the **plus** symbol beside **IMAP**. Specify the settings for your connector, then **Save** your changes.

## Create grants for IMAP users

Most IMAP providers require that you use an [application password](/docs/provider-guides/app-passwords/) to authenticate third-party tools. If this is the case, you need to redirect the user to their provider so they can enter their app password before authenticating. Some providers use the user's usual login credentials and don't require app passwords.

Nylas offers Hosted OAuth and Bring Your Own Authentication for the [supported IMAP providers](/docs/provider-guides/).


> **Info:** 
> 🔍 **Nylas creates only one grant per email address in each application**. If a user authenticates with your Nylas application using the email address associated with an existing grant, Nylas re-authenticates the grant instead of creating a new one.


### Create an IMAP grant with Hosted OAuth

You can create IMAP grants using either [Hosted OAuth with an API key](/docs/v3/auth/hosted-oauth-apikey/) or [Hosted OAuth and an access token](/docs/v3/auth/hosted-oauth-accesstoken/).

You can also start the Hosted Auth flow using the Nylas SDKs, as in the examples below.

```js [group_2-Node.js SDK]


const config = {
  clientId: process.env.NYLAS_CLIENT_ID,
  clientSecret: process.env.NYLAS_CLIENT_SECRET,
  callbackUri: "http://localhost:3000/oauth/exchange",
  apiKey: process.env.NYLAS_API_KEY,
  apiUri: process.env.NYLAS_API_URI,
};

const nylas = new Nylas({
  apiKey: config.apiKey,
  apiUri: config.apiUri,
});

const app = express();
const port = 3000;

// Route to initialize authentication
app.get("/nylas/auth", (req, res) => {
  const authUrl = nylas.auth.urlForOAuth2({
    clientId: config.clientId,
    provider: "imap",
    redirectUri: config.redirectUri,
    loginHint: process.env.AOL_IMAP_USERNAME,
    state: "state",
  });

  res.redirect(authUrl);
});

// Callback route Nylas redirects to
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.redirectUri,
      code,
    });

    const { grantId } = response;

    res.status(200);
  } catch (error) {
    res.status(500).send("Failed to exchange authorization code for token");
  }
});
```

```python [group_2-Python SDK]
from dotenv import load_dotenv
load_dotenv()


from functools import wraps
from io import BytesIO
from flask import Flask, request, redirect, g
from nylas import Client

nylas = Client(
    os.environ.get("NYLAS_CLIENT_ID"),
    os.environ.get("NYLAS_API_URI")
)

REDIRECT_CLIENT_URI = 'http://localhost:9000/oauth/exchange'
flask_app = Flask(__name__)
@flask_app.route("/nylas/generate-auth-url", methods=["GET"])

def build_auth_url():
  auth_url = nylas.auth.url_for_oauth2(
      config={
        "client_id": os.environ.get("NYLAS_CLIENT_ID"),
        "provider": 'imap',
        "redirect_uri": REDIRECT_CLIENT_URI,
        "login_hint": os.environ.get("AOL_IMAP_USERNAME"),
        "state": "state",
      }
  )

  return redirect(auth_url)

@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 {
    'email': code_exchange_response.email,
    'grant_id': code_exchange_response.grant_id
  }
```

```ruby [group_2-Ruby SDK]
# frozen_string_literal: true

require 'nylas'
require 'sinatra'

set :show_exceptions, :after_handler

error 404 do
  'No authorization code returned from Nylas'
end

error 500 do
  'Failed to exchange authorization code for token'
end

nylas = Nylas::Client.new(api_key: "<NYLAS_API_KEY>")

get '/nylas/auth' do
  config = {
    client_id: "<NYLAS_CLIENT_ID>",
    provider: 'imap',
    redirect_uri: 'http://localhost:4567/oauth/exchange',
    login_hint: '<AOL_IMAP_USER>',
    state: "<state>"
  }

  url = nylas.auth.url_for_oauth2(config)
  redirect url
end

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
    puts response

    grant_id = response[:grant_id]
    email = response[:email]

    "Grant_Id: #{grant_id} \n Email: #{email}"
  end
end
```

```java [group_2-Java SDK]


public class AuthRequest {
  public static void main(String[] args) throws NylasSdkTimeoutError, NylasApiError {
    NylasClient nylas = new NylasClient.Builder("<NYLAS_API_KEY>").build();

    get("/nylas/auth", (request, response) -> {
      List<String> scope = new ArrayList<>();
      UrlForAuthenticationConfig config = new UrlForAuthenticationConfig(
          "<NYLAS_CLIENT_ID>",
          "http://localhost:4567/oauth/exchange",
          AccessType.ONLINE,
          AuthProvider.IMAP,
          Prompt.DETECT,
          scope,
          true,
          "sQ6vFQN",
          "example@aol.com");

      String url = nylas.auth().urlForOAuth2(config);

      response.redirect(url);

      return null;
    });

    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);
      }
    });
  }
}
```

```kt [group_2-Kotlin SDK]


fun main(args: Array<String>) {
  val nylas: NylasClient = NylasClient(apiKey = "<NYLAS_API_KEY>")
  val http: Http = ignite()

  http.get("/nylas/auth") {
    val scope = listOf("https://www.googleapis.com/auth/userinfo.email")
    val config : UrlForAuthenticationConfig = UrlForAuthenticationConfig(
        "<NYLAS_CLIENT_ID>",
        "http://localhost:4567/oauth/exchange",
        AccessType.ONLINE,
        AuthProvider.IMAP,
        Prompt.DETECT,
        scope,
        true,
        "sQ6vFQN",
        "<email_to_connect>")

    val url = nylas.auth().urlForOAuth2(config)

    response.redirect(url)
  }
}

http.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
  }
}
```

### Verify your IMAP grant was created

After you complete the authentication flow and receive a grant ID, verify that your IMAP connection is working before making API requests. The commands below use the [Nylas CLI](https://cli.nylas.com/) — install it first if you haven't already.

**List all connected IMAP accounts** with [`nylas auth list`](https://cli.nylas.com/docs/commands/auth-list):

```bash
nylas auth list
```

**Test a simple API call with your IMAP grant** using [`nylas email list`](https://cli.nylas.com/docs/commands/email-list):

```bash
nylas email list --limit 1
```

If both commands succeed, your IMAP grant is verified and ready to use. If you see errors:
- Verify the user entered their IMAP credentials correctly
- Check that their IMAP server is accessible (some corporate networks restrict IMAP access)
- Confirm the user's password hasn't changed (IMAP grants expire if the password changes)
- If using SMTP, ensure the user configured SMTP settings during authentication

### Create an IMAP grant with Bring Your Own Authentication

If you already have your user's password or app password, you can create a grant for them using Bring Your Own (BYO) Authentication.


The example below shows how to make a [Bring Your Own Authentication request](/docs/reference/api/manage-grants/byo_auth/) with the correct provider and settings. The rest of the authentication process follows the same process as for non-IMAP grant creation.

```bash
curl -X POST 'https://api.us.nylas.com/v3/connect/custom' \
  --header 'Authorization: Bearer <NYLAS_API_KEY>' \
  --header 'Content-Type: application/json' \
  --data-raw '{
    "provider": "imap",
    "settings": {
      "imap_username": "<IMAP_USERNAME>",
      "imap_password": "<IMAP_PASSWORD>",
      "imap_host": "imap.mail.me.com",
      "imap_port": 993,
      "smtp_host": "smtp.mail.me.com",
      "smtp_port": 465
    }
  }'
```

For more information, see [Creating grants with Bring Your Own Authentication](/docs/v3/auth/bring-your-own-authentication/).


### Require SMTP configuration during authentication

By default, SMTP settings are optional during IMAP authentication. This can cause email sending to fail later if users skip the SMTP fields. To prevent this, add the
`options=smtp_required` query parameter to your Hosted authentication URL. This ensures users enter their SMTP host and port during authentication, so grants are ready to send email immediately.

For more information, see <a href="https://support.nylas.com/hc/en-us/articles/33355551154461-How-can-I-ensure-users-enter-their-SMTP-settings-during-authentication">Requiring SMTP settings during authentication</a>.

## Error handling for IMAP Hosted authentication

Nylas creates a grant only if both the IMAP settings are validated _and_ the provider accepts the user's login credentials. When an error occurs, the `redirect_uri` includes an `error_type` query parameter when the auth process is complete. The possible `error_type` values are...

- `provider_not_responding`: The IMAP provider didn't respond to the login request from Nylas.
- `invalid_authentication`: The provider responded with an incorrect credential error. Nylas prompts the user with an error message.

> **Info:** 
> **IMAP grants are especially sensitive to credential changes.** If a user changes their password or an app password is revoked, the grant expires. Always re-authenticate rather than deleting the grant, because IMAP object IDs are tied to the grant ID and will change if you create a new grant. See [Handling expired grants](/docs/dev-guide/best-practices/grant-lifecycle/) for details.

- `auth_limit_reached`: The user entered an incorrect password three times. Nylas redirects back to your application's `redirect_uri` with the error code instead of showing an error message.