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.
Before you start
Section titled “Before you start”- Node.js 22 or newer on your server.
- An InboxLink API secret. The hosted service at
https://inboxlink-two.vercel.appgives 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.
-
Install the SDK.
Terminal window npm install @inboxlink/sdk -
Set the secret in your server’s environment.
Terminal window INBOXLINK_API_SECRET=... -
Create a Connect session and send the person to its
connectUrl.externalUserIdis your own id for this person;redirectUriis 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); -
Handle the redirect. InboxLink sends the person back to
redirectUriwith a one-timepublic_token. Exchange it on your server and keep thegrantIdagainst 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_tokenandcompleteConnectthrows a clear error: show a “connect again” button. -
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);
Good to know
Section titled “Good to know”messages.listreads InboxLink’s synced cache by default and ignores search filters there. Passsource: "live"to query Gmail directly withq,from,to,subject,labelorincludeSpamTrash.- Lists are cursor paged;
messages.iteratewalks every page for you. - A grant answers
needs_reauthwhen 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.
Troubleshooting
Section titled “Troubleshooting”| 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 }}- Connect flow: what happens when someone connects the same mailbox again.
- Messages API: filters, paging and error codes.
- Sync and webhooks: keep the cache fresh and get events.