BlogDevelopers

How to build your own email client on Nextmail

What you need to build your own email client on Nextmail: OAuth sign-in for your users, typed SDKs in seven languages, live events, webhooks and raw messages.

ArtaNextmail team

6 min read

Most email products are closed. You get their app, their idea of what an inbox should be, and an export button for the day you leave. Nextmail was built the other way round: every screen in its app is drawn from the same API anyone can call, so the app is one client among many, and yours can be the next.

That makes Nextmail a base for building. Some teams want a client shaped around one kind of work. Some want email inside a product they already run. Some want an agent that handles mail for them. This guide covers what you get to build with, and how the pieces fit.

What people build on Nextmail

  • A client for one kind of work. A recruiting inbox that shows each candidate's stage beside the thread, a support desk, or an intake queue for a law firm, each with its own views and shortcuts.
  • Email inside your product. A CRM, a marketplace or a booking tool that shows the conversation with a customer next to their record and replies from the user's own address.
  • Agents and automations. Code that reads invoices as they arrive, answers routine questions, or files mail by rules that live in your codebase.
  • A personal client. Your own keyboard, your own layout, on your own domain.

Teams already build this way. Website gives every site it makes for a business a working inbox, with the mail in the owner's dashboard and replies in the same thread, and Ip2Geo sends its usage alerts and billing email through the API.

The building blocks

What a client needsWhat Nextmail gives you
Sign people inOAuth with PKCE, found at /.well-known/oauth-authorization-server, with the person choosing what your app may reach
Read and search mailThreads and messages over REST, paged for you by the SDKs
Stay in syncOne live event stream for every change in a mailbox, resumable after a disconnect
React on a serverSigned webhooks for received mail, opens and clicks
Send and replyOne send call, threadId to answer inside a conversation, and an idempotency key so a retry never sends twice
Keep the originalAny message as an .eml file, byte for byte when the address keeps originals
Reach Gmail and OutlookConnected mailboxes: the same API over a mailbox that stays at Google, Microsoft, Yahoo or iCloud
Work with AIAn MCP server that takes the same sign-in and the same limits

The REST API is described by an OpenAPI 3.1 document that needs no key to read, and the SDKs for TypeScript, Python, Ruby, PHP, Go, Java and C# are held to it by parity checks, so a method does not drift from the endpoint behind it.

Sign people in with OAuth

A client used by other people should never ask them to paste a key. Nextmail speaks standard OAuth: your app registers itself from the discovery document and signs people in with PKCE, the flow built for apps that cannot keep a secret, such as a desktop or mobile client.

The person stays in charge of what your app gets. The consent screen names your app and the address its token goes to. Then they choose one workspace, a preset (read only, read and send, or everything their role allows) or a set of permissions, all of their addresses or only some, and how long the access lasts. Their own role is the ceiling, and if they narrow or remove your app under Connected apps, the change applies on its next call. The same token works on the REST API and on the MCP server. The details are in OAuth for third-party clients.

It is worth comparing this with building on a big provider's mailbox. Reading whole Gmail mailboxes, for example, uses scopes Google classes as restricted, which means passing its OAuth verification, plus a security assessment if your servers store or pass on that data.

Read and page through mail

The typed SDKs have one method for every operation the API serves. The TypeScript client has no dependencies and runs on Node 20 and later, Workers, Deno, Bun and the browser. With the token from the OAuth flow, listing a mailbox looks like this:

ts
import { Nextmail } from '@nextmail/sdk' const client = new Nextmail({ accessToken }) for await (const thread of client.threads.iterate()) {  const messages = await client.threads.listMessages(thread.id)  console.log(thread.id, messages.at(-1)?.subject)}

iterate() follows the pages for you, and a script that runs for your own workspace can pass an apiKey instead of an accessToken. Keys are scoped and revocable, and every call they make is logged.

Stay in sync with live events

The hardest part of any client is staying current without hammering the server. GET /live-events holds one connection open as server-sent events and sends an event each time the mailbox changes: a message received or sent, a thread read, starred, archived or labelled, or a delivery status that changed.

