# Discover billers from Gmail

Connect a user's Gmail with a single-use PKCE transaction, then surface live discovery progress.

## 1. Create the transaction

Generate a high-entropy verifier on your server and retain it. Send only its base64url SHA-256 challenge. This endpoint requires your secret BillerAPI key.

**Node**

````javascript
import { createHash, randomBytes } from 'node:crypto';

const codeVerifier = randomBytes(64).toString('base64url');
const codeChallenge = createHash('sha256')
  .update(codeVerifier, 'ascii')
  .digest('base64url');

const response = await fetch(
  'https://sandbox.api.billerapi.com/v1/emails/gmail/oauth-sessions',
  {
    method: 'POST',
    headers: {
      'Authorization': `Bearer ${process.env.BILLERAPI_API_KEY}`,
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({
      user_id: 'user_123',
      redirect_uri: 'https://yourapp.com/oauth/gmail',
      code_challenge: codeChallenge,
      code_challenge_method: 'S256',
    }),
  },
);
const { data } = await response.json();
// Redirect the user's browser to data.oauth_url.
````

> **Warning**
> Never expose client credentials, the verifier, the returned consent URL, or the completion proof in logs. The removed `/v1/emails/gmail/oauth-url` and one-phase `/v1/emails/gmail/connect` routes have no compatibility aliases.

## 2. Receive the callback handoff

Google returns through BillerAPI's unchanged public callback. BillerAPI validates state and records authorization, then redirects to your registered URI with `status`, `session_id`, and `completion_token` in the URL fragment. The public callback does not exchange tokens.

## 3. Complete with PKCE

**Node**

````javascript
const complete = await fetch(
  `https://sandbox.api.billerapi.com/v1/emails/gmail/oauth-sessions/${sessionId}/complete`,
  {
    method: 'POST',
    headers: {
      'Authorization': `Bearer ${process.env.BILLERAPI_API_KEY}`,
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({
      user_id: userId,
      completion_token: completionToken,
      code_verifier: codeVerifier,
    }),
  },
);

const { data } = await complete.json();
// 200: COMPLETED. 202: honor Retry-After and poll data.status_url.
````

> **Note**
> `REAUTHORIZE_REQUIRED` is terminal for this transaction. Start a fresh Google authorization; do not retry the old provider code.

## 4. Stream discovery

A completed receipt includes `discovery_run_id`. Use it with the discovery-run SSE API to render progress and ready-to-connect billers.

**Browser**

````javascript
const events = new EventSource(
  `https://sandbox.api.billerapi.com/v1/discovery-runs/${discoveryRunId}/stream`,
  { withCredentials: true },
);
````

- [API reference](/docs/api/biller-discovery)
- [All endpoints](/docs/api)
