← Docs

API Reference

Base URL: https://inbox.dog

All endpoints accept and return JSON. CORS is enabled on all API and OAuth routes. Machine-readable spec: openapi.json

Authentication

Authenticated requests use your client_id and client_secret, obtained from POST /api/keys.

Credential Format Usage
client_idid_...Query param or request body
client_secretsk_...Request body or X-Client-Secret header

Never expose client_secret in client-side code.

Endpoints

POST /api/keys Create API key

Create a new API key. Returns credentials and 10 free credits. No authentication required.

Request Body

{
  "name": "my-app"  // optional, defaults to "default"
}

Response 200

{
  "client_id": "id_abc123...",
  "client_secret": "sk_xyz789...",
  "name": "my-app",
  "credits": 10
}
GET /api/keys/{client_id} Get key info

Retrieve API key details including remaining credits.

Headers

X-Client-Secret Required. Your client secret.

Response 200

{
  "client_id": "id_abc123...",
  "name": "my-app",
  "credits": 10,
  "created_at": 1700000000000
}
GET /oauth/authorize Start OAuth flow

Redirects the user to Google's consent screen.

Query Parameters

Parameter Required Description
client_idYesYour API key client ID
redirect_uriYesURL to redirect after authentication
scopeNoPermission scope. Default: email
stateNoOpaque value for CSRF protection, passed back in redirect

Callback

After consent, the user is redirected to:

https://yourapp.com/callback?code=AUTH_CODE&state=YOUR_STATE
POST /oauth/token Exchange code for tokens

Exchange an authorization code for access and refresh tokens. Costs 1 credit. Auth codes expire after 5 minutes.

Request Body

{
  "code": "AUTH_CODE_FROM_CALLBACK",
  "client_id": "YOUR_CLIENT_ID",
  "client_secret": "YOUR_CLIENT_SECRET"
}

Response 200

{
  "access_token": "ya29.a0AfH6...",
  "refresh_token": "1//0eXyz...",
  "token_type": "Bearer",
  "expires_in": 3600,
  "email": "user@example.com"
}
POST /oauth/token Refresh access token

Use a refresh token to obtain a new access token. Free — no credit cost.

Request Body

{
  "grant_type": "refresh_token",
  "refresh_token": "YOUR_REFRESH_TOKEN",
  "client_id": "YOUR_CLIENT_ID",
  "client_secret": "YOUR_CLIENT_SECRET"
}

Response 200

{
  "access_token": "ya29.a0AfH6...",
  "token_type": "Bearer",
  "expires_in": 3600
}
POST /api/checkout Purchase credits

Create a Stripe checkout session to purchase additional credits.

Request Body

{
  "client_id": "YOUR_CLIENT_ID",
  "client_secret": "YOUR_CLIENT_SECRET",
  "credits": 100  // optional, defaults to 100
}

Response 200

{
  "checkout_url": "https://checkout.stripe.com/c/pay/...",
  "session_id": "cs_live_..."
}

Redirect the user to checkout_url to complete payment.

GET /health Health check

Check if the service is running.

Response 200

{ "status": "ok", "service": "inbox.dog-oauth" }

Scopes

Pass the scope parameter to /oauth/authorize to control Gmail permissions.

Scope Gmail Permission Google Scope
emailRead-only access (default)gmail.readonly + userinfo.email
email:readRead-only accessgmail.readonly + userinfo.email
email:sendSend emails onlygmail.send + userinfo.email
email:fullFull access (read, send, modify)gmail.modify + userinfo.email

Rate Limits & Quotas

Limit Value Notes
OAuth state TTL10 minutesUser must complete consent within this window
Authorization code TTL5 minutesExchange the code promptly after callback
Access token lifetime3600 seconds (1 hour)Set by Google. Use refresh token to renew.
Credits per exchange1 creditOnly on POST /oauth/token with authorization_code
Token refresh costFreeNo credit charge

Billing

Item Cost
New API keyFree (includes 10 credits)
OAuth token exchange1 credit ($0.10)
Token refreshFree
Additional credits$0.10 per credit via Stripe
Self-hostedFree (MIT license)

Credits are only deducted on successful token exchanges. Failed flows, denied consent, and refreshes are free.