Pip is healthy GitHub Sign in

Connect a user's accounts by voice

A Pipbit device has no screen, so OAuth consent happens on a second screen — the same way Alexa and Google account linking work. Your agent starts the link in conversation, the platform brokers the hand-off to a browser at verify.pipbit.ai, and the token lands with your agent, stored per user. The platform never sees it.

The flow

  1. The user asks for something that needs their account. Ask for a spoken yes first.
  2. Your agent calls agent.start_device_link(subject_id, provider_label="Google").
  3. The platform picks the entry method: users with a verified phone are texted a code; everyone else gets a spoken three-word code back in the DeviceLink.
  4. Your agent tells the user where to go — verify.pipbit.ai, plus the code if it was spoken.
  5. The user enters the code in any browser, sees what is being connected, and confirms.
  6. The page redirects to your provider's consent screen; after approval the provider redirects to GET /pipbit/oauth/callback on your agent — a route the SDK serves automatically. The SDK verifies the signed state, exchanges the code, and stores the token under the user's subject_id.
  7. Your on_account_link(subject_id, token) hook fires — push a spoken confirmation.
  8. Every later turn, agent.get_oauth_token(subject_id) returns a live token, refreshed as needed.

Configuration

from pipbit import Agent, OAuth2Config, FileTokenStore

agent = Agent(
    name="garry",
    secret="pb_live_sk_...",
    oauth=OAuth2Config(
        authorize_url="https://accounts.google.com/o/oauth2/v2/auth",
        token_url="https://oauth2.googleapis.com/token",
        client_id="...",
        client_secret="...",
        redirect_uri="https://your-host.example.com/pipbit/oauth/callback",
        scope="https://www.googleapis.com/auth/gmail.readonly",
        token_endpoint_auth="post",       # Google wants id/secret in the body
        extra_authorize_params={
            "access_type": "offline",     # without BOTH of these, Google issues
            "prompt": "consent",          # no refresh token and the link dies
        },
    ),
    token_store=FileTokenStore("tokens.json"),   # MemoryTokenStore() for dev
    on_account_link=my_linked_hook,              # fn(subject_id, token)
)

redirect_uri must be HTTPS, point at /pipbit/oauth/callback on your host, and be registered identically with your provider. The authorize_url must be HTTPS too — the platform rejects a plain-http one at start_device_link (loopback is allowed for local mocks). token_endpoint_auth is "basic" or "post"; extra_authorize_params covers provider quirks — the values above are exactly what Google needs for durable refresh tokens.

The voice flow in your handler

def start_link(req):
    link = agent.start_device_link(req.subject_id, provider_label="Google")
    if link.method == "code" and link.user_code:
        # no phone on file: speak the three-word code
        return f"Go to verify.pipbit.ai and enter {link.user_code} to connect your Google account."
    # verified phone: the platform texted the code — never speak it
    return "I've texted you a code. Enter it at verify.pipbit.ai to connect your Google account."

Ask before you link (an explicit spoken yes, where "okay, no thanks" counts as no), and strip the URL scheme when speaking — "verify.pipbit.ai" reads better aloud than "https://…".

Tokens

agent.get_oauth_token(subject_id) returns a valid OAuth2Token, refreshing behind a per-subject lock when expired. If a refresh fails (revoked or expired grant) the stale token is dropped and it returns None — your unlinked path runs and the user re-links. FileTokenStore uses atomic writes, quarantines corrupt files, and keeps the old refresh token when a provider omits a new one. For multi-process deployments, implement the three-method TokenStore ABC over Redis or a database.

Portal settings

In the developer portal, set Authentication mode to None (public) even though your agent does OAuth: that dropdown gates access before a conversation starts, while SDK account linking happens during it, and start_device_link supplies the per-user consent URL dynamically. Leave the auth configuration URL blank.

Worked examples

Garry (Google — Gmail and Calendar) and the Sonos agent are complete worked examples, including verbal confirmation state, spoken-code handling, and on_account_link confirmations. Ask us for the example source if you don't have repo access.