← Docs

CLI & Agent Auth

Authenticate Gmail from a terminal, CLI tool, or AI agent — no callback server needed.

How it works

  1. Your CLI calls POST /api/device/code to get an auth URL
  2. The user opens the URL in a browser and authenticates with Google
  3. They land on inbox.dog's hosted page showing a one-time code
  4. They copy the code and paste it back into the terminal
  5. Your CLI calls POST /oauth/token to exchange the code for Gmail tokens

Prerequisites

  • An inbox.dog API key (client_id + client_secret)
  • A terminal or environment that can make HTTP requests

Don't have an API key? Create one:

curl -s -X POST https://inbox.dog/api/keys \
  -H "Content-Type: application/json" \
  -d '{"name": "my-agent"}'

Step 1: Get the auth URL

curl -s -X POST https://inbox.dog/api/device/code \
  -H "Content-Type: application/json" \
  -d '{
    "client_id": "id_YOUR_CLIENT_ID",
    "client_secret": "sk_YOUR_CLIENT_SECRET",
    "scope": "email:full"
  }'

Response:

{
  "auth_url": "https://inbox.dog/oauth/authorize?client_id=id_...&redirect_uri=https%3A%2F%2Finbox.dog%2Fconnect%2Foauth&scope=email%3Afull",
  "redirect_uri": "https://inbox.dog/connect/oauth",
  "expires_in": 600
}

Step 2: User authenticates

Print the auth_url to the terminal. The user opens it in their browser, signs in with Google, and sees a page with a code to copy.

Step 3: Exchange the code

Once the user pastes the code back, exchange it for Gmail tokens:

curl -s -X POST https://inbox.dog/oauth/token \
  -H "Content-Type: application/json" \
  -d '{
    "code": "PASTED_CODE_HERE",
    "client_id": "id_YOUR_CLIENT_ID",
    "client_secret": "sk_YOUR_CLIENT_SECRET"
  }'

Response:

{
  "access_token": "ya29.a0AfH6SM...",
  "refresh_token": "1//0eXy...",
  "token_type": "Bearer",
  "expires_in": 3599,
  "email": "user@gmail.com"
}

The code is one-time use and expires in 5 minutes.

Node.js Example

Using the inbox.dog npm package:

import InboxDog from "inbox.dog";
import * as readline from "node:readline/promises";

const dog = new InboxDog();
const clientId = process.env.INBOXDOG_KEY!;
const clientSecret = process.env.INBOXDOG_SECRET!;

// 1. Get the auth URL
const { auth_url } = await dog.getDeviceCode(clientId, clientSecret, "email:full");
console.log(`Open this URL to authenticate:\n${auth_url}\n`);

// 2. Wait for user to paste the code
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
const code = await rl.question("Paste the code here: ");
rl.close();

// 3. Exchange for tokens
const tokens = await dog.exchangeCode(code.trim(), clientId, clientSecret);
console.log(`Authenticated as ${tokens.email}`);

// 4. Use Gmail
const gmail = dog.gmail(tokens, clientId, clientSecret);
const { messages } = await gmail.list({ query: "is:unread", maxResults: 5 });
console.log(`${messages.length} unread messages`);

Scopes

Scope Access
emailRead-only Gmail + email address
email:readRead-only Gmail + email address
email:sendSend email + email address
email:fullFull Gmail access (read, send, modify)

API Reference

POST /api/device/code Start device auth

Validates your API key and returns an auth URL with redirect_uri preset to inbox.dog's hosted code display page.

Request Body

{
  "client_id": "id_...",        // required
  "client_secret": "sk_...",    // required
  "scope": "email:full"         // optional, defaults to "email"
}

Response 200

{
  "auth_url": "https://inbox.dog/oauth/authorize?...",
  "redirect_uri": "https://inbox.dog/connect/oauth",
  "expires_in": 600
}