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.
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=S256The user consents, picks which mailbox(es) to grant, and may remove recommended scopes. Required scopes stay selected. Supermail redirects back:
GET https://app.example.com/callback?code=ac_9f3a...&state=xyz123Verify state, then exchange the code.
3. Exchange the code for tokens
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.
{
"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.
POST https://supermail.example.com/rpc
Content-Type: application/json
Authorization: Bearer <access_token>
{
"jsonrpc": "2.0",
"id": 1,
"method": "Accounts/list",
"params": {}
}{
"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.
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 } }
}{
"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.