The open-source agentic framework to build, orchestrate, and operate production AI agents.
Documentation • Quickstart • API Reference • Deployment • thaink2
This repository is the open-source core. Some capabilities named in the product —
billing, the consumption analysis screen, prospection, identity-provider sign-in,
multi-factor authentication, agent evaluation, the supervision screen, organisation
management — ship as separate commercial bricks and are absent here. Where the
core holds a hook for one, it is documented as such. A 404 on those routes means
"not in this edition", not "object not found".
The administration panel is part of this edition: users, groups, permissions, MFA enforcement. Only the management of organisations is sold separately — deciding which tenant a person belongs to governs other people's reach, rather than serving whoever runs the install.
Full documentation: docs.apowerb.com.
Three commands, a database included, and nothing to fill in:
git clone https://github.com/apowerb/apowerb-hosting.git && cd apowerb-hosting
cp .env.example .env && ./scripts/generate-secrets.sh
docker compose -f docker-compose/docker-compose.yml --env-file .env up -dThe interface is on http://localhost:3000, the API on
port 8000. generate-secrets.sh writes the random values the stack needs, and
Postgres runs inside the stack — there is no external database to provide.
Agents need a model to answer, and that is the one thing this cannot invent for
you. Add your own key in the interface, or declare a shared one in .env
(DEFAULT_LLM_MODEL and DEFAULT_LLM_API_KEY). Until then everything else
works and the model simply does not appear in the list.
apowerb-hosting holds this
stack, plus Kubernetes manifests, a Helm chart and a Traefik overlay.
Installation from source below is the other path:
it wants your own PostgreSQL.
- Quick start
- Features
- Prerequisites
- Installation from source
- Configuration
- Running
- CLI
- Architecture
- API Reference
- Available Tools
- Integrations
- Webhooks
- RAG (Retrieval-Augmented Generation)
- Text-to-SQL
- SSE Streaming
- Credits and billing
- Scheduled runs
- Agent Hub
- Development
- Troubleshooting
- REST API based on FastAPI with automatic OpenAPI documentation
- Google ADK (Agent Development Kit) for agent management and execution
- LiteLLM for multi-model compatibility (Anthropic, OpenAI, Mistral, Google, OVHcloud, etc.)
- Multi-pattern orchestration: base, parallel, sequential, loop
- Sub-agents: hierarchical agent composition
- Modular tool system with 31 tool modules (Google Workspace, Microsoft 365, databases, RAG, etc.)
- RAG as a Service: index files, URLs, databases, and S3 into knowledge bases
- Text-to-SQL: natural language to SQL query conversion
- Webhooks: Gmail (Pub/Sub) and Outlook (Graph API) push notifications to trigger agents
- OAuth integrations: GitHub, Google, Microsoft, LinkedIn
- SSE streaming: real-time agent responses, RAG progress, and notifications
- Artifact generation: agents can create and execute code files
- Agent Hub: publish and clone agents across organizations
- Scheduled runs: cron-based agent execution, driven by an external orchestrator
- Bug reports: any user files a defect from the app; the server attaches the server
log lines of the failing request (correlated by X-Request-ID), where the user was, and an optional consented screenshot. Reviewed in a triage screen before any issue is created — and the GitHub sink refuses a public repository
- Supervision: an auditable session list, scoped to what the caller may read
- Revocable sessions: a per-account cut-off that refuses tokens minted before it
- Persistent sessions with conversation context
- PostgreSQL database with auto-migrations
- Encryption for API keys, tokens, and sensitive data
- Full CLI for agent and server management
The quick start above needs Docker and Docker Compose, and nothing else. What follows is for running the core from its sources:
- Python 3.13+
- PostgreSQL
- UV (package manager)
- Install UV and create virtual environment:
pip install uv
uv venv- Activate the virtual environment:
# Windows
.venv\Scripts\activate
# macOS/Linux
source .venv/bin/activate- Install dependencies:
uv sync
uv pip install .- Configure environment:
cp .env.example .envOn the quick start path, none of these are yours to fill in — the Compose stack wires the database and generates the key. This section is for an installation that brings its own PostgreSQL.
Five settings, and the server refuses to boot without them
(RUNTIME_REQUIRED_FIELDS in configs/settings.py):
Two more are read from the same block and are not required, each having a
default: DB_PORT (5432) and DB_SCHEMA (public).
TEST_TOKEN is not required either, and has not been since 0.2.x. The only
middleware that reads it is mounted nowhere, so demanding it forced every
deployment to invent one. An .env that still carries it is harmless.
Where this installation is reached, and where it sends people. Each one ships
with a localhost default, which is right on a laptop and wrong everywhere
else — a value handed to a browser or written into a mail points at the
reader's machine, not at the server.
Set APP_PUBLIC_URL and four others follow.
Declaring APP_PUBLIC_URL fills these in, their paths being fixed by the
pages that serve them — only the origin varies:
Anything you set yourself always wins — deducing only fills a blank, and only from a base that is a single absolute URL, scheme included. A base declared but empty, or holding a comma-separated list, deduces nothing and is reported by the startup warning below. An installation that configures none of these behaves exactly as before.
CORS_ALLOWED_ORIGINS deduced from your front URL widens what the
browser is allowed to call from, compared with the localhost default. That
is the intent — but it is a security setting, so set it yourself if your
policy is narrower than "the front I just declared".
GITHUB_REDIRECT_URI and GOOGLE_REDIRECT_URI are not deduced, and not
read either: the front computes its own sign-in callback from the browser's
origin and sends it with the code exchange. Setting them changes nothing.
settings still declares these, and user still carries stripe_customer_id, but the
billing routes and the credit logic live in the billing brick. Setting them changes
nothing in a core-only install.
Two backends drive scheduled runs. ORCHESTRATOR selects one; the code still defaults to
mage, the historical backend, so an instance that wants th2etl must say so explicitly.
With ORCHESTRATOR=th2etl:
With ORCHESTRATOR=mage:
Installing a brick and loading it are two different steps: a package present in the
environment but absent from TH2_EXTENSIONS behaves exactly like one that was never
installed. Check the loaded set, not the installed set, when a capability seems missing.
apowerb serveuv run uvicorn apowerb.main:app --reloadServer starts at http://127.0.0.1:8000/
/docs, /redoc and /openapi.json are removed from the served routes by default:
they hand the full route inventory to an unauthenticated caller, which is enough to
fingerprint a deployment from the outside. Opt in with PUBLISH_API_SCHEMA=true for a
browsable Swagger; WORKING_MODE=production overrides that flag as a second lock.
The published reference is at docs.apowerb.com.
# Start server
apowerb serve --host 0.0.0.0 --port 8000
# Manage agents
apowerb agents list
apowerb agents create
apowerb agents delete <agent_id>
# Manage tools
apowerb tools list
# Manage runs
apowerb runs listapowerb/
├── src/apowerb/
│ ├── main.py FastAPI entry point, middleware, router mounting, startup
│ ├── models.py SQLAlchemy ORM models
│ │
│ ├── auth/ Email/password login, JWT dependencies, session cut-off
│ ├── users/ User CRUD
│ ├── routers/ HTTP endpoints, one module per family
│ ├── core/ Business logic behind the routers
│ │ ├── adk_runner.py ADK execution
│ │ ├── adk_agent_builder.py agent Python file generation
│ │ ├── extensions/ brick loader and registry hooks
│ │ ├── guardrails.py input/output guardrails
│ │ ├── run_gate.py admission control for runs
│ │ └── history_compaction.py conversation trimming
│ ├── tools_store/ Tool registry + portfolio of tool modules
│ ├── skills_store/ Reusable agent skills
│ ├── agent_store/ Agent templates and seeds
│ ├── bi/ Charts, dashboards, datasets
│ ├── sqlgen/ Text-to-SQL generation
│ ├── integrations/ OAuth workspace integrations
│ ├── artifacts/ Artifact storage and execution
│ ├── scheduler/ Orchestrator clients (th2etl, mage), background workers
│ ├── storage/ Local and S3 storage abstraction
│ ├── middleware/ Request middleware
│ ├── helpers/ Database, security, encryption, notification bus, migrations
│ ├── schema/ Pydantic schemas
│ ├── configs/ Settings and logging
│ └── cli/ Typer CLI (`apowerb`)
│
├── agents_pool/ Generated agent code (runtime)
├── artifacts_store/ Artifacts written by agents (runtime, local mode)
├── uploads/ Uploaded files (runtime, local mode)
├── tests/
└── pyproject.toml
Routers not to look for here — they arrive with the bricks: billing, usage analytics,
prospection, identity-provider sign-in, MFA, evaluation, organisation management
(/api/admin/organizations*). The rest of /api/admin is here. Session listing and
traces are served here; only the supervision screen is a brick.
On application start, apowerb:
- Creates required directories (agents_pool/,artifacts_store/,uploads/)
- Runs auto-migrations (ensure_*functions) for all database tables
- Generates agent Python modules from database
- Starts the webhook renewal background task (every 6 hours)
- Mounts the Google ADK FastAPI sub-application
The full reference — every route, parameter and response shape — is generated from the code and published at docs.apowerb.com/api-reference. It is not duplicated here: a hand-maintained route table in this file went stale within two weeks last time, and a stale reference is worse than none.
Route families in this edition, all under /api unless noted:
Absent from this edition, provided by bricks: billing, usage, prospection,
campaigns, MFA (/api/auth/mfa/*), identity-provider sign-in (/api/users/{github,google,microsoft,linkedin}),
evaluation, and organisation management (/api/admin/organizations*). They
answer 404 here. The rest of /api/admin — users, groups, permissions — is served here.
Supervision is split: the session list and traces are served here, and the core answers on
its own who may read another account's session (register_supervision_scope, wired to the
superadmin check); the supervision screen is what a brick provides.
Since the release that carries this note, the five /api/admin/organizations* routes are
served by a brick rather than by the core. The mechanism stayed, deliberately:
- the admin_organizationandadmin_org_membertables are still created here, and existing rows are left untouched;
- an administrator is still bounded by the organisation he belongs to, so a boundary drawn before the upgrade keeps being enforced after it;
- a user's organisation is still reported by /api/admin/usersand/api/admin/me.
What an install without the brick loses is the ability to create, rename, delete an organisation or to move somebody between two. If you never created one, nothing changes: with no organisation to belong to, an administrator who is not a superadmin administers only himself — which is what this build already did.
apowerb supports two categories of OAuth:
- User Login — Sign up / log in via OAuth (GitHub, Google, Microsoft, LinkedIn)
- Workspace Integrations — Connect external services as tools for agents
- User calls GET /api/integrations/{provider}/{service}/connectto get the OAuth URL
- User authorizes the app on the provider's consent screen
- Provider redirects to POST /api/integrations/{provider}/callback
- apowerb stores the access/refresh tokens encrypted in the integrationstable
- Agent tools can now use the integration tokens
apowerb can automatically trigger agents when events occur in connected services (new email, etc.). Two providers are supported: Gmail (via Google Pub/Sub) and Outlook (via Microsoft Graph subscriptions).
Email arrives → Provider pushes notification → apowerb receives it
→ Fetches new email content → Runs associated agent → Logs result
Create a subscription:
POST /api/webhooks/subscriptions
{
"provider": "google_gmail",
"resource": "INBOX",
"agent_id": "AGENT_ID",
"change_type": "created",
"agent_message_template": "New email from {{ sender }}: {{ subject }}\n\n{{ body }}"
}- Gmail watches expire after 7 days
- Outlook subscriptions expire after 3 days
- A background task runs every 6 hours and renews subscriptions expiring within 12 hours
Gmail ──push──▶ Google Pub/Sub ──HTTP POST──▶ apowerb
(topic) /api/webhooks/gmail/notifications
│
▼
Fetch new emails via Gmail API
│
▼
Run associated agent
- A Google Cloud project with billing enabled
- Gmail API and Cloud Pub/Sub API enabled
- A Google OAuth 2.0 application for workspace integration
- A publicly accessible URL for apowerb
gcloud services enable gmail.googleapis.com pubsub.googleapis.com \
--project=YOUR_PROJECT_IDgcloud pubsub topics create gmail-notifications \
--project=YOUR_PROJECT_IDGmail uses the service account gmail-api-push@system.gserviceaccount.com to push notifications. Grant it the Pub/Sub Publisher role:
gcloud pubsub topics add-iam-policy-binding gmail-notifications \
--project=YOUR_PROJECT_ID \
--member="serviceAccount:gmail-api-push@system.gserviceaccount.com" \
--role="roles/pubsub.publisher"Note: In the Google Cloud Console UI, the "Publisher" role may not appear in the dropdown by default — type "publisher" in the search bar to find it, or use the CLI command above.
gcloud pubsub subscriptions create gmail-notifications-push \
--topic=gmail-notifications \
--push-endpoint=https://YOUR_DOMAIN/api/webhooks/gmail/notifications \
--project=YOUR_PROJECT_IDIn Google Cloud Console → Credentials:
- Create an OAuth 2.0 Client ID (type: Web application)
- Add authorized redirect URI: https://YOUR_DOMAIN/integrations/google/callback
- Add scope: https://www.googleapis.com/auth/gmail.readonly
GOOGLE_INTEGRATION_CLIENT_ID=your-client-id.apps.googleusercontent.com
GOOGLE_INTEGRATION_CLIENT_SECRET=GOCSPX-xxxxxxxxxx
GOOGLE_INTEGRATION_REDIRECT_URI=https://YOUR_DOMAIN/integrations/google/callback
GMAIL_PUBSUB_PROJECT_ID=your-gcp-project-id
GMAIL_PUBSUB_TOPIC=gmail-notifications
PUBLIC_BASE_URL=https://YOUR_DOMAINIn the apowerb-ui UI: Integrations → Connect Google (Gmail)
Via UI (Webhook Manager) or API:
curl -X POST https://YOUR_DOMAIN/api/webhooks/subscriptions \
-H "Authorization: Bearer TOKEN" \
-H "Content-Type: application/json" \
-d '{
"provider": "google_gmail",
"resource": "INBOX",
"agent_id": "AGENT_ID",
"change_type": "created"
}'- A registered Microsoft Azure AD application
- Microsoft Graph API permissions: Mail.Read
- A publicly accessible HTTPS URL
- Configure MICROSOFT_INTEGRATION_*environment variables
- Connect Outlook integration in the UI
- Create a webhook subscription with "provider": "microsoft_outlook"
The Outlook handler uses Microsoft Graph change notifications with clientState validation.
RAG lets agents answer questions based on documents you provide — PDFs, web pages, database exports, or S3 files.
Upload documents → Documents get indexed (via th2llm) → Agent searches them when answering
- Poll: GET /api/rag/status/{knowledge_id}
- Stream (SSE): GET /api/rag/stream/{agent_id}?session_id=xxx
- Path traversal prevention on session/agent IDs
- SSRF protection on URL indexing (blocks localhost, private IPs)
- Session ownership validation
- HMAC-SHA256 signature verification on th2llm webhooks
Agents can connect to relational databases and convert natural language questions into SQL queries.
- Create a database tool config with connection parameters
- Create an agent with text_to_sqltool enabled
- Ask questions — the agent introspects the schema, generates SQL, executes it, and returns results
Three SSE streaming channels are available:
Not in this edition. The user row carries a credit balance and transactions /
credit_purchases exist in the schema, but the packages, the Stripe checkout and the
crediting logic belong to the billing brick. /api/billing/* answers 404 here.
Token metering, by contrast, is in the core since 09/09/26. register_core_usage() is
wired when the app is built, before load_overlay(), and the recorder writes one
llm_usage row per completed model turn. GET /api/config/default-llm/usage serves the
caller their own gauge. What stays commercial is the consumption analysis screen —
usage broken down per agent, per tool and per user.
The cap came with the counter, and it covers only the shared thaink2/default model
(llm_usage.billed_to_thaink2): a personal API key is paid for by whoever supplies it, so
it is never capped. A run over the cap is refused with a 402 before the answer starts,
never cut mid-stream. DEFAULT_LLM_MONTHLY_TOKEN_QUOTA is the per-user allowance for the
current Europe/Paris calendar month, and 0 means unlimited — the kill-switch to use
without a redeploy if the guard blocks wrongly. DEFAULT_LLM_USER_TOKEN_CAP and
DEFAULT_LLM_GLOBAL_TOKEN_CAP (sliding window of DEFAULT_LLM_CAP_WINDOW_HOURS, 0 =
unlimited) are the fallback cap, per account and for the whole deployment; they apply only
when no scale is registered on register_default_llm_cap, and the core registers one, so
a standard install is capped by the monthly quota.
Agent runs can be automated on a schedule. The core does not run the cron itself: it
registers the schedule with an external orchestrator, selected by ORCHESTRATOR
(th2etl, or the historical mage which is still the code default).
POST /api/adk/schedule_run
{
"agent_id": "agent42",
"user_id": "user@example.com",
"session_id": "session_scheduled",
"new_message": {
"role": "user",
"parts": [{ "text": "Generate the daily report" }]
},
"schedule_interval": "@daily",
"start_time": "2026-03-10T08:00:00"
}Agents can be published to a shared Hub and cloned by other users:
- POST /api/hub/publish— Publish an agent (copies config, tools, instructions)
- POST /api/hub/clone— Clone a Hub agent into your workspace
- GET /api/hub— Browse available agents
Sessions, events and ADK artifacts live in the tables Google ADK owns (sessions,
events), which supervision reads directly rather than fanning out one HTTP call per
agent.
Some columns here serve bricks rather than the core: credits, stripe_customer_id,
mfa_* and the transactions / credit_purchases tables are declared so a brick can use
them, and stay untouched without one. llm_usage is no longer one of them: the core writes
to it itself (see Credits and billing).
Via LiteLLM, all major model providers are supported:
- Anthropic: anthropic/claude-sonnet-4-5-20250929,anthropic/claude-3-haiku-20240307
- OpenAI: openai/gpt-4o,openai/gpt-4,openai/gpt-3.5-turbo
- Mistral: mistral/mistral-large-latest
- Google: gemini/gemini-pro
- Azure AI Foundry: azure_ai/llama-3-3-70b-instruct
- OVHcloud: ovhcloud/DeepSeek-R1-Distill-Llama-70B
- And more...
For a Foundry serverless endpoint, set the model name with the azure_ai/
provider prefix and pass the endpoint settings in agent_model_params:
{
"agent_model": "azure_ai/llama-3-3-70b-instruct",
"agent_model_params": {
"model_api_base": "https://<resource>.models.ai.azure.com",
"model_api_key": "<api-key>",
"model_api_version": "2025-04-01-preview"
}
}model_api_version is optional and is also accepted as api_version.
For Microsoft Entra ID, resolve a bearer token with DefaultAzureCredential
in the deployment environment and provide that token as model_api_key; LiteLLM
passes it through the same api_key argument. Tool schemas and tool calls remain
enabled through the normal ADK LiteLlm path.
# Install development dependencies
uv sync --group dev
# Run tests
pytest
# Linting
ruff check .
ruff format .Guides, quickstart and the generated API reference are at docs.apowerb.com. Their source lives in apowerb/apowerb-docs.
Contributions are welcome. CONTRIBUTING.md covers the development setup, the branch and commit conventions this repository actually uses, and what makes a pull request reviewable.
Security issues take a different path: see SECURITY.md, and do not open a public issue for one.
For questions, contact the thaink² team or open an issue.
apowerb is distributed under the Apache License 2.0. Copyright 2025-2026 thaink².
This repository holds the open-source core. The commercial bricks — billing, the consumption analysis screen, prospection, identity-provider sign-in, multi-factor authentication, agent evaluation, the supervision screen and organisation management — are distributed separately under commercial terms and are not covered by this licence. The token metering and the cap themselves live in this repository, so they are covered by it.
"apowerb" and the apowerb logo are trademarks of thaink². The licence covers the code, not the marks — see TRADEMARK.md.