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 -d
The 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.
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 repositoryThe quick start above needs Docker and Docker Compose, and nothing else. What follows is for running the core from its sources:
pip install uv
uv venv
# Windows
.venv\Scripts\activate
# macOS/Linux
source .venv/bin/activate
uv sync
uv pip install .
cp .env.example .env
On 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):
| Variable | Description |
|---|---|
DB_HOST |
PostgreSQL database host |
DB_NAME |
Database name |
DB_USER |
Database username |
DB_PASSWORD |
Database password |
ENCRYPT_KEY |
Encryption key for secrets and JWT signing — a url-safe base64 32-byte key, Fernet.generate_key() |
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.
| Variable | Default | Description |
|---|---|---|
WORKING_MODE |
development |
development or production |
ALGORITHM |
HS256 |
JWT signing algorithm |
ACCESS_TOKEN_EXPIRE_MINUTES |
120 |
JWT token lifetime |
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.
| Variable | Default | Description |
|---|---|---|
APP_PUBLIC_URL |
http://localhost:3000 |
Public URL of the front app. Password-reset and e-mail-verification links are built from it, and the settings below are deduced from it |
PUBLIC_BASE_URL |
http://localhost:8000 |
Public URL of this API, for the callback URLs it hands out to webhooks |
ROOT_PATH |
http://localhost:8000 |
Base of the HTTP calls this API makes to itself. Usually an internal address, not the public one |
Declaring APP_PUBLIC_URL fills these in, their paths being fixed by the
pages that serve them — only the origin varies:
| Deduced | Becomes |
|---|---|
GITHUB_INTEGRATION_REDIRECT_URI |
<APP_PUBLIC_URL>/integrations/github/callback |
GOOGLE_INTEGRATION_REDIRECT_URI |
<APP_PUBLIC_URL>/integrations/google/callback |
FRONTEND_URLS |
<APP_PUBLIC_URL> |
CORS_ALLOWED_ORIGINS |
<APP_PUBLIC_URL> |
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.
| Variable | Default | Description |
|---|---|---|
CORS_ALLOWED_ORIGINS |
http://localhost:3000 |
The origin whitelist CORS actually uses (comma-separated) |
FRONTEND_URLS |
http://localhost:3000 |
Despite the name, not read by CORS — only used to derive the Outlook mail OAuth callback |
| Variable | Description |
|---|---|
GITHUB_CLIENT_ID / GITHUB_CLIENT_SECRET |
GitHub OAuth (login) |
GITHUB_REDIRECT_URI |
GitHub login callback |
GOOGLE_CLIENT_ID / GOOGLE_CLIENT_SECRET |
Google OAuth (login) |
GOOGLE_REDIRECT_URI |
Google login callback |
MICROSOFT_CLIENT_ID / MICROSOFT_CLIENT_SECRET |
Microsoft OAuth (login) |
MICROSOFT_TENANT_ID |
Microsoft tenant (common for multi-tenant) |
LINKEDIN_CLIENT_ID / LINKEDIN_CLIENT_SECRET |
LinkedIn OAuth (login) |
| Variable | Description |
|---|---|
GOOGLE_INTEGRATION_CLIENT_ID |
OAuth app for Google Workspace (Drive, Gmail, Calendar, Sheets, Docs) |
GOOGLE_INTEGRATION_CLIENT_SECRET |
Google integration secret |
GOOGLE_INTEGRATION_REDIRECT_URI |
Google integration callback |
MICROSOFT_INTEGRATION_CLIENT_ID |
OAuth app for Microsoft 365 (Outlook, Teams, OneDrive, SharePoint) |
MICROSOFT_INTEGRATION_CLIENT_SECRET |
Microsoft integration secret |
MICROSOFT_INTEGRATION_TENANT_ID |
Microsoft integration tenant |
MICROSOFT_INTEGRATION_REDIRECT_URI |
Microsoft integration callback |
OUTLOOK_MAIL_REDIRECT_URI |
Callback for the Outlook Mail tool connection. Deduced from APP_PUBLIC_URL |
GITHUB_INTEGRATION_CLIENT_ID |
OAuth app for GitHub workspace integration |
GITHUB_INTEGRATION_CLIENT_SECRET |
GitHub integration secret |
GITHUB_INTEGRATION_REDIRECT_URI |
GitHub integration callback |
| Variable | Description |
|---|---|
GMAIL_PUBSUB_PROJECT_ID |
Google Cloud project ID |
GMAIL_PUBSUB_TOPIC |
Pub/Sub topic name (just the name, not the full path) |
| Variable | Default | Description |
|---|---|---|
PUBLIC_BASE_URL |
http://localhost:8000 |
Public URL of this apowerb instance |
RAG_WEBHOOK_SECRET |
th2-webhook-default-secret |
HMAC-SHA256 secret for RAG webhooks (change in production!) |
RAG_BASE_URL |
— | RAG API base URL (th2llm) |
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.
| Variable | Description |
|---|---|
STRIPE_SECRET_KEY |
Stripe secret key |
STRIPE_PUBLISHABLE_KEY |
Stripe publishable key |
STRIPE_WEBHOOK_SECRET |
Stripe webhook signing secret |
| Variable | Description |
|---|---|
STORAGE_MODE |
local or s3 |
S3_REGION |
AWS region |
S3_ACCESS_KEY / S3_ACCESS_KEY_SECRET |
S3 credentials |
S3_ENDPOINT |
S3 endpoint URL |
S3_BUCKET_NAME |
Bucket name |
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.
| Variable | Default | Description |
|---|---|---|
ORCHESTRATOR |
mage |
mage or th2etl |
With ORCHESTRATOR=th2etl:
| Variable | Default | Description |
|---|---|---|
TH2ETL_BASE_URL |
http://localhost:8009 |
th2etl instance |
TH2ETL_API_KEY |
— | Must equal the API_KEY of that th2etl instance — its business routes require Authorization: Bearer <key> |
With ORCHESTRATOR=mage:
| Variable | Default | Description |
|---|---|---|
BASE_URL |
http://localhost:6789 |
Mage AI scheduler URL |
API_KEY |
— | Mage API key |
OAUTH_TOKEN |
— | Mage OAuth token |
PROJECT_NAME |
default_repo |
Mage project name |
| Variable | Default | Description |
|---|---|---|
TH2_EXTENSIONS |
— | Comma-separated modules to load as bricks |
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 serve
uv run uvicorn apowerb.main:app --reload
Server 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 list
apowerb/
├── 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:
agents_pool/, artifacts_store/, uploads/)ensure_* functions) for all database tablesThe 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:
| Family | What it covers |
|---|---|
auth |
Email/password login, refresh, logout, password reset, email verification |
users |
User CRUD, me |
bug-reports |
In-app defect reports, triage, and the reviewed GitHub issue sink |
agents |
Agent CRUD, reload, status, template resync, run |
adk |
ADK execution: run, streaming (run_sse), sessions, titles, traces |
tools, tools_config |
Tool registry and per-agent tool configuration |
skills, workflows |
Reusable skills, multi-step flows |
superagents |
Templates composing several agents |
rag |
Indexing files, URLs, databases, S3; status and progress stream |
artifacts |
Files produced by agents, listing, download, execution, library |
files |
Upload (including chunked), download, delete |
data-lake |
Pin storage read/write/list |
hub |
Publish and clone agents |
integrations |
OAuth workspace connections (Google, Microsoft, GitHub) |
emailing |
Outlook mail OAuth flow, shared mailboxes |
webhooks |
Subscription CRUD and inbound push dispatch |
notifications |
User notifications and SSE stream |
supervision |
Session list and session trace for audit (/api/adk/sessions*), scoped to what the caller may read |
scheduler |
Scheduled runs through the configured orchestrator |
charts, dashboards, bi-* |
BI: charts, dashboards, datasets, refresh, stats |
share |
Shareable conversation links |
api-keys |
Saved provider keys |
config, health |
Public configuration, liveness |
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:
admin_organization and admin_org_member tables are still created here, and
existing rows are left untouched;/api/admin/users and /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.
| Tool Module | Functions |
|————-|———–|
| google_drive | List, search, read, download files |
| google_gmail | List, search, read, send emails |
| google_calendar | List, search, create events |
| google_sheets | Get spreadsheet info, read, write cells |
| google_docs | Create, read, append to documents |
| Tool Module | Functions |
|————-|———–|
| outlook_mail | List, search, read, send emails, list folders, download attachments |
| onedrive | List, search, read, download, upload files, create folders, delete, shared files |
| teams | List chats, get/send messages, reply, search, create group chats, list members |
| Tool Module | Functions |
|————-|———–|
| database | Generic database queries, SQL execution |
| text_to_sql | Natural language to SQL, schema introspection |
| db_to_rag | Index database query results into RAG |
| data_handler | Data filtering, aggregation, transformation |
| Tool Module | Functions |
|————-|———–|
| rag | Create, list, search, delete knowledge bases |
| memory | Save/search user memory, tool usage patterns |
| Tool Module | Functions |
|————-|———–|
| emailing | Send emails (SMTP) |
| marketing | HubSpot integration, sales leads |
| Tool Module | Functions |
|————-|———–|
| basic | Read files, data loader, image conversion, bearer tokens |
| api_call | Generic API calls, Thaink² forecast |
| s3_tools | Read S3 PDFs, search S3 files |
| visualization | Chart generation, CSV to chart export |
| web_search_mcp | Web search via MCP |
| thaink2 | Custom Thaink² RAG tools |
apowerb supports two categories of OAuth:
GET /api/integrations/{provider}/{service}/connect to get the OAuth URLPOST /api/integrations/{provider}/callbackintegrations table| Provider | Services | Scopes |
|---|---|---|
| Drive, Gmail, Calendar, Sheets, Docs | Service-specific (e.g., gmail.readonly, drive.readonly) |
|
| Microsoft | Outlook, Teams, OneDrive, SharePoint | Service-specific (e.g., Mail.Read, Files.ReadWrite) |
| GitHub | Repositories, Gists | repo, gist, read:user |
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
| Method | Endpoint | Description |
|---|---|---|
| POST | /api/webhooks/subscriptions |
Create subscription |
| GET | /api/webhooks/subscriptions |
List subscriptions |
| PATCH | /api/webhooks/subscriptions/{id} |
Update (agent, template, change_type) |
| DELETE | /api/webhooks/subscriptions/{id} |
Delete and unsubscribe |
| POST | /api/webhooks/subscriptions/{id}/renew |
Manually renew |
| GET | /api/webhooks/logs |
View execution logs |
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 : \n\n"
}
Gmail ──push──▶ Google Pub/Sub ──HTTP POST──▶ apowerb
(topic) /api/webhooks/gmail/notifications
│
▼
Fetch new emails via Gmail API
│
▼
Run associated agent
gcloud services enable gmail.googleapis.com pubsub.googleapis.com \
--project=YOUR_PROJECT_ID
gcloud pubsub topics create gmail-notifications \
--project=YOUR_PROJECT_ID
Gmail 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_ID
In Google Cloud Console → Credentials:
https://YOUR_DOMAIN/integrations/google/callbackhttps://www.googleapis.com/auth/gmail.readonlyGOOGLE_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_DOMAIN
In 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"
}'
Mail.ReadMICROSOFT_INTEGRATION_* environment variables"provider": "microsoft_outlook"The Outlook handler uses Microsoft Graph change notifications with clientState validation.
| Error | Cause | Fix |
|---|---|---|
403 — User not authorized |
Gmail can’t publish to Pub/Sub topic | Grant gmail-api-push@system.gserviceaccount.com the Publisher role on your topic |
404 — Topic not found |
Topic missing or wrong project | Verify GMAIL_PUBSUB_PROJECT_ID and GMAIL_PUBSUB_TOPIC |
| Notifications not arriving | Push subscription misconfigured | Verify the push endpoint URL is correct and publicly accessible |
401 — Invalid credentials |
OAuth token expired | Reconnect the integration from the UI |
| Watch expired | Auto-renewal failed | Check scheduler logs, manually renew via API |
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
| Source | Endpoint | Description |
|---|---|---|
| Files | POST /api/rag/index-files |
Upload PDF, CSV, TXT, DOCX, etc. (max 50 MB/file) |
| URL | POST /api/rag/index-url |
Crawl and index a web page |
| Database (SQL) | POST /api/rag/index-db |
Index query results using a SQL query |
| Database (NL) | POST /api/rag/index-db-nl |
Index query results using natural language |
| S3 | POST /api/rag/index-s3 |
Index files from an S3 bucket |
GET /api/rag/status/{knowledge_id}GET /api/rag/stream/{agent_id}?session_id=xxxAgents can connect to relational databases and convert natural language questions into SQL queries.
text_to_sql tool enabledThree SSE streaming channels are available:
| Channel | Endpoint | Events |
|---|---|---|
| Agent response | POST /api/adk/run_sse |
Token-by-token agent output |
| RAG progress | GET /api/rag/stream/{agent_id} |
connected, status, complete |
| Notifications | GET /api/notifications/stream |
Real-time user notifications |
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"
}
| Preset | Cron |
|---|---|
@hourly |
0 * * * * |
@daily |
0 0 * * * |
@weekly |
0 0 * * 0 |
@monthly |
0 0 1 * * |
| Custom | e.g., */15 * * * * |
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 workspaceGET /api/hub — Browse available agents| Table | Description | Key Fields |
|---|---|---|
user |
User accounts | email, password, role, credits, stripe_customer_id, OAuth IDs, mfa_enabled, mfa_required, sessions_valid_from |
integrations |
Connected OAuth services | user_id, provider, access_token, refresh_token, scopes, meta |
webhook_subscriptions |
Active webhook watches | user_id, integration_id, provider, subscription_id, resource, agent_id, status, expiration |
webhook_logs |
Webhook execution history | subscription_id, trigger_event, email_subject, agent_response, duration_ms, status |
notifications |
User notifications | user_id, title, message, type (webhook/info/warning/error), is_read |
transactions |
Credit movements | user_id, type (purchase/usage/refund/bonus), amount, balance_after, stripe IDs |
credit_purchases |
Stripe purchase sessions | user_id, stripe_session_id, credits_amount, price_paid, status |
llm_usage |
Per-call token accounting | agent_id, owner_id, invocation_id, model, token counts, created_at |
business_intelligence |
BI charts, dashboards, datasets | owner, definition, refresh state |
shared_conversations |
Shareable conversation links | share_id, session, owner |
oauth_states |
Short-lived OAuth state tokens | state, provider, expiry |
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/claude-sonnet-4-5-20250929, anthropic/claude-3-haiku-20240307openai/gpt-4o, openai/gpt-4, openai/gpt-3.5-turbomistral/mistral-large-latestgemini/gemini-proazure_ai/llama-3-3-70b-instructovhcloud/DeepSeek-R1-Distill-Llama-70BFor 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 .
| Code | Meaning |
|---|---|
400 |
Bad request — check your request body |
401 |
Unauthorized — missing or invalid token |
403 |
Forbidden — you don’t own this resource |
404 |
Not found — agent, session, or document doesn’t exist |
413 |
File too large — max 50 MB per file |
422 |
Unprocessable — e.g., query returned no data |
500 |
Internal server error |
503 |
Service temporarily unavailable |
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.