ts
for await (const event of client.liveEvents.stream({ types: ['message.received'] })) {  const thread = await client.threads.get(event.data.threadId)  showInInbox(thread)}

Every event carries an id. After a disconnect the stream picks up from the last one you handled and replays what you missed, and when you have been away too long it tells you to list again rather than leaving a gap. Events carry ids, not content, so your client fetches only what changed.

If your code only needs to wait for one message, such as a sign-in code it asked a service to send, GET /threads/wait holds the request open for up to a minute and answers with the first match.

Send, reply and keep the thread together

Sending is one call. Pass the threadId of a conversation and the reply lands in that thread. Pass an idempotency key, and a retry after a timeout never sends the message twice.

ts
await client.emails.send(  {    from: '[email protected]',    to: '[email protected]',    subject: 'Re: your order',    html: '<p>It shipped this morning.</p>',    threadId,  },  { idempotencyKey: `reply-${threadId}` },)

Every error has a fixed type and a more specific code to branch on, and every rate-limit answer says in Retry-After how long to wait.

Webhooks for the server side

A client in the browser listens to the live stream. A server listens to webhooks: signed POSTs for received mail, opens, clicks and the rest, retried when your endpoint is down and kept as dead letters you can send again. An endpoint can ask for the message content and a link to the original source in every event, and an address can even skip the inbox and hand its mail straight to your server.

Keep the original, and reach the mailboxes people already have

Any message can be downloaded as its source, the .eml file a mail app shows under Show original. With Keep original on, an address stores every message byte for byte, signatures and Received lines included, which matters if your product archives mail or checks it.

Your users do not have to move their mail to use your client. With connected mailboxes, a Gmail, Outlook, Yahoo or iCloud mailbox stays where it is while your client works it through the same API, and the people on your own domains get mailboxes Nextmail hosts.

Your brand, and the agents around it

If you would rather not build every screen, you do not have to. A workspace can carry its own logo, fonts and sign-in page, and the Nextmail web app can be served at an address on your own domain, such as mail.example.com, so the parts you did not build still look like yours. Mail apps such as Apple Mail and Outlook connect to the same addresses over IMAP and SMTP.

And because the MCP server uses the same sign-in and the same limits, an AI agent working beside your client can read, search and draft in the same mailbox, but only within what the person approved.

Where to start

  1. Try a few calls from the API playground, which hands you a temporary key scoped to one request.
  2. Install the SDK for your language and list a mailbox.
  3. Add OAuth when other people will sign in, and the live event stream when the screen has to stay current.

The full API, the SDKs and MCP are on every plan, the free one included, and Nextmail prices the workspace rather than each seat, so a client used by a whole team does not get more expensive as the team grows. If you want the protocol background first, read our guide to JMAP, the open standard for syncing mail as JSON over HTTPS, and our case for treating email as a mailbox rather than a send pipe.

References

  1. 1RFC 8414: OAuth 2.0 Authorization Server MetadataIETF
  2. 2RFC 7591: OAuth 2.0 Dynamic Client Registration ProtocolIETF
  3. 3RFC 7636: Proof Key for Code Exchange by OAuth Public ClientsIETF
  4. 4Server-sent eventsWHATWG
  5. 5OpenAPI Specification v3.1.0OpenAPI Initiative
  6. 6Choose Gmail API scopesGoogle

Keep reading

Related articles

6 min read

What is JMAP? The JSON email protocol, explained

JMAP is the IETF standard for email, contacts and calendars over JSON and HTTPS. How it works, how it differs from IMAP, and who supports it in 2026.

ArtaDevelopers

5 min read

Email as a mailbox: why AI agents need a real inbox

Most email APIs only send. What a real mailbox adds for apps and AI agents, the four ways to get one in 2026, and how much code each leaves you.

BenjaminAI

9 min read

Resend pricing in 2026: what it costs, how to pay less

What Resend costs at 50,000 to 1,000,000 emails a month, with every plan, overage rate and add-on, and what the same mail costs on Nextmail.

BenjaminComparisons

Try it yourself

Your email,
on your own domain.

Nextmail puts your domains, sending and inbox in one place, with the full API on every plan, the free one included.