Link email accounts

Learn how to link emails accounts with Unipile API.

Unipile supports all email providers if they all rely on IMAP and SMTP, but based on the provider, the following Accounts should be linked:

  • Gmail : Google Account
  • Outlook : Microsoft Outlook Account
  • Any other (Yahoo, Zoho, AOL, Apple...) : IMAP Account

How Does It Work?


Gmail and Outlook

For Gmail and Outlook, Unipile offers a secure and user-friendly authentication flow via OAuth. Users are redirected to the official Google or Microsoft login pages, where they can grant your app permission to access their data.

This requires setting up OAuth in your app with the proper permission scopes. While configuring it for production takes some effort, the end result is a trusted flow where users can authenticate in just a few clicks.

Once you’ve completed account linking setup, we recommend configuring OAuth for production.

📘

Note on calendar capabilities

If you want to use calendar capabilities, you can do so with the same linked account. There is no need to link it separately for email and for calendar. The available capabilities depend on the permission scopes granted, as described in the OAuth guides.


Other Providers (IMAP)

For all other email providers, users authenticate through their IMAP/SMTP configuration, which typically includes:

  • Email address
  • Password
  • IMAP and SMTP hostnames
  • IMAP and SMTP ports
  • IMAP and SMTP encryption protocols

Because server settings vary by provider, finding the correct configuration can be frustrating for users. To simplify this, Unipile lets users enter only their email address and password. The platform will then attempt to automatically detect the right configuration. If detection fails, users will be prompted to manually provide the missing details.


📘

Note on search and listing capabilities

By default, listing all emails across an IMAP account with List all Emails is not supported. Instead, you must use List folder Emails, which requires querying each folder individually. This can make account-wide searches slower or heavier to perform. This limitation exists because Unipile normally acts only as a bridge between your application and the IMAP server.

If you enable Initial Sync, Unipile will download and securely store the account’s emails. This allows you to use List all Emails for account-wide searches and provides faster query responses.

The trade-off is that account initialization takes longer—data is not immediately available after linking—and the emails are stored on Unipile’s infrastructure (encrypted).


Link with Hosted Authentication

To link email accounts using Hosted Authentication, follow the Authenticate with Hosted Auth guide and specify *:EMAILS as the providers when calling Create Auth Link.

Server code

// ...

const response = await hostedAuthApi.createAuthLink({
  body: {
    providers: "*:EMAILS",
    expires_on: dayjs().add(10, "minutes").toISOString(),
    redirect_uri: "https://myapp.com/link-account/callback",
  },
});

// ...
# ...

response = hosted_auth_api.create_auth_link(
    {
        "providers": "*:EMAILS",
        "expires_on": "2025-05-05T00:00:00.000Z",
        "redirect_uri": "https://myapp.com/link-account/callback",
    }
)

# ...
curl --request POST \
     --url https://api.unipile.com/v2/auth/link \
     --header 'X-API-KEY: api-key' \
     --header 'accept: application/json' \
     --header 'content-type: application/json' \
     --data '
{
  "providers": "*:EMAILS",
  "redirect_uri": "https://myapp.com/link-account/callback",
  "expires_on": "2025-05-05T00:00:00.000Z"
}
'

This will present users with the option to connect an email account through Google, Microsoft Outlook, or IMAP. Other Unipile-supported providers without email capabilities are hidden.

For IMAP accounts, Hosted Authentication automatically handles the process of requesting additional details if the IMAP/SMTP configuration cannot be detected.


Link with Custom Authentication

As mentioned earlier, the authentication method depends on the account provider. You’ll need to implement both approaches to support all cases.


Google & Outlook

To build a custom authentication form for Google & Outlook Accounts, they must follow the OAuth Flow.


Other providers (IMAP)

To build a custom authentication form for IMAP Accounts, it must follow the Credentials Flow.

Note that IMAP does not have checkpoints, so you don't have to care about handling them if you only plan to authenticate Email accounts. But you'll have to handle the process of requesting additional details if the IMAP/SMTP configuration cannot be detected at the Collect credentials step:

  1. Make a simple form that takes an email and password.
  2. On submit of this form, call Start Auth Intent with credentials as Basic Authentication (See API Reference). This endpoint will attempt to find the appropriate configuration based on the given email.
  3. Check the response error type : if equal to api/invalid_parameters, no configuration has been found and you should request for additional details.

Client code

const handleSubmit = async (e: React.FormEvent<HTMLFormElement>) => {
    //...

    try {
      const data = await startAuthIntent({
        provider: "imap",
        credentials: {
          user,
          password,
        },
      });

      //...
    } catch (e) {
      console.error(e);
      if (e.type === "api/invalid_parameters) {
        setStatus("full-authentication") // Set this state to show a full authentication form
      } else {   
        setStatus("error");
      }
    }
};
  1. Redirect to a more complex form that takes an email, password, host, port and encryption for IMAP and SMTP. You can prefill email and password with the values given in the first form.
  2. On submit of this form, call Start Auth Intent with credentials as Full Authentication (See API Reference)

Client code

const handleSubmit = async (e: React.FormEvent<HTMLFormElement>) => {
    e.preventDefault();
    const formData = new FormData(e.target as HTMLFormElement);
    const imapUser = formData.get("imap_user") as string;
    const imapPassword = formData.get("imap_password") as string;
    const imapHost = formData.get("imap_host") as string;
    const imapPort = formData.get("imap_port") as string;
    const imapEncryption = formData.get("imap_encryption") as "tls" | "ssl" | "starttls" | "default";

    const smtpUser = formData.get("smtp_user") as string;
    const smtpPassword = formData.get("smtp_password") as string;
    const smtpHost = formData.get("smtp_host") as string;
    const smtpPort = formData.get("smtp_port") as string;
    const smtpEncryption = formData.get("smtp_encryption") as "tls" | "ssl" | "starttls" | "default";

    setStatus("loading");

    try {
      const data = await startAuthIntent({
        provider: "imap",
        credentials: {
          imap: {
            user: imapUser,
            password: imapPassword,
            host: imapHost,
            port: Number(imapPort),
            encryption: imapEncryption,
          },
          smtp: {
            user: smtpUser,
            password: smtpPassword,
            host: smtpHost,
            port: Number(smtpPort),
            encryption: smtpEncryption,
          },
        },
      });

      setStatus("success");
      onSuccess(data);
    } catch (e) {
      console.error(e);
      setStatus("error");
    }
  };