Supermail

Quickstart

From zero to reading a mailbox's events — register, authorize, exchange, and call the API.

This walks the whole integration end to end. Replace https://supermail.example.com with your deployment's base URL.

1. Register your application

Register an app to obtain a client_id, a client_secret (confidential clients), your exact redirect URIs, and the scopes it may request. Keep the secret safe — it is shown once.

Classify the capabilities every grant must include as required_scopes; all other registered scopes are recommended. For this walkthrough the app requests openid mail.read accounts.read. The user sees all three selected and may remove any that your app did not mark required.

2. Send the user to authorize

Redirect the user's browser to /oauth/authorize with PKCE. Generate a code_verifier, derive code_challenge = base64url(sha256(verifier)), and keep the verifier.

Browser redirect
GET https://supermail.example.com/oauth/authorize?response_type=code
  &client_id=app_01H9A...
  &redirect_uri=https%3A%2F%2Fapp.example.com%2Fcallback
  &scope=openid%20mail.read%20accounts.read
  &state=xyz123
  &code_challenge=E9Melhoa2OwvFrEMTJguCHaoeK1t8URWbuGJSstw-cM
  &code_challenge_method=S256

The user consents, picks which mailbox(es) to grant, and may remove recommended scopes. Required scopes stay selected. Supermail redirects back:

Redirect back to your app
GET https://app.example.com/callback?code=ac_9f3a...&state=xyz123

Verify state, then exchange the code.

3. Exchange the code for tokens

Request
POST https://supermail.example.com/oauth/token
Content-Type: application/x-www-form-urlencoded

grant_type=authorization_code
&code=ac_9f3a...
&code_verifier=dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk
&client_id=app_01H9A...

The OAuth 2.1 token request does not repeat the authorization request's redirect_uri.

Response
{
  "token_type": "Bearer",
  "access_token": "<jwt>",
  "expires_in": 600,
  "refresh_token": "rt_5b2c...",
  "id_token": "<jwt>",
  "scope": "openid mail.read accounts.read"
}

4. List the granted mailboxes

Now call the Resource API with the access token.

Request
POST https://supermail.example.com/rpc
Content-Type: application/json
Authorization: Bearer <access_token>

{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "Accounts/list",
  "params": {}
}
Response
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "accounts": [{
      "id": "account_01H9X...",
      "lifecycle": "ACCOUNT_LIFECYCLE_ACTIVE",
      "created_at": "2026-08-21T10:00:00Z",
      "updated_at": "2026-08-21T10:00:00Z",
      "mine": true,
      "service_bindings": [{
        "id": "binding_01H9Y...",
        "account_id": "account_01H9X...",
        "role": "SERVICE_ROLE_MAIL_READ",
        "provider": "gmail",
        "adapter": "gmail-mail",
        "lifecycle": "SERVICE_BINDING_LIFECYCLE_ACTIVE",
        "generation": "1",
        "read_epoch": "1",
        "freshness_policy": "FRESHNESS_POLICY_MANAGED",
        "intended_capabilities": ["CAPABILITY_MAIL_READ"],
        "working_capabilities": ["CAPABILITY_MAIL_READ"],
        "issues": [],
        "created_at": "2026-08-21T10:00:00Z",
        "updated_at": "2026-08-21T10:00:00Z"
      }],
      "presentation": {
        "revision": "0",
        "updated_at": "2026-08-21T10:00:00Z"
      }
    }],
    "page": { "has_more": false }
  }
}

5. Read the mailbox's events

Page through the event log. Events are thin change notifications, not message content.

Request
POST https://supermail.example.com/rpc
Content-Type: application/json
Authorization: Bearer <access_token>

{
  "jsonrpc": "2.0",
  "id": 2,
  "method": "Events/list",
  "params": { "account_id": "account_01H9X...", "types": ["email.*"], "page": { "size": 50 } }
}
Response
{
  "jsonrpc": "2.0",
  "id": 2,
  "result": {
    "events": [
      {
        "id": "event_3Nq7...",
        "seq": "42",
        "type": "email.received",
        "account_id": "account_01H9X...",
        "created": "2026-07-12T09:15:00+00:00",
        "email_received": { "provider_id": { "object_id": "..." }, "placements": [{ "mailbox_id": "...", "occurrence_id": "..." }] }
      }
    ],
    "page": { "next_token": "eyJ..." }
  }
}

Fold these into your own state, deduplicating on event.id. To follow new events as they happen, configure a webhook consumer.

Next steps

On this page