Skip to content

5-minute Quickstart

python -m pip install "chattice[fastapi]" uvicorn

This app answers any Google Chat message synchronously. It uses the actual public API: Dispatcher is the application event engine, Router owns handlers, and the FastAPI integration exposes the HTTPS endpoint.

1. Create app.py

import os

from fastapi import FastAPI

from chattice import Dispatcher, Router
from chattice.events import MessageEvent
from chattice.integrations.fastapi import create_chat_router
from chattice.transports.http import GoogleTokenVerifier

router = Router()


@router.message()
async def hello(message: MessageEvent) -> str:
    return "Hello from Google Chat!"


dispatcher = Dispatcher()
dispatcher.include_router(router)

app = FastAPI()
app.include_router(
    create_chat_router(
        dispatcher,
        GoogleTokenVerifier(audience=os.environ["CHATTICE_AUDIENCE"]),
    )
)

Returning a string is the synchronous response to the current interaction. It does not make a Chat API call and must complete within Google's interaction deadline (30 seconds). Outbound Bot calls are a separate, authenticated channel.

The webhook endpoint is mounted at the root path / by default; pass path="..." to create_chat_router(...) to serve it elsewhere.

2. Run it

Choose the same audience value that you will select in the Chat API configuration: either the exact public HTTPS endpoint URL or the Cloud project number.

export CHATTICE_AUDIENCE="https://chat.example.com/"
python -m uvicorn app:app --host 0.0.0.0 --port 8000

# with uv
uv run uvicorn app:app --host 0.0.0.0 --port 8000

The endpoint must be publicly reachable over HTTPS before Google Chat can call it. For local development, use an HTTPS tunnel tool (ngrok, cloudflared, or similar) and set both the configuration URL and CHATTICE_AUDIENCE to that exact public URL. If you forget to export CHATTICE_AUDIENCE, the app fails at startup with KeyError: 'CHATTICE_AUDIENCE' — set the variable and run again. Never use MockVerifier on an endpoint reachable by Google or other users; it is only for isolated tests.

3. Configure and try it

Follow Create/configure a Google Chat app, choose the HTTP endpoint, save the app, then open a direct message with it or add it to a test Space. Send hello; the response is Hello from Google Chat!.

In a shared Space, Chat normally invokes the app when mentioned or through a configured command. Direct messages are their own Spaces.

4. Run it over Pub/Sub instead (no public HTTPS endpoint)

No public URL available? Google Chat can deliver events into a Cloud Pub/Sub topic, and a long-lived subscriber process consumes them — no web server, no domain, no TLS. The complete echo bot:

python -m pip install "chattice[pubsub]"
# app.py
import asyncio
import os

from chattice import Dispatcher, Router
from chattice.auth import ServiceAccountCredentialsProvider
from chattice.client import Bot
from chattice.events import MessageEvent

router = Router()


@router.message()
async def echo(message: MessageEvent) -> None:
    await message.reply(f"You said: {message.text}")


async def main() -> None:
    service_account_file = os.environ["CHATTICE_SERVICE_ACCOUNT_FILE"]
    app_credentials = ServiceAccountCredentialsProvider.from_service_account_file(
        service_account_file
    )
    pull_credentials = ServiceAccountCredentialsProvider.from_service_account_file(
        service_account_file, scopes=["https://www.googleapis.com/auth/pubsub"]
    )
    async with Bot(app_credentials_provider=app_credentials) as bot:
        dispatcher = Dispatcher(bot=bot)
        dispatcher.include_router(router)
        await dispatcher.run_pubsub(
            os.environ["CHATTICE_SUBSCRIPTION"],
            bot=bot,
            credentials_provider=pull_credentials,
        )


if __name__ == "__main__":
    asyncio.run(main())
export CHATTICE_SUBSCRIPTION="projects/<project>/subscriptions/<name>"
export CHATTICE_SERVICE_ACCOUNT_FILE="/path/to/service-account.json"
python app.py

The service account needs the Pub/Sub Subscriber role on the subscription. The two providers request Pub/Sub scopes for the subscriber and chat.bot for outbound calls; subscriber credentials are not inherited from the Bot. Create the topic and the pull subscription, then select the topic in the Chat app connection settings — Pub/Sub setup.

There is no synchronous response channel on Pub/Sub: the handler answers through outbound calls (message.reply(), bot.app.messages.create()), and dialogs and App Home are unavailable. Pass bot=bot to run_pubsub — the runner injects it into the DI context for handlers. For interactive UI keep the HTTP transport above. See HTTP and Pub/Sub for the full transport comparison, including the push mode (create_pubsub_router) for HTTPS endpoints that want durable delivery.

5. Verify without Google credentials

The repository's examples/docs/from_zero.py runs the same parser and dispatcher plus replies, Threads, commands, buttons, forms, dialogs, private messages, and Workspace Events using the public MockBot. This is a framework test, not a request-verification bypass:

python examples/docs/from_zero.py

Expected output:

documentation journey OK

Next: Configure Google Chat.