Multi-tenant mode allows a single Sales Agent deployment to serve multiple publishers, each isolated on their own subdomain. When ADCP_MULTI_TENANT=true, the application resolves the tenant from the request’s Host header and scopes all data access to that tenant.
This mode is designed for ad-tech platforms, managed service providers, or organizations that operate multiple publisher properties from a centralized infrastructure.
In multi-tenant mode, each tenant is assigned a unique subdomain under the configured BASE_DOMAIN. When a request arrives, the application extracts the subdomain from the Host header and resolves it to a tenant record in the database.
Request: https://acme-news.adcp.yourplatform.com/mcp
↓
Host header: acme-news.adcp.yourplatform.com
↓
Subdomain extracted: acme-news
↓
Tenant resolved: Acme News (tenant_id: abc-123)
↓
All queries scoped to tenant_id: abc-123
Tenants can also set a virtual_host field for fully custom domains (e.g., ads.acmenews.com), which the application resolves in addition to the subdomain.
| Variable | Value | Description |
|---|---|---|
ADCP_MULTI_TENANT |
true |
Enables subdomain-based tenant routing |
BASE_DOMAIN |
adcp.yourplatform.com |
Base domain for tenant subdomains |
SALES_AGENT_DOMAIN |
adcp.yourplatform.com |
Domain used in generated URLs and links |
SUPER_ADMIN_EMAILS |
admin@yourplatform.com |
Comma-separated list of super admin email addresses |
SUPER_ADMIN_DOMAINS |
yourplatform.com |
Comma-separated list of email domains granted super admin access |
Add these environment variables to your salesagent service:
salesagent:
environment:
ADCP_MULTI_TENANT: "true"
BASE_DOMAIN: "adcp.yourplatform.com"
SALES_AGENT_DOMAIN: "adcp.yourplatform.com"
SUPER_ADMIN_EMAILS: "admin@yourplatform.com"
SUPER_ADMIN_DOMAINS: "yourplatform.com"
Multi-tenant mode requires wildcard DNS so that any subdomain resolves to your server.
Create an A or CNAME record for *.adcp.yourplatform.com:
Type: A
Name: *.adcp.yourplatform.com
Value: <your-server-ip>
TTL: 300
Or with CNAME (if behind a load balancer):
Type: CNAME
Name: *.adcp.yourplatform.com
Value: lb.yourplatform.com
TTL: 300
For tenants that use a virtual_host (custom domain), they must create a CNAME pointing to your base domain:
Type: CNAME
Name: ads.acmenews.com
Value: adcp.yourplatform.com
TTL: 300
The nginx reverse proxy must accept traffic for all subdomains and forward the Host header so the application can resolve tenants.
events {
worker_connections 1024;
}
http {
upstream adcp_backend {
server salesagent:8080;
}
# Wildcard server block for all tenant subdomains
server {
listen 80;
server_name *.adcp.yourplatform.com;
location / {
proxy_pass http://adcp_backend;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
# SSE support for admin activity stream
location /admin/activity/stream {
proxy_pass http://adcp_backend;
proxy_set_header Host $host;
proxy_http_version 1.1;
proxy_set_header Connection "";
proxy_buffering off;
proxy_cache off;
}
}
}
For HTTPS, obtain a wildcard certificate for *.adcp.yourplatform.com:
server {
listen 443 ssl;
server_name *.adcp.yourplatform.com;
ssl_certificate /etc/ssl/certs/wildcard.adcp.yourplatform.com.pem;
ssl_certificate_key /etc/ssl/private/wildcard.adcp.yourplatform.com.key;
location / {
proxy_pass http://adcp_backend;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto https;
}
}
https://adcp.yourplatform.com/adminacme-news), becomes acme-news.adcp.yourplatform.comads.acmenews.com)curl -X POST https://adcp.yourplatform.com/api/v1/tenants \
-H "Authorization: Bearer <super-admin-token>" \
-H "Content-Type: application/json" \
-d '{
"name": "Acme News",
"subdomain": "acme-news",
"virtual_host": "ads.acmenews.com"
}'
Super admins have full platform access across all tenants. They can create and manage tenants, view all data, and configure platform-wide settings.
Super admin access is granted through environment variables:
| Variable | Description | Example |
|---|---|---|
SUPER_ADMIN_EMAILS |
Specific email addresses | alice@corp.com,bob@corp.com |
SUPER_ADMIN_DOMAINS |
Email domains (all users at domain) | corp.com,platform.io |
Any user whose email matches SUPER_ADMIN_EMAILS or whose email domain matches SUPER_ADMIN_DOMAINS is automatically granted super admin privileges when they authenticate.
SUPER_ADMIN_EMAILS or SUPER_ADMIN_DOMAINS require a service restart to take effect.
Each tenant can be independently configured through the Admin UI or API. The following settings are scoped per tenant:
Each tenant can connect to a different ad server (GAM, Kevel, Triton, Broadstreet, or Mock) with tenant-specific credentials.
Each tenant can configure its own OIDC provider for admin authentication. Supported providers:
| Provider | OIDC Discovery | Notes |
|---|---|---|
https://accounts.google.com/.well-known/openid-configuration |
Default option | |
| Microsoft / Entra ID | https://login.microsoftonline.com/{tenant}/v2.0/.well-known/openid-configuration |
Azure AD |
| Okta | https://{domain}.okta.com/.well-known/openid-configuration |
Enterprise SSO |
| Auth0 | https://{domain}.auth0.com/.well-known/openid-configuration |
Universal login |
| Keycloak | https://{host}/realms/{realm}/.well-known/openid-configuration |
Self-hosted |
Each tenant provides its own GEMINI_API_KEY through the Admin UI under Settings > AI. This key is encrypted at rest using the platform’s ENCRYPTION_KEY.
Per-tenant policies control:
Tenants can configure naming templates that control how entities (orders, line items, creatives) are named in the ad server. Templates support variable interpolation.
All tenants share a single PostgreSQL database. Tenant isolation is enforced at the application layer through composite primary keys and tenant-scoped queries – every query includes a WHERE tenant_id = :tenant_id clause.
For high-traffic deployments:
USE_PGBOUNCER=true)DATABASE_QUERY_TIMEOUT (default: 30s)The FastAPI application is stateless and can be scaled horizontally:
salesagent instances behind a load balancerENCRYPTION_KEY and DATABASE_URLThere is no hard limit on the number of tenants. Performance depends on:
Monitor the health endpoint and database metrics to determine when to scale.