This guide covers setting up the Prebid Sales Agent in multi-tenant mode, where a single deployment hosts multiple publishers with subdomain-based routing.
Prerequisites: This guide assumes you have a working single-tenant deployment. See Single-Tenant Deployment for Docker images, environment variables, and basic setup.
Single-Tenant (Default): One publisher per deployment. Simple path-based routing (/admin, /mcp, /a2a). Most publishers should use this.
Multi-Tenant: Multiple publishers on one deployment. Subdomain-based routing (publisher1.yourdomain.com, publisher2.yourdomain.com). For platforms hosting multiple publishers.
Set the ADCP_MULTI_TENANT environment variable:
# Fly.io
fly secrets set ADCP_MULTI_TENANT=true --app your-app-name
# Docker
ADCP_MULTI_TENANT=true docker compose up -d
# Cloud Run
gcloud run services update salesagent \
--update-env-vars "ADCP_MULTI_TENANT=true"Set your base domain configuration:
# Your base domain
BASE_DOMAIN=yourdomain.com
# Where the sales agent is hosted
SALES_AGENT_DOMAIN=sales-agent.yourdomain.com
# Where the admin UI is accessible
ADMIN_DOMAIN=admin.sales-agent.yourdomain.com
# Domain for super admin emails (users from this domain get super admin access)
SUPER_ADMIN_DOMAIN=yourdomain.comSession Cookies in Multi-Tenant Mode: When
ADCP_MULTI_TENANT=true, session cookies are automatically scoped toSALES_AGENT_DOMAINto work across all tenant subdomains. This allows users to authenticate atadmin.sales-agent.yourdomain.comand access tenant dashboards attenant.sales-agent.yourdomain.com. In single-tenant mode (default), cookies use the actual request domain instead.
Point a wildcard DNS record to your deployment:
*.sales-agent.yourdomain.com → your-deployment-ip
For Fly.io:
fly ips list --app your-app-name
# Add A/AAAA records for the IPs shown- Fly.io: Automatic wildcard SSL
- Cloud Run: Use Cloud Load Balancer with managed certificates
- Docker: Use Caddy, nginx with certbot, or a reverse proxy with wildcard cert
Approximated is a proxy service that allows tenants to use their own custom domains (e.g., sales.publisher.com) instead of subdomains.
# Approximated API credentials
APPROXIMATED_API_KEY=your-approximated-api-key
# The backend URL Approximated will proxy to
APPROXIMATED_BACKEND_URL=sales-agent.yourdomain.com- Tenant sets their custom domain in Admin UI (Settings > Account > Virtual Host)
- System configures Approximated proxy for that domain
- Tenant adds a CNAME record:
sales.publisher.com → proxy.approximated.app - Requests to
sales.publisher.comare proxied to your deployment - The
Apx-Incoming-Hostheader identifies which tenant
- Go to Settings > Account
- Set Virtual Host to the tenant's custom domain (e.g.,
sales.publisher.com) - The system will show DNS instructions for the tenant
- Click Configure Custom Domain to set up the Approximated proxy
- Log in as a super admin at
https://admin.sales-agent.yourdomain.com - Click Create Tenant
- Enter:
- Name: Publisher display name
- Subdomain: e.g.,
acme→acme.sales-agent.yourdomain.com - Virtual Host (optional): Custom domain like
sales.acmepublisher.com
- Configure the ad server adapter (Mock or GAM)
# Docker
docker compose exec adcp-server python -m scripts.setup.setup_tenant \
"Acme Publisher" \
--subdomain acme \
--adapter mock
# Fly.io
fly ssh console -C "python -m scripts.setup.setup_tenant 'Acme Publisher' \
--subdomain acme \
--adapter mock"Each tenant using Google Ad Manager needs their own service account.
If you've configured GCP service account provisioning:
- Go to tenant Settings > Ad Server
- Select Google Ad Manager adapter
- Click Provision Service Account
- The system creates a GCP service account and shows the email
- Have the publisher add this email as a Trafficker in their GAM
See GAM Service Account Setup for details.
- Publisher creates their own GCP service account
- Publisher exports the JSON key file
- In Admin UI, paste the service account JSON in Settings > Ad Server
Before a tenant can create media buys, they need:
- Currency Limits: At least USD configured (Settings > Currencies)
- Property Tags: At least
all_inventorytag (Settings > Property Tags) - Products: At least one product configured (Products page)
- Advertisers: At least one advertiser/principal (Advertisers page)
Note: In multi-tenant mode, SSO is optional per-tenant. The platform manages authentication centrally, so individual tenants can skip SSO configuration. In single-tenant mode, SSO is required before accepting orders.
The Admin UI shows a setup checklist for each tenant.
In multi-tenant mode, the system routes requests based on:
- Host header:
acme.sales-agent.yourdomain.com→ tenantacme - Apx-Incoming-Host header: For Approximated proxy requests
- x-adcp-tenant header: Explicit tenant override (advanced)
Example MCP client configuration:
# Subdomain-based routing
transport = StreamableHttpTransport(
url="https://acme.sales-agent.yourdomain.com/mcp/",
headers={"x-adcp-auth": "advertiser-token"}
)
# Or with custom domain
transport = StreamableHttpTransport(
url="https://sales.acmepublisher.com/mcp/",
headers={"x-adcp-auth": "advertiser-token"}
)- Verify the subdomain/domain is configured for a tenant
- Check that the Host header is being passed correctly
- For Approximated: verify
Apx-Incoming-Hostheader is present
- Check DNS:
dig sales.publisher.comshould show Approximated IPs - Verify
APPROXIMATED_API_KEYis set - Check tenant's
virtual_hostfield is set correctly - Verify Approximated proxy is configured (Admin UI shows status)
Check the setup checklist in Admin UI:
- Currency limits configured?
- Property tags exist?
- Products configured?
- Adapter connected?
- Single-Tenant Deployment - Standard deployment guide
- GAM Service Account Setup - Per-tenant GAM configuration
- GCP Service Account Provisioning - Automatic service account creation