The Sales Agent is configured primarily through environment variables. This section documents every variable organized by category.
| Variable | Default | Description |
|---|---|---|
DATABASE_URL |
(required) | PostgreSQL connection string (e.g., postgresql://user:pass@host:5432/dbname) |
DATABASE_QUERY_TIMEOUT |
30 |
Maximum seconds for a single database query before timeout |
DATABASE_CONNECT_TIMEOUT |
10 |
Maximum seconds to wait for a database connection from the pool |
USE_PGBOUNCER |
false |
Set to true if connecting through PgBouncer; adjusts connection pooling behavior |
| Variable | Default | Description |
|---|---|---|
ADCP_SALES_PORT |
8080 |
Port the FastAPI application listens on (nginx proxies to this port) |
ADCP_SALES_HOST |
0.0.0.0 |
Host address the application binds to |
ENVIRONMENT |
development |
Environment name used for logging and configuration (e.g., development, staging, production) |
PRODUCTION |
false |
Set to true for production deployments; enables stricter security defaults |
| Variable | Default | Description |
|---|---|---|
ADCP_MULTI_TENANT |
false |
Enable multi-tenant mode with subdomain-based routing |
SALES_AGENT_DOMAIN |
(none) | Full domain for the Sales Agent (e.g., salesagent.example.com) |
BASE_DOMAIN |
(none) | Base domain for tenant subdomains (e.g., example.com; tenants get tenant1.example.com) |
| Variable | Default | Description |
|---|---|---|
ADCP_AUTH_TEST_MODE |
false |
Enable test authentication mode with pre-configured credentials. Never enable in production. |
CREATE_DEMO_TENANT |
false |
Automatically create a demo tenant with sample data on startup |
SUPER_ADMIN_EMAILS |
(none) | Comma-separated list of email addresses granted super admin access (e.g., admin@co.com,ops@co.com) |
SUPER_ADMIN_DOMAINS |
(none) | Comma-separated list of email domains granted super admin access (e.g., yourcompany.com) |
| Variable | Default | Description |
|---|---|---|
GAM_OAUTH_CLIENT_ID |
(none) | Google OAuth 2.0 client ID for Ad Manager API access |
GAM_OAUTH_CLIENT_SECRET |
(none) | Google OAuth 2.0 client secret for Ad Manager API access |
GCP_PROJECT_ID |
(none) | Google Cloud project ID associated with the Ad Manager API |
GOOGLE_APPLICATION_CREDENTIALS |
(none) | Path to the GCP service account JSON key file |
| Variable | Default | Description |
|---|---|---|
GEMINI_API_KEY |
(none) | Google Gemini API key for AI agents (naming, review, ranking, policy) |
LOGFIRE_TOKEN |
(none) | Logfire token for structured observability and tracing |
| Variable | Default | Description |
|---|---|---|
ENCRYPTION_KEY |
(none) | Fernet encryption key for encrypting sensitive configuration values in the database (e.g., adapter credentials). Generate with python -c "from cryptography.fernet import Fernet; print(Fernet.generate_key().decode())" |
FLASK_SECRET_KEY |
(none) | Secret key for Flask session signing in the Admin UI. Use a strong random string. |
WEBHOOK_SECRET |
(none) | Shared secret for verifying A2A push notification webhook signatures |
| Variable | Default | Description |
|---|---|---|
SKIP_MIGRATIONS |
false |
Skip Alembic database migrations on startup. Useful when running migrations separately. |
SKIP_NGINX |
false |
Skip starting the nginx reverse proxy. Use when nginx is managed externally. |
SKIP_CRON |
false |
Skip starting background cron jobs (e.g., delivery metric syncing) |
The default docker-compose.yml provides a complete development environment. Here is an annotated example:
version: "3.8"
services:
salesagent:
build: .
ports:
- "8000:8000" # nginx reverse proxy
environment:
# Database
DATABASE_URL: postgresql://salesagent:salesagent@postgres:5432/salesagent
# Server
ENVIRONMENT: development
ADCP_SALES_PORT: "8080"
ADCP_SALES_HOST: "0.0.0.0"
# Auth (test mode for development)
ADCP_AUTH_TEST_MODE: "true"
CREATE_DEMO_TENANT: "true"
SUPER_ADMIN_EMAILS: "test_super_admin@example.com"
# Security (development values — replace in production)
ENCRYPTION_KEY: "dev-encryption-key-replace-in-prod"
FLASK_SECRET_KEY: "dev-flask-secret-replace-in-prod"
# AI (optional)
# GEMINI_API_KEY: "your-gemini-key"
# Observability (optional)
# LOGFIRE_TOKEN: "your-logfire-token"
depends_on:
postgres:
condition: service_healthy
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8000/health"]
interval: 30s
timeout: 10s
retries: 3
postgres:
image: postgres:17
environment:
POSTGRES_USER: salesagent
POSTGRES_PASSWORD: salesagent
POSTGRES_DB: salesagent
volumes:
- pgdata:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U salesagent"]
interval: 10s
timeout: 5s
retries: 5
volumes:
pgdata:
For production, create a docker-compose.prod.yml override:
version: "3.8"
services:
salesagent:
environment:
ENVIRONMENT: production
PRODUCTION: "true"
ADCP_AUTH_TEST_MODE: "false"
CREATE_DEMO_TENANT: "false"
# Real credentials
SUPER_ADMIN_EMAILS: "admin@yourpublisher.com"
ENCRYPTION_KEY: "${ENCRYPTION_KEY}"
FLASK_SECRET_KEY: "${FLASK_SECRET_KEY}"
DATABASE_URL: "${DATABASE_URL}"
# Ad server (example: GAM)
GAM_OAUTH_CLIENT_ID: "${GAM_OAUTH_CLIENT_ID}"
GAM_OAUTH_CLIENT_SECRET: "${GAM_OAUTH_CLIENT_SECRET}"
GCP_PROJECT_ID: "${GCP_PROJECT_ID}"
GOOGLE_APPLICATION_CREDENTIALS: /secrets/gcp-sa.json
# AI
GEMINI_API_KEY: "${GEMINI_API_KEY}"
volumes:
- ./secrets/gcp-sa.json:/secrets/gcp-sa.json:ro
Run with:
docker compose -f docker-compose.yml -f docker-compose.prod.yml up -d
The Sales Agent includes an nginx reverse proxy that handles TLS termination, streaming support, and path-based routing to the FastAPI application.
nginx listens on port 8000 and proxies to the FastAPI app on port 8080:
upstream app {
server 127.0.0.1:8080;
}
server {
listen 8000;
# Streaming support — disable buffering for streaming responses
proxy_buffering off;
proxy_cache off;
proxy_set_header Connection '';
proxy_http_version 1.1;
chunked_transfer_encoding off;
# MCP endpoint (StreamableHTTP transport)
location /mcp/ {
proxy_pass http://app/mcp/;
proxy_read_timeout 86400s; # 24h for long-lived streaming connections
proxy_send_timeout 86400s;
}
# A2A endpoint
location /a2a {
proxy_pass http://app/a2a;
}
# Admin UI
location /admin {
proxy_pass http://app/admin;
}
# REST API
location /api/ {
proxy_pass http://app/api/;
}
# Health check
location /health {
proxy_pass http://app/health;
}
# Agent Card (A2A discovery)
location /.well-known/ {
proxy_pass http://app/.well-known/;
}
# Landing pages
location / {
proxy_pass http://app/;
}
}
For production deployments with SSL:
server {
listen 443 ssl http2;
server_name salesagent.yourpublisher.com;
ssl_certificate /etc/ssl/certs/salesagent.crt;
ssl_certificate_key /etc/ssl/private/salesagent.key;
# ... same proxy configuration as above ...
}
server {
listen 80;
server_name salesagent.yourpublisher.com;
return 301 https://$host$request_uri;
}
The MCP protocol uses StreamableHTTP for streaming. Key nginx settings for streaming responses:
proxy_buffering off — Disables response buffering so streaming events are forwarded immediately.proxy_cache off — Prevents caching of streaming responses.proxy_read_timeout 86400s — Allows long-lived streaming connections (24 hours).proxy_http_version 1.1 — Required for chunked transfer encoding.Connection '' — Prevents nginx from closing the connection prematurely.SKIP_NGINX=true) and use an external reverse proxy (e.g., Cloudflare, AWS ALB), ensure it supports streaming responses with the settings above. Many CDNs buffer responses by default, which breaks streaming.
Many settings are configurable per-tenant through the Admin UI, allowing each publisher to customize their Sales Agent independently.
Configured under Settings > Ad Server:
connection_config_class.Configured under Settings > Authentication:
Configured under Settings > AI:
gemini-2.0-flash, gpt-4o).Configured under Settings > Policies:
Configured under Settings > Naming:
{advertiser}_{product}_{date}).Configured under Settings > Measurement:
# Database
DATABASE_URL=postgresql://salesagent:salesagent@localhost:5432/salesagent
# Server
ENVIRONMENT=development
ADCP_SALES_PORT=8080
ADCP_SALES_HOST=0.0.0.0
# Auth (test mode)
ADCP_AUTH_TEST_MODE=true
CREATE_DEMO_TENANT=true
SUPER_ADMIN_EMAILS=test_super_admin@example.com
# Security (development values)
ENCRYPTION_KEY=dev-only-encryption-key-32-chars!!
FLASK_SECRET_KEY=dev-only-flask-secret-key
# AI (optional for development)
# GEMINI_API_KEY=your-dev-gemini-key
# Observability (optional)
# LOGFIRE_TOKEN=your-dev-logfire-token
# Database
DATABASE_URL=postgresql://salesagent:STRONG_PASSWORD@db.internal:5432/salesagent
DATABASE_QUERY_TIMEOUT=30
DATABASE_CONNECT_TIMEOUT=10
USE_PGBOUNCER=true
# Server
ENVIRONMENT=production
PRODUCTION=true
ADCP_SALES_PORT=8080
ADCP_SALES_HOST=0.0.0.0
# Multi-Tenant (optional)
# ADCP_MULTI_TENANT=true
# SALES_AGENT_DOMAIN=salesagent.yourpublisher.com
# BASE_DOMAIN=yourpublisher.com
# Auth
ADCP_AUTH_TEST_MODE=false
CREATE_DEMO_TENANT=false
SUPER_ADMIN_EMAILS=admin@yourpublisher.com,ops@yourpublisher.com
# Google Ad Manager (if using GAM adapter)
GAM_OAUTH_CLIENT_ID=your-production-client-id
GAM_OAUTH_CLIENT_SECRET=your-production-client-secret
GCP_PROJECT_ID=your-gcp-project
GOOGLE_APPLICATION_CREDENTIALS=/secrets/gcp-service-account.json
# AI
GEMINI_API_KEY=your-production-gemini-key
# Security (use strong random values)
ENCRYPTION_KEY=generate-with-fernet-generate-key
FLASK_SECRET_KEY=generate-with-python-secrets-module
WEBHOOK_SECRET=generate-with-python-secrets-module
# Observability
LOGFIRE_TOKEN=your-production-logfire-token
# Service Control
SKIP_MIGRATIONS=false
SKIP_NGINX=false
SKIP_CRON=false
.env files containing real credentials to version control. Use a secrets manager (e.g., Docker secrets, HashiCorp Vault, AWS Secrets Manager) for production deployments.