Skip to content

Quick start

This guide connects one Gmail mailbox to your app and lists its messages. It takes about ten minutes if you already have a server route that can handle a redirect.

  • Node.js 22 or newer on your server.
  • An InboxLink API secret. The hosted service at https://inboxlink-two.vercel.app gives each host app its own secret: open an issue on GitHub to ask for one, or run your own InboxLink server.

You do not need a Google Cloud project or GOOGLE_* settings. Google sign-in runs on the InboxLink server.

  1. Install the SDK.

    Terminal window
    npm install @inboxlink/sdk
  2. Set the secret in your server’s environment.

    Terminal window
    INBOXLINK_API_SECRET=...
  3. Create a Connect session and send the person to its connectUrl. externalUserId is your own id for this person; redirectUri is a route in your app, not a Google URL.

    import { InboxLink } from "@inboxlink/sdk";
    const il = new InboxLink({ apiSecret: process.env.INBOXLINK_API_SECRET });
    const session = await il.createConnectSession({
    externalUserId: user.id,
    redirectUri: "https://your-app.example.com/inboxlink/done",
    });
    return Response.redirect(session.connectUrl, 302);
  4. Handle the redirect. InboxLink sends the person back to redirectUri with a one-time public_token. Exchange it on your server and keep the grantId against your user.

    const { grantId } = await il.completeConnect({ redirectUrl: request.url });
    await db.saveInboxGrant(user.id, grantId);

    The token works once. If the person cancels at Google, there is no public_token and completeConnect throws a clear error: show a “connect again” button.

  5. Sync and read messages.

    const { jobId } = await il.grants.sync(grantId); // queued (HTTP 202)
    // wait for the sync.completed webhook, or poll:
    const job = await il.grants.getSyncJob(grantId, jobId); // queued → running → completed (or failed)
    const page = await il.messages.list(grantId, { limit: 25 });
    for (const m of page.messages) console.log(m.subject);
    const { message } = await il.messages.get(grantId, page.messages[0]!.id);
  • messages.list reads InboxLink’s synced cache by default and ignores search filters there. Pass source: "live" to query Gmail directly with q, from, to, subject, label or includeSpamTrash.
  • Lists are cursor paged; messages.iterate walks every page for you.
  • A grant answers needs_reauth when Google withdraws access. Send the person through Connect again.
  • To disconnect a mailbox, call il.grants.revoke(grantId). The sealed token and cached messages are deleted.
You see Cause Fix
401 on createConnectSession Missing or wrong API secret Check INBOXLINK_API_SECRET on your server
redirectUri_not_allowed The server only accepts listed origins Use an allowed origin, or ask for yours to be added
No public_token on your redirect The person cancelled at Google, or Connect failed Show a “connect again” button
error=connect_restart on your redirect The session expired or was used already Create a new session and redirect again
Empty messages.list right after Connect The first sync has not finished Wait for sync.completed or the job to complete
Filters ignored The default source is the cache Pass source: "live"
409 needs_reauth or grant_inactive Google withdrew access Send the person through Connect again

Errors come back as InboxLinkApiError, with status and code:

import { InboxLinkApiError } from "@inboxlink/sdk";
try {
await il.messages.list(grantId);
} catch (err) {
if (err instanceof InboxLinkApiError && err.code === "needs_reauth") {
// ask the person to reconnect
}
}