Repository: prebid/prebid-server
Pull request #3174 (2023-10-03 08:13:59)

pubmatic: add fledge support

github-actions[bot]: ## Code coverage summary 
Note: 
- Prebid team doesn't anticipate tests covering code paths that might result in marshal and unmarshal errors 
- Coverage summary encompasses all commits leading up to the latest one, 68159b0ec6c4de5569d6197e8cff7428954d620c 
#### pubmatic 
Refer [here](https://htmlpreview.github.io/?https://github.com/prebid/prebid-server/coverage-preview/.github/preview/6390391854_1696320925/pubmatic.html) for heat map coverage report 
``` 
github.com/prebid/prebid-server/adapters/pubmatic/pubmatic.go:80:	MakeRequests				93.4%
github.com/prebid/prebid-server/adapters/pubmatic/pubmatic.go:184:	validateAdSlot				91.3%
github.com/prebid/prebid-server/adapters/pubmatic/pubmatic.go:227:	assignBannerSize			100.0%
github.com/prebid/prebid-server/adapters/pubmatic/pubmatic.go:235:	assignBannerWidthAndHeight		100.0%
github.com/prebid/prebid-server/adapters/pubmatic/pubmatic.go:243:	parseImpressionObject			91.3%
github.com/prebid/prebid-server/adapters/pubmatic/pubmatic.go:330:	extractPubmaticExtFromRequest		96.7%
github.com/prebid/prebid-server/adapters/pubmatic/pubmatic.go:382:	getAlternateBidderCodesFromRequestExt	100.0%
github.com/prebid/prebid-server/adapters/pubmatic/pubmatic.go:400:	addKeywordsToExt			85.7%
github.com/prebid/prebid-server/adapters/pubmatic/pubmatic.go:414:	MakeBids				90.7%
github.com/prebid/prebid-server/adapters/pubmatic/pubmatic.go:496:	getNativeAdm				90.9%
github.com/prebid/prebid-server/adapters/pubmatic/pubmatic.go:518:	getMapFromJSON				100.0%
github.com/prebid/prebid-server/adapters/pubmatic/pubmatic.go:530:	populateFirstPartyDataImpAttributes	100.0%
github.com/prebid/prebid-server/adapters/pubmatic/pubmatic.go:543:	populateAdUnitKey			100.0%
github.com/prebid/prebid-server/adapters/pubmatic/pubmatic.go:558:	populateDctrKey				85.0%
github.com/prebid/prebid-server/adapters/pubmatic/pubmatic.go:608:	getStringArray				100.0%
github.com/prebid/prebid-server/adapters/pubmatic/pubmatic.go:621:	getBidType				100.0%
github.com/prebid/prebid-server/adapters/pubmatic/pubmatic.go:641:	Builder					100.0%
total:									(statements)				93.1%

 ``` 


Patch:
diff --git a/account/account.go b/account/account.go
index 70527d0f..b4d0c334 100644
--- a/account/account.go
+++ b/account/account.go
@@ -50,18 +50,6 @@ func GetAccount(ctx context.Context, cfg *config.Configuration, fetcher stored_r
 				Message: fmt.Sprintf("The prebid-server account config for account id \"%s\" is malformed. Please reach out to the prebid server host.", accountID),
 			}}
 		}
-		usingGDPRChannelEnabled := useGDPRChannelEnabled(account)
-		usingCCPAChannelEnabled := useCCPAChannelEnabled(account)
-
-		if usingGDPRChannelEnabled {
-			me.RecordAccountGDPRChannelEnabledWarning(accountID)
-		}
-		if usingCCPAChannelEnabled {
-			me.RecordAccountCCPAChannelEnabledWarning(accountID)
-		}
-		if usingGDPRChannelEnabled || usingCCPAChannelEnabled {
-			me.RecordAccountUpgradeStatus(accountID)
-		}
 
 		if err != nil {
 			errs = append(errs, err)
@@ -154,11 +142,3 @@ func setDerivedConfig(account *config.Account) {
 		}
 	}
 }
-
-func useGDPRChannelEnabled(account *config.Account) bool {
-	return account.GDPR.ChannelEnabled.IsSet() && !account.GDPR.IntegrationEnabled.IsSet()
-}
-
-func useCCPAChannelEnabled(account *config.Account) bool {
-	return account.CCPA.ChannelEnabled.IsSet() && !account.CCPA.IntegrationEnabled.IsSet()
-}
diff --git a/adapters/pubmatic/pubmatic.go b/adapters/pubmatic/pubmatic.go
index 04bd00cb..bd406d68 100644
--- a/adapters/pubmatic/pubmatic.go
+++ b/adapters/pubmatic/pubmatic.go
@@ -19,6 +19,8 @@ import (
 
 const MAX_IMPRESSIONS_PUBMATIC = 30
 
+const ae = "ae"
+
 type PubmaticAdapter struct {
 	URI string
 }
@@ -42,6 +44,7 @@ type pubmaticBidExtVideo struct {
 type ExtImpBidderPubmatic struct {
 	adapters.ExtImpBidder
 	Data json.RawMessage `json:"data,omitempty"`
+	AE   int             `json:"ae,omitempty"`
 }
 
 type ExtAdServer struct {
@@ -60,6 +63,10 @@ type extRequestAdServer struct {
 	openrtb_ext.ExtRequest
 }
 
+type respExt struct {
+	FledgeAuctionConfigs map[string]json.RawMessage `json:"fledge_auction_configs,omitempty"`
+}
+
 const (
 	dctrKeyName        = "key_val"
 	pmZoneIDKeyName    = "pmZoneId"
@@ -304,6 +311,10 @@ func parseImpressionObject(imp *openrtb2.Imp, extractWrapperExtFromImp, extractP
 		populateFirstPartyDataImpAttributes(bidderExt.Data, extMap)
 	}
 
+	if bidderExt.AE != 0 {
+		extMap[ae] = bidderExt.AE
+	}
+
 	imp.Ext = nil
 	if len(extMap) > 0 {
 		ext, err := json.Marshal(extMap)
@@ -465,6 +476,20 @@ func (a *PubmaticAdapter) MakeBids(internalRequest *openrtb2.BidRequest, externa
 	if bidResp.Cur != "" {
 		bidResponse.Currency = bidResp.Cur
 	}
+
+	if bidResp.Ext != nil {
+		var bidRespExt respExt
+		if err := json.Unmarshal(bidResp.Ext, &bidRespExt); err == nil && bidRespExt.FledgeAuctionConfigs != nil {
+			bidResponse.FledgeAuctionConfigs = make([]*openrtb_ext.FledgeAuctionConfig, 0, len(bidRespExt.FledgeAuctionConfigs))
+			for impId, config := range bidRespExt.FledgeAuctionConfigs {
+				fledgeAuctionConfig := &openrtb_ext.FledgeAuctionConfig{
+					ImpId:  impId,
+					Config: config,
+				}
+				bidResponse.FledgeAuctionConfigs = append(bidResponse.FledgeAuctionConfigs, fledgeAuctionConfig)
+			}
+		}
+	}
 	return bidResponse, errs
 }
 
diff --git a/adapters/smartx/smartx.go b/adapters/smartx/smartx.go
new file mode 100644
index 00000000..6fbc9496
--- /dev/null
+++ b/adapters/smartx/smartx.go
@@ -0,0 +1,90 @@
+package smartx
+
+import (
+	"encoding/json"
+	"errors"
+	"fmt"
+	"net/http"
+
+	"github.com/prebid/openrtb/v19/openrtb2"
+	"github.com/prebid/prebid-server/adapters"
+	"github.com/prebid/prebid-server/config"
+	"github.com/prebid/prebid-server/openrtb_ext"
+)
+
+type adapter struct {
+	endpointURL string
+}
+
+func Builder(_ openrtb_ext.BidderName, config config.Adapter, _ config.Server) (adapters.Bidder, error) {
+	return &adapter{
+		endpointURL: config.Endpoint,
+	}, nil
+}
+
+func (a *adapter) MakeRequests(openRTBRequest *openrtb2.BidRequest, requestInfo *adapters.ExtraRequestInfo) (requestsToBidder []*adapters.RequestData, errs []error) {
+	openRTBRequestJSON, err := json.Marshal(openRTBRequest)
+	if err != nil {
+		errs = append(errs, fmt.Errorf("marshal bidRequest: %w", err))
+		return nil, errs
+	}
+
+	headers := http.Header{}
+	headers.Add("Content-Type", "application/json;charset=utf-8")
+	headers.Add("x-openrtb-version", "2.5")
+
+	if openRTBRequest.Device != nil {
+		if openRTBRequest.Device.UA != "" {
+			headers.Set("User-Agent", openRTBRequest.Device.UA)
+		}
+
+		if openRTBRequest.Device.IP != "" {
+			headers.Set("Forwarded", "for="+openRTBRequest.Device.IP)
+			headers.Set("X-Forwarded-For", openRTBRequest.Device.IP)
+		}
+	}
+
+	return append(requestsToBidder, &adapters.RequestData{
+		Method:  http.MethodPost,
+		Uri:     a.endpointURL,
+		Body:    openRTBRequestJSON,
+		Headers: headers,
+	}), nil
+}
+
+func (a *adapter) MakeBids(request *openrtb2.BidRequest, _ *adapters.RequestData, responseData *adapters.ResponseData) (*adapters.BidderResponse, []error) {
+	if adapters.IsResponseStatusCodeNoContent(responseData) {
+		return nil, nil
+	}
+
+	if err := adapters.CheckResponseStatusCodeForErrors(responseData); err != nil {
+		return nil, []error{err}
+	}
+
+	var response openrtb2.BidResponse
+	if err := json.Unmarshal(responseData.Body, &response); err != nil {
+		return nil, []error{err}
+	}
+
+	if len(response.SeatBid) == 0 {
+		return nil, []error{errors.New("no bidders found in JSON response")}
+	}
+
+	bidResponse := adapters.NewBidderResponseWithBidsCapacity(len(request.Imp))
+	if response.Cur != "" {
+		bidResponse.Currency = response.Cur
+	}
+
+	var errs []error
+
+	for _, seatBid := range response.SeatBid {
+		for i := range seatBid.Bid {
+			bidResponse.Bids = append(bidResponse.Bids, &adapters.TypedBid{
+				Bid:     &seatBid.Bid[i],
+				BidType: openrtb_ext.BidTypeVideo,
+			})
+		}
+	}
+
+	return bidResponse, errs
+}
diff --git a/analytics/config/config.go b/analytics/build/build.go
similarity index 55%
rename from analytics/config/config.go
rename to analytics/build/build.go
index 557fec36..6fb48705 100644
--- a/analytics/config/config.go
+++ b/analytics/build/build.go
@@ -1,4 +1,4 @@
-package config
+package build
 
 import (
 	"github.com/benbjohnson/clock"
@@ -8,14 +8,15 @@ import (
 	"github.com/prebid/prebid-server/analytics/filesystem"
 	"github.com/prebid/prebid-server/analytics/pubstack"
 	"github.com/prebid/prebid-server/config"
+	"github.com/prebid/prebid-server/privacy"
 )
 
 // Modules that need to be logged to need to be initialized here
-func NewPBSAnalytics(analytics *config.Analytics) analytics.PBSAnalyticsModule {
+func New(analytics *config.Analytics) analytics.Runner {
 	modules := make(enabledAnalytics, 0)
 	if len(analytics.File.Filename) > 0 {
 		if mod, err := filesystem.NewFileLogger(analytics.File.Filename); err == nil {
-			modules = append(modules, mod)
+			modules["filelogger"] = mod
 		} else {
 			glog.Fatalf("Could not initialize FileLogger for file %v :%v", analytics.File.Filename, err)
 		}
@@ -32,7 +33,7 @@ func NewPBSAnalytics(analytics *config.Analytics) analytics.PBSAnalyticsModule {
 			analytics.Pubstack.Buffers.Timeout,
 			clock.New())
 		if err == nil {
-			modules = append(modules, pubstackModule)
+			modules["pubstack"] = pubstackModule
 		} else {
 			glog.Errorf("Could not initialize PubstackModule: %v", err)
 		}
@@ -41,17 +42,23 @@ func NewPBSAnalytics(analytics *config.Analytics) analytics.PBSAnalyticsModule {
 }
 
 // Collection of all the correctly configured analytics modules - implements the PBSAnalyticsModule interface
-type enabledAnalytics []analytics.PBSAnalyticsModule
+type enabledAnalytics map[string]analytics.Module
 
-func (ea enabledAnalytics) LogAuctionObject(ao *analytics.AuctionObject) {
-	for _, module := range ea {
-		module.LogAuctionObject(ao)
+func (ea enabledAnalytics) LogAuctionObject(ao *analytics.AuctionObject, ac privacy.ActivityControl) {
+	for name, module := range ea {
+		component := privacy.Component{Type: privacy.ComponentTypeAnalytics, Name: name}
+		if ac.Allow(privacy.ActivityReportAnalytics, component, privacy.ActivityRequest{}) {
+			module.LogAuctionObject(ao)
+		}
 	}
 }
 
-func (ea enabledAnalytics) LogVideoObject(vo *analytics.VideoObject) {
-	for _, module := range ea {
-		module.LogVideoObject(vo)
+func (ea enabledAnalytics) LogVideoObject(vo *analytics.VideoObject, ac privacy.ActivityControl) {
+	for name, module := range ea {
+		component := privacy.Component{Type: privacy.ComponentTypeAnalytics, Name: name}
+		if ac.Allow(privacy.ActivityReportAnalytics, component, privacy.ActivityRequest{}) {
+			module.LogVideoObject(vo)
+		}
 	}
 }
 
@@ -67,14 +74,20 @@ func (ea enabledAnalytics) LogSetUIDObject(so *analytics.SetUIDObject) {
 	}
 }
 
-func (ea enabledAnalytics) LogAmpObject(ao *analytics.AmpObject) {
-	for _, module := range ea {
-		module.LogAmpObject(ao)
+func (ea enabledAnalytics) LogAmpObject(ao *analytics.AmpObject, ac privacy.ActivityControl) {
+	for name, module := range ea {
+		component := privacy.Component{Type: privacy.ComponentTypeAnalytics, Name: name}
+		if ac.Allow(privacy.ActivityReportAnalytics, component, privacy.ActivityRequest{}) {
+			module.LogAmpObject(ao)
+		}
 	}
 }
 
-func (ea enabledAnalytics) LogNotificationEventObject(ne *analytics.NotificationEvent) {
-	for _, module := range ea {
-		module.LogNotificationEventObject(ne)
+func (ea enabledAnalytics) LogNotificationEventObject(ne *analytics.NotificationEvent, ac privacy.ActivityControl) {
+	for name, module := range ea {
+		component := privacy.Component{Type: privacy.ComponentTypeAnalytics, Name: name}
+		if ac.Allow(privacy.ActivityReportAnalytics, component, privacy.ActivityRequest{}) {
+			module.LogNotificationEventObject(ne)
+		}
 	}
 }
diff --git a/analytics/core.go b/analytics/core.go
index eca93741..0279ae83 100644
--- a/analytics/core.go
+++ b/analytics/core.go
@@ -9,10 +9,10 @@ import (
 	"github.com/prebid/prebid-server/openrtb_ext"
 )
 
-// PBSAnalyticsModule must be implemented by analytics modules to extract the required information and logging
+// Module must be implemented by analytics modules to extract the required information and logging
 // activities. Do not use marshal the parameter objects directly as they can change over time. Use a separate
 // model for each analytics module and transform as appropriate.
-type PBSAnalyticsModule interface {
+type Module interface {
 	LogAuctionObject(*AuctionObject)
 	LogVideoObject(*VideoObject)
 	LogCookieSyncObject(*CookieSyncObject)
diff --git a/analytics/filesystem/file_module.go b/analytics/filesystem/file_module.go
index 9a357529..f055e447 100644
--- a/analytics/filesystem/file_module.go
+++ b/analytics/filesystem/file_module.go
@@ -86,7 +86,7 @@ func (f *FileLogger) LogNotificationEventObject(ne *analytics.NotificationEvent)
 }
 
 // Method to initialize the analytic module
-func NewFileLogger(filename string) (analytics.PBSAnalyticsModule, error) {
+func NewFileLogger(filename string) (analytics.Module, error) {
 	options := glog.LogOptions{
 		File:  filename,
 		Flag:  glog.LstdFlags,
diff --git a/analytics/pubstack/pubstack_module.go b/analytics/pubstack/pubstack_module.go
index 987c935f..f2d8726d 100644
--- a/analytics/pubstack/pubstack_module.go
+++ b/analytics/pubstack/pubstack_module.go
@@ -50,7 +50,7 @@ type PubstackModule struct {
 	clock         clock.Clock
 }
 
-func NewModule(client *http.Client, scope, endpoint, configRefreshDelay string, maxEventCount int, maxByteSize, maxTime string, clock clock.Clock) (analytics.PBSAnalyticsModule, error) {
+func NewModule(client *http.Client, scope, endpoint, configRefreshDelay string, maxEventCount int, maxByteSize, maxTime string, clock clock.Clock) (analytics.Module, error) {
 	configUpdateTask, err := NewConfigUpdateHttpTask(
 		client,
 		scope,
@@ -63,7 +63,7 @@ func NewModule(client *http.Client, scope, endpoint, configRefreshDelay string,
 	return NewModuleWithConfigTask(client, scope, endpoint, maxEventCount, maxByteSize, maxTime, configUpdateTask, clock)
 }
 
-func NewModuleWithConfigTask(client *http.Client, scope, endpoint string, maxEventCount int, maxByteSize, maxTime string, configTask ConfigUpdateTask, clock clock.Clock) (analytics.PBSAnalyticsModule, error) {
+func NewModuleWithConfigTask(client *http.Client, scope, endpoint string, maxEventCount int, maxByteSize, maxTime string, configTask ConfigUpdateTask, clock clock.Clock) (analytics.Module, error) {
 	glog.Infof("[pubstack] Initializing module scope=%s endpoint=%s\n", scope, endpoint)
 
 	// parse args
diff --git a/analytics/runner.go b/analytics/runner.go
new file mode 100644
index 00000000..08f67561
--- /dev/null
+++ b/analytics/runner.go
@@ -0,0 +1,14 @@
+package analytics
+
+import (
+	"github.com/prebid/prebid-server/privacy"
+)
+
+type Runner interface {
+	LogAuctionObject(*AuctionObject, privacy.ActivityControl)
+	LogVideoObject(*VideoObject, privacy.ActivityControl)
+	LogCookieSyncObject(*CookieSyncObject)
+	LogSetUIDObject(*SetUIDObject)
+	LogAmpObject(*AmpObject, privacy.ActivityControl)
+	LogNotificationEventObject(*NotificationEvent, privacy.ActivityControl)
+}
diff --git a/config/account.go b/config/account.go
index d57fdb90..92af6c8a 100644
--- a/config/account.go
+++ b/config/account.go
@@ -53,9 +53,8 @@ type CookieSync struct {
 
 // AccountCCPA represents account-specific CCPA configuration
 type AccountCCPA struct {
-	Enabled            *bool          `mapstructure:"enabled" json:"enabled,omitempty"`
-	IntegrationEnabled AccountChannel `mapstructure:"integration_enabled" json:"integration_enabled"`
-	ChannelEnabled     AccountChannel `mapstructure:"channel_enabled" json:"channel_enabled"`
+	Enabled        *bool          `mapstructure:"enabled" json:"enabled,omitempty"`
+	ChannelEnabled AccountChannel `mapstructure:"channel_enabled" json:"channel_enabled"`
 }
 
 type AccountPriceFloors struct {
@@ -94,17 +93,14 @@ func (pf *AccountPriceFloors) IsAdjustForBidAdjustmentEnabled() bool {
 func (a *AccountCCPA) EnabledForChannelType(channelType ChannelType) *bool {
 	if channelEnabled := a.ChannelEnabled.GetByChannelType(channelType); channelEnabled != nil {
 		return channelEnabled
-	} else if integrationEnabled := a.IntegrationEnabled.GetByChannelType(channelType); integrationEnabled != nil {
-		return integrationEnabled
 	}
 	return a.Enabled
 }
 
 // AccountGDPR represents account-specific GDPR configuration
 type AccountGDPR struct {
-	Enabled            *bool          `mapstructure:"enabled" json:"enabled,omitempty"`
-	IntegrationEnabled AccountChannel `mapstructure:"integration_enabled" json:"integration_enabled"`
-	ChannelEnabled     AccountChannel `mapstructure:"channel_enabled" json:"channel_enabled"`
+	Enabled        *bool          `mapstructure:"enabled" json:"enabled,omitempty"`
+	ChannelEnabled AccountChannel `mapstructure:"channel_enabled" json:"channel_enabled"`
 	// Array of basic enforcement vendors that is used to create the hash table so vendor names can be instantly accessed
 	BasicEnforcementVendors    []string `mapstructure:"basic_enforcement_vendors" json:"basic_enforcement_vendors"`
 	BasicEnforcementVendorsMap map[string]struct{}
@@ -129,8 +125,6 @@ type AccountGDPR struct {
 func (a *AccountGDPR) EnabledForChannelType(channelType ChannelType) *bool {
 	if channelEnabled := a.ChannelEnabled.GetByChannelType(channelType); channelEnabled != nil {
 		return channelEnabled
-	} else if integrationEnabled := a.IntegrationEnabled.GetByChannelType(channelType); integrationEnabled != nil {
-		return integrationEnabled
 	}
 	return a.Enabled
 }
@@ -298,10 +292,6 @@ func (m AccountModules) ModuleConfig(id string) (json.RawMessage, error) {
 	return m[vendor][module], nil
 }
 
-func (a *AccountChannel) IsSet() bool {
-	return a.AMP != nil || a.App != nil || a.Video != nil || a.Web != nil || a.DOOH != nil
-}
-
 type AccountPrivacy struct {
 	AllowActivities *AllowActivities `mapstructure:"allowactivities" json:"allowactivities"`
 	IPv6Config      IPv6             `mapstructure:"ipv6" json:"ipv6"`
diff --git a/config/bidderinfo.go b/config/bidderinfo.go
index 3b3f86d4..f1f437c5 100644
--- a/config/bidderinfo.go
+++ b/config/bidderinfo.go
@@ -9,10 +9,8 @@ import (
 	"strings"
 	"text/template"
 
-	"github.com/golang/glog"
 	"github.com/prebid/prebid-server/macros"
 	"github.com/prebid/prebid-server/openrtb_ext"
-	"github.com/prebid/prebid-server/util/sliceutil"
 
 	validator "github.com/asaskevich/govalidator"
 	"gopkg.in/yaml.v3"
@@ -38,9 +36,6 @@ type BidderInfo struct {
 
 	Experiment BidderInfoExperiment `yaml:"experiment" mapstructure:"experiment"`
 
-	// needed for backwards compatibility
-	UserSyncURL string `yaml:"usersync_url" mapstructure:"usersync_url"`
-
 	// needed for Rubicon
 	XAPI AdapterXAPI `yaml:"xapi" mapstructure:"xapi"`
 
@@ -329,9 +324,6 @@ func processBidderAliases(aliasNillableFieldsByBidder map[string]aliasNillableFi
 			syncer := Syncer{Key: syncerKey}
 			aliasBidderInfo.Syncer = &syncer
 		}
-		if aliasBidderInfo.UserSyncURL == "" {
-			aliasBidderInfo.UserSyncURL = parentBidderInfo.UserSyncURL
-		}
 		if alias.Disabled == nil {
 			aliasBidderInfo.Disabled = parentBidderInfo.Disabled
 		}
@@ -614,51 +606,6 @@ func applyBidderInfoConfigOverrides(configBidderInfos BidderInfos, fsBidderInfos
 				bidderInfo.EndpointCompression = fsBidderCfg.EndpointCompression
 			}
 
-			// validate and try to apply the legacy usersync_url configuration in attempt to provide
-			// an easier upgrade path. be warned, this will break if the bidder adds a second syncer
-			// type and will eventually be removed after we've given hosts enough time to upgrade to
-			// the new config.
-			if bidderInfo.UserSyncURL != "" {
-				if fsBidderCfg.Syncer == nil {
-					return nil, fmt.Errorf("adapters.%s.usersync_url cannot be applied, bidder does not define a user sync", strings.ToLower(bidderName))
-				}
-
-				endpointsCount := 0
-				if bidderInfo.Syncer.IFrame != nil {
-					bidderInfo.Syncer.IFrame.URL = bidderInfo.UserSyncURL
-					endpointsCount++
-				}
-				if bidderInfo.Syncer.Redirect != nil {
-					bidderInfo.Syncer.Redirect.URL = bidderInfo.UserSyncURL
-					endpointsCount++
-				}
-
-				// use Supports as a hint if there are no good defaults provided
-				if endpointsCount == 0 {
-					if sliceutil.ContainsStringIgnoreCase(bidderInfo.Syncer.Supports, "iframe") {
-						bidderInfo.Syncer.IFrame = &SyncerEndpoint{URL: bidderInfo.UserSyncURL}
-						endpointsCount++
-					}
-					if sliceutil.ContainsStringIgnoreCase(bidderInfo.Syncer.Supports, "redirect") {
-						bidderInfo.Syncer.Redirect = &SyncerEndpoint{URL: bidderInfo.UserSyncURL}
-						endpointsCount++
-					}
-				}
-
-				if endpointsCount == 0 {
-					return nil, fmt.Errorf("adapters.%s.usersync_url cannot be applied, bidder does not define user sync endpoints and does not define supported endpoints", strings.ToLower(bidderName))
-				}
-
-				// if the bidder defines both an iframe and redirect endpoint, we can't be sure which config value to
-				// override, and  it wouldn't be both. this is a fatal configuration error.
-				if endpointsCount > 1 {
-					return nil, fmt.Errorf("adapters.%s.usersync_url cannot be applied, bidder defines multiple user sync endpoints or supports multiple endpoints", strings.ToLower(bidderName))
-				}
-
-				// provide a warning that this compatibility layer is temporary
-				glog.Warningf("adapters.%s.usersync_url is deprecated and will be removed in a future version, please update to the latest user sync config values", strings.ToLower(bidderName))
-			}
-
 			fsBidderInfos[string(normalizedBidderName)] = bidderInfo
 		} else {
 			return nil, fmt.Errorf("error finding configuration for bidder %s: unknown bidder", bidderName)
diff --git a/endpoints/cookie_sync.go b/endpoints/cookie_sync.go
index bbee3847..24530efa 100644
--- a/endpoints/cookie_sync.go
+++ b/endpoints/cookie_sync.go
@@ -50,7 +50,7 @@ func NewCookieSyncEndpoint(
 	gdprPermsBuilder gdpr.PermissionsBuilder,
 	tcf2CfgBuilder gdpr.TCF2ConfigBuilder,
 	metrics metrics.MetricsEngine,
-	pbsAnalytics analytics.PBSAnalyticsModule,
+	analyticsRunner analytics.Runner,
 	accountsFetcher stored_requests.AccountFetcher,
 	bidders map[string]openrtb_ext.BidderName) HTTPRouterHandler {
 
@@ -70,7 +70,7 @@ func NewCookieSyncEndpoint(
 			bidderHashSet:          bidderHashSet,
 		},
 		metrics:         metrics,
-		pbsAnalytics:    pbsAnalytics,
+		pbsAnalytics:    analyticsRunner,
 		accountsFetcher: accountsFetcher,
 	}
 }
@@ -80,7 +80,7 @@ type cookieSyncEndpoint struct {
 	config          *config.Configuration
 	privacyConfig   usersyncPrivacyConfig
 	metrics         metrics.MetricsEngine
-	pbsAnalytics    analytics.PBSAnalyticsModule
+	pbsAnalytics    analytics.Runner
 	accountsFetcher stored_requests.AccountFetcher
 }
 
diff --git a/endpoints/events/event.go b/endpoints/events/event.go
index 7f923eda..d5cf89b2 100644
--- a/endpoints/events/event.go
+++ b/endpoints/events/event.go
@@ -16,6 +16,7 @@ import (
 	"github.com/prebid/prebid-server/config"
 	"github.com/prebid/prebid-server/errortypes"
 	"github.com/prebid/prebid-server/metrics"
+	"github.com/prebid/prebid-server/privacy"
 	"github.com/prebid/prebid-server/stored_requests"
 	"github.com/prebid/prebid-server/util/httputil"
 )
@@ -40,13 +41,13 @@ const integrationParamMaxLength = 64
 
 type eventEndpoint struct {
 	Accounts      stored_requests.AccountFetcher
-	Analytics     analytics.PBSAnalyticsModule
+	Analytics     analytics.Runner
 	Cfg           *config.Configuration
 	TrackingPixel *httputil.Pixel
 	MetricsEngine metrics.MetricsEngine
 }
 
-func NewEventEndpoint(cfg *config.Configuration, accounts stored_requests.AccountFetcher, analytics analytics.PBSAnalyticsModule, me metrics.MetricsEngine) httprouter.Handle {
+func NewEventEndpoint(cfg *config.Configuration, accounts stored_requests.AccountFetcher, analytics analytics.Runner, me metrics.MetricsEngine) httprouter.Handle {
 	ee := &eventEndpoint{
 		Accounts:      accounts,
 		Analytics:     analytics,
@@ -114,11 +115,13 @@ func (e *eventEndpoint) Handle(w http.ResponseWriter, r *http.Request, _ httprou
 		return
 	}
 
+	activities := privacy.NewActivityControl(&account.Privacy)
+
 	// handle notification event
 	e.Analytics.LogNotificationEventObject(&analytics.NotificationEvent{
 		Request: eventRequest,
 		Account: account,
-	})
+	}, activities)
 
 	// Add tracking pixel if format == image
 	if eventRequest.Format == analytics.Image {
diff --git a/endpoints/openrtb2/amp_auction.go b/endpoints/openrtb2/amp_auction.go
index aa80d075..2fde59c2 100644
--- a/endpoints/openrtb2/amp_auction.go
+++ b/endpoints/openrtb2/amp_auction.go
@@ -11,8 +11,6 @@ import (
 	"strings"
 	"time"
 
-	"github.com/prebid/prebid-server/privacy"
-
 	"github.com/buger/jsonparser"
 	"github.com/golang/glog"
 	"github.com/julienschmidt/httprouter"
@@ -33,6 +31,7 @@ import (
 	"github.com/prebid/prebid-server/hooks"
 	"github.com/prebid/prebid-server/metrics"
 	"github.com/prebid/prebid-server/openrtb_ext"
+	"github.com/prebid/prebid-server/privacy"
 	"github.com/prebid/prebid-server/stored_requests"
 	"github.com/prebid/prebid-server/stored_requests/backends/empty_fetcher"
 	"github.com/prebid/prebid-server/stored_responses"
@@ -64,7 +63,7 @@ func NewAmpEndpoint(
 	accounts stored_requests.AccountFetcher,
 	cfg *config.Configuration,
 	metricsEngine metrics.MetricsEngine,
-	pbsAnalytics analytics.PBSAnalyticsModule,
+	analyticsRunner analytics.Runner,
 	disabledBidders map[string]string,
 	defReqJSON []byte,
 	bidderMap map[string]openrtb_ext.BidderName,
@@ -93,7 +92,7 @@ func NewAmpEndpoint(
 		accounts,
 		cfg,
 		metricsEngine,
-		pbsAnalytics,
+		analyticsRunner,
 		disabledBidders,
 		defRequest,
 		defReqJSON,
@@ -104,6 +103,7 @@ func NewAmpEndpoint(
 		storedRespFetcher,
 		hookExecutionPlanBuilder,
 		tmaxAdjustments,
+		openrtb_ext.NormalizeBidderName,
 	}).AmpAuction), nil
 
 }
@@ -134,11 +134,12 @@ func (deps *endpointDeps) AmpAuction(w http.ResponseWriter, r *http.Request, _ h
 		CookieFlag:    metrics.CookieFlagUnknown,
 		RequestStatus: metrics.RequestStatusOK,
 	}
+	activityControl := privacy.ActivityControl{}
 
 	defer func() {
 		deps.metricsEngine.RecordRequest(labels)
 		deps.metricsEngine.RecordRequestTime(labels, time.Since(start))
-		deps.analytics.LogAmpObject(&ao)
+		deps.analytics.LogAmpObject(&ao, activityControl)
 	}()
 
 	// Add AMP headers
@@ -230,7 +231,7 @@ func (deps *endpointDeps) AmpAuction(w http.ResponseWriter, r *http.Request, _ h
 
 	tcf2Config := gdpr.NewTCF2Config(deps.cfg.GDPR.TCF2, account.GDPR)
 
-	activityControl := privacy.NewActivityControl(&account.Privacy)
+	activityControl = privacy.NewActivityControl(&account.Privacy)
 
 	secGPC := r.Header.Get("Sec-GPC")
 
diff --git a/endpoints/openrtb2/auction.go b/endpoints/openrtb2/auction.go
index b625c0ff..3a929c43 100644
--- a/endpoints/openrtb2/auction.go
+++ b/endpoints/openrtb2/auction.go
@@ -14,8 +14,6 @@ import (
 	"strings"
 	"time"
 
-	"github.com/prebid/prebid-server/privacy"
-
 	"github.com/buger/jsonparser"
 	"github.com/gofrs/uuid"
 	"github.com/golang/glog"
@@ -30,6 +28,7 @@ import (
 	"github.com/prebid/prebid-server/bidadjustment"
 	"github.com/prebid/prebid-server/hooks"
 	"github.com/prebid/prebid-server/ortb"
+	"github.com/prebid/prebid-server/privacy"
 	"golang.org/x/net/publicsuffix"
 	jsonpatch "gopkg.in/evanphx/json-patch.v4"
 
@@ -89,7 +88,7 @@ func NewEndpoint(
 	accounts stored_requests.AccountFetcher,
 	cfg *config.Configuration,
 	metricsEngine metrics.MetricsEngine,
-	pbsAnalytics analytics.PBSAnalyticsModule,
+	analyticsRunner analytics.Runner,
 	disabledBidders map[string]string,
 	defReqJSON []byte,
 	bidderMap map[string]openrtb_ext.BidderName,
@@ -117,7 +116,7 @@ func NewEndpoint(
 		accounts,
 		cfg,
 		metricsEngine,
-		pbsAnalytics,
+		analyticsRunner,
 		disabledBidders,
 		defRequest,
 		defReqJSON,
@@ -127,9 +126,12 @@ func NewEndpoint(
 		ipValidator,
 		storedRespFetcher,
 		hookExecutionPlanBuilder,
-		tmaxAdjustments}).Auction), nil
+		tmaxAdjustments,
+		openrtb_ext.NormalizeBidderName}).Auction), nil
 }
 
+type normalizeBidderName func(name string) (openrtb_ext.BidderName, bool)
+
 type endpointDeps struct {
 	uuidGenerator             uuidutil.UUIDGenerator
 	ex                        exchange.Exchange
@@ -139,7 +141,7 @@ type endpointDeps struct {
 	accounts                  stored_requests.AccountFetcher
 	cfg                       *config.Configuration
 	metricsEngine             metrics.MetricsEngine
-	analytics                 analytics.PBSAnalyticsModule
+	analytics                 analytics.Runner
 	disabledBidders           map[string]string
 	defaultRequest            bool
 	defReqJSON                []byte
@@ -150,6 +152,7 @@ type endpointDeps struct {
 	storedRespFetcher         stored_requests.Fetcher
 	hookExecutionPlanBuilder  hooks.ExecutionPlanBuilder
 	tmaxAdjustments           *exchange.TmaxAdjustmentsPreprocessed
+	normalizeBidderName       normalizeBidderName
 }
 
 func (deps *endpointDeps) Auction(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
@@ -176,10 +179,12 @@ func (deps *endpointDeps) Auction(w http.ResponseWriter, r *http.Request, _ http
 		CookieFlag:    metrics.CookieFlagUnknown,
 		RequestStatus: metrics.RequestStatusOK,
 	}
+
+	activityControl := privacy.ActivityControl{}
 	defer func() {
 		deps.metricsEngine.RecordRequest(labels)
 		deps.metricsEngine.RecordRequestTime(labels, time.Since(start))
-		deps.analytics.LogAuctionObject(&ao)
+		deps.analytics.LogAuctionObject(&ao, activityControl)
 	}()
 
 	w.Header().Set("X-Prebid", version.BuildXPrebidHeader(version.Ver))
@@ -197,7 +202,7 @@ func (deps *endpointDeps) Auction(w http.ResponseWriter, r *http.Request, _ http
 
 	tcf2Config := gdpr.NewTCF2Config(deps.cfg.GDPR.TCF2, account.GDPR)
 
-	activityControl := privacy.NewActivityControl(&account.Privacy)
+	activityControl = privacy.NewActivityControl(&account.Privacy)
 
 	ctx := context.Background()
 
@@ -764,7 +769,7 @@ func (deps *endpointDeps) validateRequest(req *openrtb_ext.RequestWrapper, isAmp
 		}
 	}
 
-	var aliases map[string]string
+	var requestAliases map[string]string
 	reqExt, err := req.GetRequestExt()
 	if err != nil {
 		return []error{fmt.Errorf("request.ext is invalid: %v", err)}
@@ -776,17 +781,17 @@ func (deps *endpointDeps) validateRequest(req *openrtb_ext.RequestWrapper, isAmp
 	}
 
 	if reqPrebid != nil {
-		aliases = reqPrebid.Aliases
+		requestAliases = reqPrebid.Aliases
 
-		if err := deps.validateAliases(aliases); err != nil {
+		if err := deps.validateAliases(requestAliases); err != nil {
 			return []error{err}
 		}
 
-		if err := deps.validateAliasesGVLIDs(reqPrebid.AliasGVLIDs, aliases); err != nil {
+		if err := deps.validateAliasesGVLIDs(reqPrebid.AliasGVLIDs, requestAliases); err != nil {
 			return []error{err}
 		}
 
-		if err := deps.validateBidAdjustmentFactors(reqPrebid.BidAdjustmentFactors, aliases); err != nil {
+		if err := deps.validateBidAdjustmentFactors(reqPrebid.BidAdjustmentFactors, requestAliases); err != nil {
 			return []error{err}
 		}
 
@@ -794,7 +799,7 @@ func (deps *endpointDeps) validateRequest(req *openrtb_ext.RequestWrapper, isAmp
 			return []error{err}
 		}
 
-		if err := deps.validateEidPermissions(reqPrebid.Data, aliases); err != nil {
+		if err := deps.validateEidPermissions(reqPrebid.Data, requestAliases); err != nil {
 			return []error{err}
 		}
 
@@ -843,7 +848,7 @@ func (deps *endpointDeps) validateRequest(req *openrtb_ext.RequestWrapper, isAmp
 		}
 	}
 
-	if errs := deps.validateUser(req, aliases, gpp); errs != nil {
+	if errs := deps.validateUser(req, requestAliases, gpp); errs != nil {
 		if len(errs) > 0 {
 			errL = append(errL, errs...)
 		}
@@ -870,7 +875,7 @@ func (deps *endpointDeps) validateRequest(req *openrtb_ext.RequestWrapper, isAmp
 		if errortypes.ContainsFatalError([]error{err}) {
 			return errL
 		}
-	} else if _, err := ccpaPolicy.Parse(exchange.GetValidBidders(aliases)); err != nil {
+	} else if _, err := ccpaPolicy.Parse(exchange.GetValidBidders(requestAliases)); err != nil {
 		if _, invalidConsent := err.(*errortypes.Warning); invalidConsent {
 			errL = append(errL, &errortypes.Warning{
 				Message:     fmt.Sprintf("CCPA consent is invalid and will be ignored. (%v)", err),
@@ -893,7 +898,7 @@ func (deps *endpointDeps) validateRequest(req *openrtb_ext.RequestWrapper, isAmp
 		}
 		impIDs[imp.ID] = i
 
-		errs := deps.validateImp(imp, aliases, i, hasStoredResponses, storedBidResp)
+		errs := deps.validateImp(imp, requestAliases, i, hasStoredResponses, storedBidResp)
 		if len(errs) > 0 {
 			errL = append(errL, errs...)
 		}
@@ -985,7 +990,7 @@ func validateSChains(sChains []*openrtb_ext.ExtRequestPrebidSChain) error {
 	return err
 }
 
-func (deps *endpointDeps) validateEidPermissions(prebid *openrtb_ext.ExtRequestPrebidData, aliases map[string]string) error {
+func (deps *endpointDeps) validateEidPermissions(prebid *openrtb_ext.ExtRequestPrebidData, requestAliases map[string]string) error {
 	if prebid == nil {
 		return nil
 	}
@@ -1005,7 +1010,7 @@ func (deps *endpointDeps) validateEidPermissions(prebid *openrtb_ext.ExtRequestP
 			return fmt.Errorf(`request.ext.prebid.data.eidpermissions[%d] missing or empty required field: "bidders"`, i)
 		}
 
-		if err := validateBidders(eid.Bidders, deps.bidderMap, aliases); err != nil {
+		if err := deps.validateBidders(eid.Bidders, deps.bidderMap, requestAliases); err != nil {
 			return fmt.Errorf(`request.ext.prebid.data.eidpermissions[%d] contains %v`, i, err)
 		}
 	}
@@ -1013,20 +1018,16 @@ func (deps *endpointDeps) validateEidPermissions(prebid *openrtb_ext.ExtRequestP
 	return nil
 }
 
-func validateBidders(bidders []string, knownBidders map[string]openrtb_ext.BidderName, knownAliases map[string]string) error {
+func (deps *endpointDeps) validateBidders(bidders []string, knownBidders map[string]openrtb_ext.BidderName, knownRequestAliases map[string]string) error {
 	for _, bidder := range bidders {
 		if bidder == "*" {
 			if len(bidders) > 1 {
 				return errors.New(`bidder wildcard "*" mixed with specific bidders`)
 			}
 		} else {
-			bidderName := bidder
-			normalizedCoreBidder, ok := openrtb_ext.NormalizeBidderName(bidderName)
-			if ok {
-				bidderName = normalizedCoreBidder.String()
-			}
-			_, isCoreBidder := knownBidders[bidderName]
-			_, isAlias := knownAliases[bidder]
+			bidderNormalized, _ := deps.normalizeBidderName(bidder)
+			_, isCoreBidder := knownBidders[bidderNormalized.String()]
+			_, isAlias := knownRequestAliases[bidder]
 			if !isCoreBidder && !isAlias {
 				return fmt.Errorf(`unrecognized bidder "%v"`, bidder)
 			}
diff --git a/endpoints/openrtb2/sample-requests/valid-whole/exemplary/eidpermissions-insensitive-bidder-name.json b/endpoints/openrtb2/sample-requests/valid-whole/exemplary/eidpermissions-insensitive-bidder-name.json
deleted file mode 100644
index b06b2593..00000000
--- a/endpoints/openrtb2/sample-requests/valid-whole/exemplary/eidpermissions-insensitive-bidder-name.json
+++ /dev/null
@@ -1,109 +0,0 @@
-{
-    "description": "This demonstrates ext.prebid.data.eidpermissions.bidders with case insensitive bidder names",
-    "config": {
-        "mockBidders": [
-            {
-                "bidderName": "appnexus",
-                "currency": "USD",
-                "price": 1.00
-            },
-            {
-                "bidderName": "rubicon",
-                "currency": "USD",
-                "price": 1.00
-            }
-        ]
-    },
-    "mockBidRequest": {
-        "id": "some-request-id",
-        "site": {
-            "page": "prebid.org"
-        },
-        "user": {
-            "ext": {
-                "consent": "gdpr-consent-string"
-            }
-        },
-        "regs": {
-            "ext": {
-                "gdpr": 1,
-                "us_privacy": "1NYN"
-            }
-        },
-        "imp": [
-            {
-                "id": "some-impression-id",
-                "banner": {
-                    "format": [
-                        {
-                            "w": 300,
-                            "h": 250
-                        },
-                        {
-                            "w": 300,
-                            "h": 600
-                        }
-                    ]
-                },
-                "ext": {
-                    "APPNEXUS": {
-                        "placementId": 12883451
-                    }
-                }
-            }
-        ],
-        "tmax": 500,
-        "ext": {
-            "prebid": {
-                "data": {
-                    "eidpermissions": [
-                        {
-                            "source": "source1",
-                            "bidders": [
-                                "APPNEXUS"
-                            ]
-                        }
-                    ]
-                },
-                "cache": {
-                    "bids": {}
-                },
-                "channel": {
-                    "name": "video",
-                    "version": "1.0"
-                },
-                "targeting": {
-                    "includewinners": false,
-                    "pricegranularity": {
-                        "precision": 2,
-                        "ranges": [
-                            {
-                                "max": 20,
-                                "increment": 0.10
-                            }
-                        ]
-                    }
-                }
-            }
-        }
-    },
-    "expectedBidResponse": {
-        "id": "some-request-id",
-        "seatbid": [
-            {
-                "bid": [
-                    {
-                        "id": "appnexus-bid",
-                        "impid": "some-impression-id",
-                        "price": 1
-                    }
-                ],
-                "seat": "APPNEXUS"
-            }
-        ],
-        "bidid": "test bid id",
-        "cur": "USD",
-        "nbr": 0
-    },
-    "expectedReturnCode": 200
-}
\ No newline at end of file
diff --git a/endpoints/openrtb2/video_auction.go b/endpoints/openrtb2/video_auction.go
index 7b2a7a52..8faf1554 100644
--- a/endpoints/openrtb2/video_auction.go
+++ b/endpoints/openrtb2/video_auction.go
@@ -52,7 +52,7 @@ func NewVideoEndpoint(
 	accounts stored_requests.AccountFetcher,
 	cfg *config.Configuration,
 	met metrics.MetricsEngine,
-	pbsAnalytics analytics.PBSAnalyticsModule,
+	analyticsRunner analytics.Runner,
 	disabledBidders map[string]string,
 	defReqJSON []byte,
 	bidderMap map[string]openrtb_ext.BidderName,
@@ -82,7 +82,7 @@ func NewVideoEndpoint(
 		accounts,
 		cfg,
 		met,
-		pbsAnalytics,
+		analyticsRunner,
 		disabledBidders,
 		defRequest,
 		defReqJSON,
@@ -92,7 +92,8 @@ func NewVideoEndpoint(
 		ipValidator,
 		empty_fetcher.EmptyFetcher{},
 		hooks.EmptyPlanBuilder{},
-		tmaxAdjustments}).VideoAuctionEndpoint), nil
+		tmaxAdjustments,
+		openrtb_ext.NormalizeBidderName}).VideoAuctionEndpoint), nil
 }
 
 /*
@@ -149,6 +150,8 @@ func (deps *endpointDeps) VideoAuctionEndpoint(w http.ResponseWriter, r *http.Re
 	}
 	debugLog.DebugEnabledOrOverridden = debugLog.Enabled || debugLog.DebugOverride
 
+	activityControl := privacy.ActivityControl{}
+
 	defer func() {
 		if len(debugLog.CacheKey) > 0 && vo.VideoResponse == nil {
 			err := debugLog.PutDebugLogError(deps.cache, deps.cfg.CacheURL.ExpectedTimeMillis, vo.Errors)
@@ -158,7 +161,7 @@ func (deps *endpointDeps) VideoAuctionEndpoint(w http.ResponseWriter, r *http.Re
 		}
 		deps.metricsEngine.RecordRequest(labels)
 		deps.metricsEngine.RecordRequestTime(labels, time.Since(start))
-		deps.analytics.LogVideoObject(&vo)
+		deps.analytics.LogVideoObject(&vo, activityControl)
 	}()
 
 	w.Header().Set("X-Prebid", version.BuildXPrebidHeader(version.Ver))
@@ -303,7 +306,7 @@ func (deps *endpointDeps) VideoAuctionEndpoint(w http.ResponseWriter, r *http.Re
 		return
 	}
 
-	activityControl := privacy.NewActivityControl(&account.Privacy)
+	activityControl = privacy.NewActivityControl(&account.Privacy)
 
 	secGPC := r.Header.Get("Sec-GPC")
 	auctionRequest := &exchange.AuctionRequest{
diff --git a/endpoints/setuid.go b/endpoints/setuid.go
index 520a133b..7af70d00 100644
--- a/endpoints/setuid.go
+++ b/endpoints/setuid.go
@@ -18,6 +18,7 @@ import (
 	"github.com/prebid/prebid-server/errortypes"
 	"github.com/prebid/prebid-server/gdpr"
 	"github.com/prebid/prebid-server/metrics"
+	"github.com/prebid/prebid-server/openrtb_ext"
 	"github.com/prebid/prebid-server/privacy"
 	gppPrivacy "github.com/prebid/prebid-server/privacy/gpp"
 	"github.com/prebid/prebid-server/stored_requests"
@@ -36,7 +37,7 @@ const (
 
 const uidCookieName = "uids"
 
-func NewSetUIDEndpoint(cfg *config.Configuration, syncersByBidder map[string]usersync.Syncer, gdprPermsBuilder gdpr.PermissionsBuilder, tcf2CfgBuilder gdpr.TCF2ConfigBuilder, pbsanalytics analytics.PBSAnalyticsModule, accountsFetcher stored_requests.AccountFetcher, metricsEngine metrics.MetricsEngine) httprouter.Handle {
+func NewSetUIDEndpoint(cfg *config.Configuration, syncersByBidder map[string]usersync.Syncer, gdprPermsBuilder gdpr.PermissionsBuilder, tcf2CfgBuilder gdpr.TCF2ConfigBuilder, analyticsRunner analytics.Runner, accountsFetcher stored_requests.AccountFetcher, metricsEngine metrics.MetricsEngine) httprouter.Handle {
 	encoder := usersync.Base64Encoder{}
 	decoder := usersync.Base64Decoder{}
 
@@ -46,7 +47,7 @@ func NewSetUIDEndpoint(cfg *config.Configuration, syncersByBidder map[string]use
 			Errors: make([]error, 0),
 		}
 
-		defer pbsanalytics.LogSetUIDObject(&so)
+		defer analyticsRunner.LogSetUIDObject(&so)
 
 		cookie := usersync.ReadCookie(r, decoder, &cfg.HostCookie)
 		if !cookie.AllowSyncs() {
@@ -329,7 +330,13 @@ func getSyncer(query url.Values, syncersByBidder map[string]usersync.Syncer) (us
 		return nil, "", errors.New(`"bidder" query param is required`)
 	}
 
-	syncer, syncerExists := syncersByBidder[bidder]
+	// case insensitive comparison
+	bidderNormalized, bidderFound := openrtb_ext.NormalizeBidderName(bidder)
+	if !bidderFound {
+		return nil, "", errors.New("The bidder name provided is not supported by Prebid Server")
+	}
+
+	syncer, syncerExists := syncersByBidder[bidderNormalized.String()]
 	if !syncerExists {
 		return nil, "", errors.New("The bidder name provided is not supported by Prebid Server")
 	}
@@ -340,7 +347,7 @@ func getSyncer(query url.Values, syncersByBidder map[string]usersync.Syncer) (us
 func isSyncerPriority(bidderNameFromSyncerQuery string, priorityGroups [][]string) bool {
 	for _, group := range priorityGroups {
 		for _, bidder := range group {
-			if bidderNameFromSyncerQuery == bidder {
+			if strings.EqualFold(bidderNameFromSyncerQuery, bidder) {
 				return true
 			}
 		}
diff --git a/exchange/adapter_builders.go b/exchange/adapter_builders.go
index c5b4b113..95cb1626 100755
--- a/exchange/adapter_builders.go
+++ b/exchange/adapter_builders.go
@@ -148,6 +148,7 @@ import (
 	"github.com/prebid/prebid-server/adapters/smartadserver"
 	"github.com/prebid/prebid-server/adapters/smarthub"
 	"github.com/prebid/prebid-server/adapters/smartrtb"
+	"github.com/prebid/prebid-server/adapters/smartx"
 	"github.com/prebid/prebid-server/adapters/smartyads"
 	"github.com/prebid/prebid-server/adapters/smilewanted"
 	"github.com/prebid/prebid-server/adapters/sonobi"
@@ -348,6 +349,7 @@ func newAdapterBuilders() map[openrtb_ext.BidderName]adapters.Builder {
 		openrtb_ext.BidderSmartAdserver:     smartadserver.Builder,
 		openrtb_ext.BidderSmartHub:          smarthub.Builder,
 		openrtb_ext.BidderSmartRTB:          smartrtb.Builder,
+		openrtb_ext.BidderSmartx:            smartx.Builder,
 		openrtb_ext.BidderSmartyAds:         smartyads.Builder,
 		openrtb_ext.BidderSmileWanted:       smilewanted.Builder,
 		openrtb_ext.BidderSonobi:            sonobi.Builder,
diff --git a/exchange/adapter_util.go b/exchange/adapter_util.go
index 2337f7f6..1890635c 100644
--- a/exchange/adapter_util.go
+++ b/exchange/adapter_util.go
@@ -33,10 +33,6 @@ func buildBidders(infos config.BidderInfos, builders map[openrtb_ext.BidderName]
 	var errs []error
 
 	for bidder, info := range infos {
-		if len(info.AliasOf) > 0 {
-			errs = append(errs, fmt.Errorf("This feature is currently under development"))
-			continue
-		}
 		bidderName, bidderNameFound := openrtb_ext.NormalizeBidderName(bidder)
 		if !bidderNameFound {
 			errs = append(errs, fmt.Errorf("%v: unknown bidder", bidder))
diff --git a/exchange/utils.go b/exchange/utils.go
index 0a38e487..0d9c90cd 100644
--- a/exchange/utils.go
+++ b/exchange/utils.go
@@ -7,6 +7,7 @@ import (
 	"errors"
 	"fmt"
 	"math/rand"
+	"strings"
 
 	"github.com/buger/jsonparser"
 	"github.com/prebid/go-gdpr/vendorconsent"
@@ -710,7 +711,7 @@ func removeUnpermissionedEids(request *openrtb2.BidRequest, bidder string, reque
 		allowed := false
 		if rule, hasRule := eidRules[eid.Source]; hasRule {
 			for _, ruleBidder := range rule {
-				if ruleBidder == "*" || ruleBidder == bidder {
+				if ruleBidder == "*" || strings.EqualFold(ruleBidder, bidder) {
 					allowed = true
 					break
 				}
diff --git a/metrics/config/metrics.go b/metrics/config/metrics.go
index 757cde33..d5cb0034 100644
--- a/metrics/config/metrics.go
+++ b/metrics/config/metrics.go
@@ -322,22 +322,6 @@ func (me *MultiMetricsEngine) RecordBidValidationSecureMarkupWarn(adapter openrt
 	}
 }
 
-func (me *MultiMetricsEngine) RecordAccountGDPRChannelEnabledWarning(account string) {
-	for _, thisME := range *me {
-		thisME.RecordAccountGDPRChannelEnabledWarning(account)
-	}
-}
-func (me *MultiMetricsEngine) RecordAccountCCPAChannelEnabledWarning(account string) {
-	for _, thisME := range *me {
-		thisME.RecordAccountCCPAChannelEnabledWarning(account)
-	}
-}
-func (me *MultiMetricsEngine) RecordAccountUpgradeStatus(account string) {
-	for _, thisME := range *me {
-		thisME.RecordAccountUpgradeStatus(account)
-	}
-}
-
 func (me *MultiMetricsEngine) RecordModuleCalled(labels metrics.ModuleLabels, duration time.Duration) {
 	for _, thisME := range *me {
 		thisME.RecordModuleCalled(labels, duration)
@@ -531,15 +515,6 @@ func (me *NilMetricsEngine) RecordBidValidationSecureMarkupError(adapter openrtb
 func (me *NilMetricsEngine) RecordBidValidationSecureMarkupWarn(adapter openrtb_ext.BidderName, account string) {
 }
 
-func (me *NilMetricsEngine) RecordAccountGDPRChannelEnabledWarning(account string) {
-}
-
-func (me *NilMetricsEngine) RecordAccountCCPAChannelEnabledWarning(account string) {
-}
-
-func (me *NilMetricsEngine) RecordAccountUpgradeStatus(account string) {
-}
-
 func (me *NilMetricsEngine) RecordModuleCalled(labels metrics.ModuleLabels, duration time.Duration) {
 }
 
diff --git a/metrics/go_metrics.go b/metrics/go_metrics.go
index 37080b05..0d60a700 100644
--- a/metrics/go_metrics.go
+++ b/metrics/go_metrics.go
@@ -125,11 +125,6 @@ type accountMetrics struct {
 	bidValidationCreativeSizeWarnMeter metrics.Meter
 	bidValidationSecureMarkupMeter     metrics.Meter
 	bidValidationSecureMarkupWarnMeter metrics.Meter
-
-	// Account Deprciation Metrics
-	channelEnabledGDPRMeter        metrics.Meter
-	channelEnabledCCPAMeter        metrics.Meter
-	accountDeprecationSummaryMeter metrics.Meter
 }
 
 type ModuleMetrics struct {
@@ -566,10 +561,6 @@ func (me *Metrics) getAccountMetrics(id string) *accountMetrics {
 	am.bidValidationSecureMarkupMeter = metrics.GetOrRegisterMeter(fmt.Sprintf("account.%s.response.validation.secure.err", id), me.MetricsRegistry)
 	am.bidValidationSecureMarkupWarnMeter = metrics.GetOrRegisterMeter(fmt.Sprintf("account.%s.response.validation.secure.warn", id), me.MetricsRegistry)
 
-	am.channelEnabledCCPAMeter = metrics.GetOrRegisterMeter(fmt.Sprintf("account.%s.config.ccpa.channel_enabled.warn", id), me.MetricsRegistry)
-	am.channelEnabledGDPRMeter = metrics.GetOrRegisterMeter(fmt.Sprintf("account.%s.config.gdpr.channel_enabled.warn", id), me.MetricsRegistry)
-	am.accountDeprecationSummaryMeter = metrics.GetOrRegisterMeter(fmt.Sprintf("account.%s.config.summary", id), me.MetricsRegistry)
-
 	if !me.MetricsDisabled.AccountModulesMetrics {
 		for _, mod := range me.modules {
 			am.moduleMetrics[mod] = makeBlankModuleMetrics()
@@ -615,27 +606,6 @@ func (me *Metrics) RecordDebugRequest(debugEnabled bool, pubID string) {
 	}
 }
 
-func (me *Metrics) RecordAccountGDPRChannelEnabledWarning(account string) {
-	if account != PublisherUnknown {
-		am := me.getAccountMetrics(account)
-		am.channelEnabledGDPRMeter.Mark(1)
-	}
-}
-
-func (me *Metrics) RecordAccountCCPAChannelEnabledWarning(account string) {
-	if account != PublisherUnknown {
-		am := me.getAccountMetrics(account)
-		am.channelEnabledCCPAMeter.Mark(1)
-	}
-}
-
-func (me *Metrics) RecordAccountUpgradeStatus(account string) {
-	if account != PublisherUnknown {
-		am := me.getAccountMetrics(account)
-		am.accountDeprecationSummaryMeter.Mark(1)
-	}
-}
-
 func (me *Metrics) RecordStoredResponse(pubId string) {
 	me.StoredResponsesMeter.Mark(1)
 	if pubId != PublisherUnknown && !me.MetricsDisabled.AccountStoredResponses {
diff --git a/metrics/metrics.go b/metrics/metrics.go
index 7315fa72..99e64af0 100644
--- a/metrics/metrics.go
+++ b/metrics/metrics.go
@@ -470,7 +470,4 @@ type MetricsEngine interface {
 	RecordModuleSuccessRejected(labels ModuleLabels)
 	RecordModuleExecutionError(labels ModuleLabels)
 	RecordModuleTimeout(labels ModuleLabels)
-	RecordAccountGDPRChannelEnabledWarning(account string)
-	RecordAccountCCPAChannelEnabledWarning(account string)
-	RecordAccountUpgradeStatus(account string)
 }
diff --git a/metrics/metrics_mock.go b/metrics/metrics_mock.go
index f6ecf57e..eb092f0d 100644
--- a/metrics/metrics_mock.go
+++ b/metrics/metrics_mock.go
@@ -221,15 +221,3 @@ func (me *MetricsEngineMock) RecordModuleExecutionError(labels ModuleLabels) {
 func (me *MetricsEngineMock) RecordModuleTimeout(labels ModuleLabels) {
 	me.Called(labels)
 }
-
-func (me *MetricsEngineMock) RecordAccountGDPRChannelEnabledWarning(account string) {
-	me.Called(account)
-}
-
-func (me *MetricsEngineMock) RecordAccountCCPAChannelEnabledWarning(account string) {
-	me.Called(account)
-}
-
-func (me *MetricsEngineMock) RecordAccountUpgradeStatus(account string) {
-	me.Called(account)
-}
diff --git a/metrics/prometheus/prometheus.go b/metrics/prometheus/prometheus.go
index 363e5527..30fb6c5f 100644
--- a/metrics/prometheus/prometheus.go
+++ b/metrics/prometheus/prometheus.go
@@ -88,11 +88,6 @@ type Metrics struct {
 	accountBidResponseSecureMarkupError   *prometheus.CounterVec
 	accountBidResponseSecureMarkupWarn    *prometheus.CounterVec
 
-	// Account Deprecation Metrics
-	channelEnabledGDPR        prometheus.Counter
-	channelEnabledCCPA        prometheus.Counter
-	accountDeprecationSummary prometheus.Counter
-
 	// Module Metrics as a map where the key is the module name
 	moduleDuration        map[string]*prometheus.HistogramVec
 	moduleCalls           map[string]*prometheus.CounterVec
@@ -502,17 +497,6 @@ func NewMetrics(cfg config.PrometheusMetrics, disabledMetrics config.DisabledMet
 		"Count of AdsCert request, and if they were successfully sent.",
 		[]string{successLabel})
 
-	metrics.channelEnabledCCPA = newCounterWithoutLabels(cfg, reg,
-		"account_config_ccpa_channel_enabled_warn",
-		"Count of requests referencing an account whose config specifies a depreceated ccpa.channel_enabled field")
-	metrics.channelEnabledGDPR = newCounterWithoutLabels(cfg, reg,
-		"account_config_gdpr_channel_enabled_warn",
-		"Count of requests referencing an account whose config specifies a depreceated gdpr.channel_enabled field")
-
-	metrics.accountDeprecationSummary = newCounterWithoutLabels(cfg, reg,
-		"account_config_summary",
-		"Count of deprecated account config fields encountered across all accounts")
-
 	createModulesMetrics(cfg, reg, &metrics, moduleStageNames, standardTimeBuckets)
 
 	metrics.Gatherer = reg
@@ -693,24 +677,6 @@ func (m *Metrics) RecordDebugRequest(debugEnabled bool, pubID string) {
 	}
 }
 
-func (m *Metrics) RecordAccountGDPRChannelEnabledWarning(account string) {
-	if account != metrics.PublisherUnknown {
-		m.channelEnabledGDPR.Inc()
-	}
-}
-
-func (m *Metrics) RecordAccountCCPAChannelEnabledWarning(account string) {
-	if account != metrics.PublisherUnknown {
-		m.channelEnabledCCPA.Inc()
-	}
-}
-
-func (m *Metrics) RecordAccountUpgradeStatus(account string) {
-	if account != metrics.PublisherUnknown {
-		m.accountDeprecationSummary.Inc()
-	}
-}
-
 func (m *Metrics) RecordStoredResponse(pubId string) {
 	m.storedResponses.Inc()
 	if !m.metricsDisabled.AccountStoredResponses && pubId != metrics.PublisherUnknown {
diff --git a/openrtb_ext/bidders.go b/openrtb_ext/bidders.go
index 67f43ec6..475d56a7 100644
--- a/openrtb_ext/bidders.go
+++ b/openrtb_ext/bidders.go
@@ -178,6 +178,7 @@ var coreBidderNames []BidderName = []BidderName{
 	BidderSmartAdserver,
 	BidderSmartHub,
 	BidderSmartRTB,
+	BidderSmartx,
 	BidderSmartyAds,
 	BidderSmileWanted,
 	BidderSonobi,
@@ -466,6 +467,7 @@ const (
 	BidderSmartAdserver     BidderName = "smartadserver"
 	BidderSmartHub          BidderName = "smarthub"
 	BidderSmartRTB          BidderName = "smartrtb"
+	BidderSmartx            BidderName = "smartx"
 	BidderSmartyAds         BidderName = "smartyads"
 	BidderSmileWanted       BidderName = "smilewanted"
 	BidderSonobi            BidderName = "sonobi"
diff --git a/openrtb_ext/imp_smartx.go b/openrtb_ext/imp_smartx.go
new file mode 100644
index 00000000..9a2975b0
--- /dev/null
+++ b/openrtb_ext/imp_smartx.go
@@ -0,0 +1,10 @@
+package openrtb_ext
+
+type ExtImpSmartclip struct {
+	TagID       string `json:"tagId"`
+	PublisherID string `json:"publisherId"`
+	SiteID      string `json:"siteId"`
+	AppID       string `json:"appId"`
+	BundleID    string `json:"bundleId"`
+	StoreURL    string `json:"storeUrl"`
+}
diff --git a/router/router.go b/router/router.go
index f2b1f7e7..29bd8382 100644
--- a/router/router.go
+++ b/router/router.go
@@ -10,7 +10,7 @@ import (
 	"strings"
 	"time"
 
-	analyticsConf "github.com/prebid/prebid-server/analytics/config"
+	analyticsBuild "github.com/prebid/prebid-server/analytics/build"
 	"github.com/prebid/prebid-server/config"
 	"github.com/prebid/prebid-server/currency"
 	"github.com/prebid/prebid-server/endpoints"
@@ -191,7 +191,7 @@ func New(cfg *config.Configuration, rateConvertor *currency.RateConverter) (r *R
 	// todo(zachbadgett): better shutdown
 	r.Shutdown = shutdown
 
-	pbsAnalytics := analyticsConf.NewPBSAnalytics(&cfg.Analytics)
+	analyticsRunner := analyticsBuild.New(&cfg.Analytics)
 
 	paramsValidator, err := openrtb_ext.NewBidderParamsValidator(schemaDirectory)
 	if err != nil {
@@ -228,17 +228,17 @@ func New(cfg *config.Configuration, rateConvertor *currency.RateConverter) (r *R
 	macroReplacer := macros.NewStringIndexBasedReplacer()
 	theExchange := exchange.NewExchange(adapters, cacheClient, cfg, syncersByBidder, r.MetricsEngine, cfg.BidderInfos, gdprPermsBuilder, rateConvertor, categoriesFetcher, adsCertSigner, macroReplacer)
 	var uuidGenerator uuidutil.UUIDRandomGenerator
-	openrtbEndpoint, err := openrtb2.NewEndpoint(uuidGenerator, theExchange, paramsValidator, fetcher, accounts, cfg, r.MetricsEngine, pbsAnalytics, disabledBidders, defReqJSON, activeBidders, storedRespFetcher, planBuilder, tmaxAdjustments)
+	openrtbEndpoint, err := openrtb2.NewEndpoint(uuidGenerator, theExchange, paramsValidator, fetcher, accounts, cfg, r.MetricsEngine, analyticsRunner, disabledBidders, defReqJSON, activeBidders, storedRespFetcher, planBuilder, tmaxAdjustments)
 	if err != nil {
 		glog.Fatalf("Failed to create the openrtb2 endpoint handler. %v", err)
 	}
 
-	ampEndpoint, err := openrtb2.NewAmpEndpoint(uuidGenerator, theExchange, paramsValidator, ampFetcher, accounts, cfg, r.MetricsEngine, pbsAnalytics, disabledBidders, defReqJSON, activeBidders, storedRespFetcher, planBuilder, tmaxAdjustments)
+	ampEndpoint, err := openrtb2.NewAmpEndpoint(uuidGenerator, theExchange, paramsValidator, ampFetcher, accounts, cfg, r.MetricsEngine, analyticsRunner, disabledBidders, defReqJSON, activeBidders, storedRespFetcher, planBuilder, tmaxAdjustments)
 	if err != nil {
 		glog.Fatalf("Failed to create the amp endpoint handler. %v", err)
 	}
 
-	videoEndpoint, err := openrtb2.NewVideoEndpoint(uuidGenerator, theExchange, paramsValidator, fetcher, videoFetcher, accounts, cfg, r.MetricsEngine, pbsAnalytics, disabledBidders, defReqJSON, activeBidders, cacheClient, tmaxAdjustments)
+	videoEndpoint, err := openrtb2.NewVideoEndpoint(uuidGenerator, theExchange, paramsValidator, fetcher, videoFetcher, accounts, cfg, r.MetricsEngine, analyticsRunner, disabledBidders, defReqJSON, activeBidders, cacheClient, tmaxAdjustments)
 	if err != nil {
 		glog.Fatalf("Failed to create the video endpoint handler. %v", err)
 	}
@@ -254,7 +254,7 @@ func New(cfg *config.Configuration, rateConvertor *currency.RateConverter) (r *R
 	r.GET("/info/bidders", infoEndpoints.NewBiddersEndpoint(cfg.BidderInfos, defaultAliases))
 	r.GET("/info/bidders/:bidderName", infoEndpoints.NewBiddersDetailEndpoint(cfg.BidderInfos, defaultAliases))
 	r.GET("/bidders/params", NewJsonDirectoryServer(schemaDirectory, paramsValidator, defaultAliases))
-	r.POST("/cookie_sync", endpoints.NewCookieSyncEndpoint(syncersByBidder, cfg, gdprPermsBuilder, tcf2CfgBuilder, r.MetricsEngine, pbsAnalytics, accounts, activeBidders).Handle)
+	r.POST("/cookie_sync", endpoints.NewCookieSyncEndpoint(syncersByBidder, cfg, gdprPermsBuilder, tcf2CfgBuilder, r.MetricsEngine, analyticsRunner, accounts, activeBidders).Handle)
 	r.GET("/status", endpoints.NewStatusEndpoint(cfg.StatusResponse))
 	r.GET("/", serveIndex)
 	r.Handler("GET", "/version", endpoints.NewVersionEndpoint(version.Ver, version.Rev))
@@ -267,7 +267,7 @@ func New(cfg *config.Configuration, rateConvertor *currency.RateConverter) (r *R
 	}
 
 	// event endpoint
-	eventEndpoint := events.NewEventEndpoint(cfg, accounts, pbsAnalytics, r.MetricsEngine)
+	eventEndpoint := events.NewEventEndpoint(cfg, accounts, analyticsRunner, r.MetricsEngine)
 	r.GET("/event", eventEndpoint)
 
 	userSyncDeps := &pbs.UserSyncDeps{
@@ -277,7 +277,7 @@ func New(cfg *config.Configuration, rateConvertor *currency.RateConverter) (r *R
 		PriorityGroups:   cfg.UserSync.PriorityGroups,
 	}
 
-	r.GET("/setuid", endpoints.NewSetUIDEndpoint(cfg, syncersByBidder, gdprPermsBuilder, tcf2CfgBuilder, pbsAnalytics, accounts, r.MetricsEngine))
+	r.GET("/setuid", endpoints.NewSetUIDEndpoint(cfg, syncersByBidder, gdprPermsBuilder, tcf2CfgBuilder, analyticsRunner, accounts, r.MetricsEngine))
 	r.GET("/getuids", endpoints.NewGetUIDsEndpoint(cfg.HostCookie))
 	r.POST("/optout", userSyncDeps.OptOut)
 	r.GET("/optout", userSyncDeps.OptOut)
diff --git a/static/bidder-info/lunamedia.yaml b/static/bidder-info/lunamedia.yaml
index ef34143e..4064a566 100644
--- a/static/bidder-info/lunamedia.yaml
+++ b/static/bidder-info/lunamedia.yaml
@@ -1,6 +1,6 @@
 endpoint: "http://rtb.lunamedia.live/?pid={{.PublisherID}}"
 maintainer:
-  email: "josh@lunamedia.io"
+  email: "cs@lunamedia.io"
 capabilities:
   site:
     mediaTypes:
diff --git a/static/bidder-info/onetag.yaml b/static/bidder-info/onetag.yaml
index 3677921e..bc52faff 100644
--- a/static/bidder-info/onetag.yaml
+++ b/static/bidder-info/onetag.yaml
@@ -1,4 +1,6 @@
 endpoint: "https://prebid-server.onetag-sys.com/prebid-server/{{.PublisherID}}"
+openrtb:
+  version: 2.6
 maintainer:
   email: devops@onetag.com
 gvlVendorID: 241
diff --git a/static/bidder-info/smartx.yaml b/static/bidder-info/smartx.yaml
new file mode 100644
index 00000000..9a387ecf
--- /dev/null
+++ b/static/bidder-info/smartx.yaml
@@ -0,0 +1,12 @@
+endpoint: "https://bid.smartclip.net/bid/1005"
+maintainer:
+  email: "bidding@smartclip.tv"
+gvlVendorID: 115
+modifyingVastXmlAllowed: false
+capabilities:
+  site:
+    mediaTypes:
+      - video
+  app:
+    mediaTypes:
+      - video
diff --git a/static/bidder-params/smartx.json b/static/bidder-params/smartx.json
new file mode 100644
index 00000000..3bd97456
--- /dev/null
+++ b/static/bidder-params/smartx.json
@@ -0,0 +1,37 @@
+{
+  "$schema": "http://json-schema.org/draft-04/schema#",
+  "title": "smartclip.tv Adapter Params",
+  "description": "A schema which validates params accepted by the smartclip.tv adapter",
+  "type": "object",
+  "properties": {
+    "tagId": {
+      "type": "string",
+      "description": "Ad tag ID"
+    },
+    "publisherId": {
+      "type": "string",
+      "description": "Publisher ID"
+    },
+    "siteId": {
+      "type": "string",
+      "description": "Site ID"
+    },
+    "appId": {
+      "type": "string",
+      "description": "App ID"
+    },
+    "bundleId": {
+      "type": "string",
+      "description": "Bundle ID"
+    },
+    "storeUrl": {
+      "type": "string",
+      "description": "AppStore URL"
+    }
+  },
+  "oneOf": [
+    { "required": ["siteId"] },
+    { "required": ["appId"] }
+  ],
+  "required": ["tagId", "publisherId"]
+}
\ No newline at end of file


Test patch:
diff --git a/account/account_test.go b/account/account_test.go
index da0457f8..7a242f21 100644
--- a/account/account_test.go
+++ b/account/account_test.go
@@ -214,106 +214,3 @@ func TestSetDerivedConfig(t *testing.T) {
 		assert.Equal(t, account.GDPR.Purpose1.EnforceAlgoID, tt.wantEnforceAlgoID, tt.description)
 	}
 }
-
-func TestGdprCcpaChannelEnabledMetrics(t *testing.T) {
-	cfg := &config.Configuration{}
-	fetcher := &mockAccountFetcher{}
-	assert.NoError(t, cfg.MarshalAccountDefaults())
-
-	testCases := []struct {
-		name                string
-		givenAccountID      string
-		givenMetric         string
-		expectedMetricCount int
-	}{
-		{
-			name:                "ChannelEnabledGDPR",
-			givenAccountID:      "gdpr_channel_enabled_acct",
-			givenMetric:         "RecordAccountGDPRChannelEnabledWarning",
-			expectedMetricCount: 1,
-		},
-		{
-			name:                "ChannelEnabledCCPA",
-			givenAccountID:      "ccpa_channel_enabled_acct",
-			givenMetric:         "RecordAccountCCPAChannelEnabledWarning",
-			expectedMetricCount: 1,
-		},
-		{
-			name:                "NotChannelEnabledCCPA",
-			givenAccountID:      "valid_acct",
-			givenMetric:         "RecordAccountCCPAChannelEnabledWarning",
-			expectedMetricCount: 0,
-		},
-		{
-			name:                "NotChannelEnabledGDPR",
-			givenAccountID:      "valid_acct",
-			givenMetric:         "RecordAccountGDPRChannelEnabledWarning",
-			expectedMetricCount: 0,
-		},
-	}
-
-	for _, test := range testCases {
-		t.Run(test.name, func(t *testing.T) {
-			metrics := &metrics.MetricsEngineMock{}
-			metrics.Mock.On(test.givenMetric, mock.Anything, mock.Anything).Return()
-			metrics.Mock.On("RecordAccountUpgradeStatus", mock.Anything, mock.Anything).Return()
-
-			_, _ = GetAccount(context.Background(), cfg, fetcher, test.givenAccountID, metrics)
-
-			metrics.AssertNumberOfCalls(t, test.givenMetric, test.expectedMetricCount)
-		})
-	}
-}
-
-func TestAccountUpgradeStatusGetAccount(t *testing.T) {
-	cfg := &config.Configuration{}
-	fetcher := &mockAccountFetcher{}
-	assert.NoError(t, cfg.MarshalAccountDefaults())
-
-	testCases := []struct {
-		name                string
-		givenAccountIDs     []string
-		givenMetrics        []string
-		expectedMetricCount int
-	}{
-		{
-			name:                "ZeroDeprecatedConfigs",
-			givenAccountIDs:     []string{"valid_acct"},
-			givenMetrics:        []string{},
-			expectedMetricCount: 0,
-		},
-		{
-			name:                "OneDeprecatedConfigGDPRChannelEnabled",
-			givenAccountIDs:     []string{"gdpr_channel_enabled_acct"},
-			givenMetrics:        []string{"RecordAccountGDPRChannelEnabledWarning"},
-			expectedMetricCount: 1,
-		},
-		{
-			name:                "OneDeprecatedConfigCCPAChannelEnabled",
-			givenAccountIDs:     []string{"ccpa_channel_enabled_acct"},
-			givenMetrics:        []string{"RecordAccountCCPAChannelEnabledWarning"},
-			expectedMetricCount: 1,
-		},
-		{
-			name:                "MultipleAccountsWithDeprecatedConfigs",
-			givenAccountIDs:     []string{"gdpr_channel_enabled_acct", "ccpa_channel_enabled_acct"},
-			givenMetrics:        []string{"RecordAccountGDPRChannelEnabledWarning", "RecordAccountCCPAChannelEnabledWarning"},
-			expectedMetricCount: 2,
-		},
-	}
-
-	for _, test := range testCases {
-		t.Run(test.name, func(t *testing.T) {
-			metrics := &metrics.MetricsEngineMock{}
-			for _, metric := range test.givenMetrics {
-				metrics.Mock.On(metric, mock.Anything, mock.Anything).Return()
-			}
-			metrics.Mock.On("RecordAccountUpgradeStatus", mock.Anything, mock.Anything).Return()
-
-			for _, accountID := range test.givenAccountIDs {
-				_, _ = GetAccount(context.Background(), cfg, fetcher, accountID, metrics)
-			}
-			metrics.AssertNumberOfCalls(t, "RecordAccountUpgradeStatus", test.expectedMetricCount)
-		})
-	}
-}
diff --git a/adapters/pubmatic/pubmatictest/exemplary/fledge.json b/adapters/pubmatic/pubmatictest/exemplary/fledge.json
new file mode 100644
index 00000000..793f0984
--- /dev/null
+++ b/adapters/pubmatic/pubmatictest/exemplary/fledge.json
@@ -0,0 +1,166 @@
+{
+  "mockBidRequest": {
+    "id": "test-request-id",
+    "imp": [
+      {
+        "id": "test-imp-id",
+        "banner": {
+          "format": [
+            {
+              "w": 728,
+              "h": 90
+            }
+          ]
+        },
+        "ext": {
+          "ae": 1,
+          "bidder": {
+            "publisherId": "999",
+            "adSlot": "AdTag_Div1@300x250"
+          }
+        }
+      }
+    ]
+  },
+  "httpCalls": [
+    {
+      "expectedRequest": {
+        "uri": "https://hbopenbid.pubmatic.com/translator?source=prebid-server",
+        "body": {
+          "id": "test-request-id",
+          "imp": [
+            {
+              "id": "test-imp-id",
+              "banner": {
+                "format": [
+                  {
+                    "w": 728,
+                    "h": 90
+                  }
+                ],
+                "h": 250,
+                "w": 300
+              },
+              "tagid": "AdTag_Div1",
+              "ext": {
+                "ae": 1
+              }
+            }
+          ],
+          "ext": {"prebid":{}}
+        }
+      },
+      "mockResponse": {
+        "status": 200,
+        "body": {
+          "id": "test-request-id",
+          "seatbid": [
+            {
+              "seat": "pubmatic",
+              "bid": [
+                {
+                  "id": "9d8e77c2-ee0f-4781-af59-5359493b11af",
+                  "impid": "test-imp-id",
+                  "price": 1.1,
+                  "adm": "some-test-ad",
+                  "crid": "crid_10",
+                  "h": 90,
+                  "w": 728,
+                  "ext": {}
+                }
+              ]
+            }
+          ],
+          "cur": "USD",
+          "ext": {
+            "fledge_auction_configs": {
+              "test-imp-id": {
+                "seller": "PUBMATIC_SELLER_CONSTANT_STRING",
+                "sellerTimeout": 123,
+                "decisionLogicUrl": "PUBMATIC_URL_CONSTANT_STRING",
+                "interestGroupBuyers": [
+                  "somedomain1.com",
+                  "somedomain2.com",
+                  "somedomain3.com",
+                  "somedomain4.com"
+                ],
+                "perBuyerSignals": {
+                  "somedomain1.com": {
+                    "multiplier": 1,
+                    "win_reporting_id": "1234"
+                  },
+                  "somedomain2.com": {
+                    "multiplier": 2,
+                    "win_reporting_id": "2345"
+                  },
+                  "somedomain3.com": {
+                    "multiplier": 3,
+                    "win_reporting_id": "3456"
+                  },
+                  "somedomain4.com": {
+                    "multiplier": 4,
+                    "win_reporting_id": "4567"
+                  }
+                }
+              }
+            }
+          }
+        }
+      }
+    }
+  ],
+  "expectedBidResponses": [
+    {
+      "currency": "USD",
+      "bids": [
+        {
+          "bid": {
+            "id": "9d8e77c2-ee0f-4781-af59-5359493b11af",
+            "impid": "test-imp-id",
+            "price": 1.1,
+            "adm": "some-test-ad",
+            "crid": "crid_10",
+            "w": 728,
+            "h": 90,
+            "ext": {}
+          },
+          "type": "banner"
+        }
+      ],
+      "fledgeauctionconfigs": [
+        {
+          "impid": "test-imp-id",
+          "config": {
+            "seller": "PUBMATIC_SELLER_CONSTANT_STRING",
+            "sellerTimeout": 123,
+            "decisionLogicUrl": "PUBMATIC_URL_CONSTANT_STRING",
+            "interestGroupBuyers": [
+              "somedomain1.com",
+              "somedomain2.com",
+              "somedomain3.com",
+              "somedomain4.com"
+            ],
+            "perBuyerSignals": {
+              "somedomain1.com": {
+                "multiplier": 1,
+                "win_reporting_id": "1234"
+              },
+              "somedomain2.com": {
+                "multiplier": 2,
+                "win_reporting_id": "2345"
+              },
+              "somedomain3.com": {
+                "multiplier": 3,
+                "win_reporting_id": "3456"
+              },
+              "somedomain4.com": {
+                "multiplier": 4,
+                "win_reporting_id": "4567"
+              }
+            }
+          }
+        }
+      ]
+    }
+  ]
+}
\ No newline at end of file
diff --git a/adapters/smartx/params_test.go b/adapters/smartx/params_test.go
new file mode 100644
index 00000000..fd28f4ea
--- /dev/null
+++ b/adapters/smartx/params_test.go
@@ -0,0 +1,58 @@
+package smartx
+
+import (
+	"encoding/json"
+	"testing"
+
+	"github.com/prebid/prebid-server/openrtb_ext"
+)
+
+var validParams = []string{
+	`{"tagId":"Nu68JuOWAvrbzoyrOR9a7A", "publisherId":"11986", "siteId":"22860"}`,
+	`{"tagId":"Nu68JuOWAvrbzoyrOR9a7A", "publisherId":"11986", "appId":"22860"}`,
+}
+
+func TestValidParams(t *testing.T) {
+	validator, err := openrtb_ext.NewBidderParamsValidator("../../static/bidder-params")
+	if err != nil {
+		t.Fatalf("Failed to fetch the json-schemas. %v", err)
+	}
+
+	for _, validParam := range validParams {
+		if err := validator.Validate(openrtb_ext.BidderSmartx, json.RawMessage(validParam)); err != nil {
+			t.Errorf("Schema rejected smartx params: %s", validParam)
+		}
+	}
+}
+
+var invalidParams = []string{
+	``,
+	`null`,
+	`true`,
+	`5`,
+	`[]`,
+	`{}`,
+	`{"anyparam": "anyvalue"}`,
+	`{"tagId":"Nu68JuOWAvrbzoyrOR9a7A"}`,
+	`{"publisherId":"11986"}`,
+	`{"siteId":"22860"}`,
+	`{"appId":"22860"}`,
+	`{"tagId":"Nu68JuOWAvrbzoyrOR9a7A", "publisherId":"11986"}`,
+	`{"tagId":"Nu68JuOWAvrbzoyrOR9a7A", "siteId":"22860"}`,
+	`{"tagId":"Nu68JuOWAvrbzoyrOR9a7A", "appId":"22860"}`,
+	`{"publisherId":"11986", "appId":"22860"}`,
+	`{"publisherId":"11986", "appId":"22860"}`,
+}
+
+func TestInvalidParams(t *testing.T) {
+	validator, err := openrtb_ext.NewBidderParamsValidator("../../static/bidder-params")
+	if err != nil {
+		t.Fatalf("Failed to fetch the json-schemas. %v", err)
+	}
+
+	for _, invalidParam := range invalidParams {
+		if err := validator.Validate(openrtb_ext.BidderSmartHub, json.RawMessage(invalidParam)); err == nil {
+			t.Errorf("Schema allowed unexpected params: %s", invalidParam)
+		}
+	}
+}
diff --git a/adapters/smartx/smartx_test.go b/adapters/smartx/smartx_test.go
new file mode 100644
index 00000000..ba6f6ba7
--- /dev/null
+++ b/adapters/smartx/smartx_test.go
@@ -0,0 +1,24 @@
+package smartx
+
+import (
+	"testing"
+
+	"github.com/prebid/prebid-server/adapters/adapterstest"
+	"github.com/prebid/prebid-server/config"
+	"github.com/prebid/prebid-server/openrtb_ext"
+)
+
+const testsDir = "smartxtest"
+const testsBidderEndpoint = "https://bid.smartclip.net/bid/1005"
+
+func TestJsonSamples(t *testing.T) {
+	bidder, buildErr := Builder(
+		openrtb_ext.BidderRise,
+		config.Adapter{Endpoint: testsBidderEndpoint},
+		config.Server{ExternalUrl: "http://hosturl.com", GvlID: 115, DataCenter: "2"})
+	if buildErr != nil {
+		t.Fatalf("Builder returned unexpected error %v", buildErr)
+	}
+
+	adapterstest.RunJSONBidderTest(t, testsDir, bidder)
+}
diff --git a/adapters/smartx/smartxtest/exemplary/01-video.json b/adapters/smartx/smartxtest/exemplary/01-video.json
new file mode 100644
index 00000000..c4d3f9c5
--- /dev/null
+++ b/adapters/smartx/smartxtest/exemplary/01-video.json
@@ -0,0 +1,77 @@
+{
+  "mockBidRequest":{
+    "id":"test-request-id-video",
+    "imp":[
+      {
+        "id":"test-imp-id",
+        "video":{
+          "mimes":[
+            "video/mp4"
+          ]
+        }
+      }
+    ]
+  },
+  "httpCalls":[
+    {
+      "expectedRequest":{
+        "uri":"https://bid.smartclip.net/bid/1005",
+        "body":{
+          "id":"test-request-id-video",
+          "imp":[
+            {
+              "id":"test-imp-id",
+              "video":{
+                "mimes":[
+                  "video/mp4"
+                ]
+              }
+            }
+          ]
+        }
+      },
+      "mockResponse":{
+        "status":200,
+        "body":{
+          "id":"test-request-id-video",
+          "seatbid":[
+            {
+              "seat":"smartadserver",
+              "bid":[
+                {
+                  "id":"8ee514f1-b2b8-4abb-89fd-084437d1e800",
+                  "impid":"test-imp-id-video",
+                  "price":0.500000,
+                  "adm":"some-test-ad",
+                  "crid":"crid_10",
+                  "h":576,
+                  "w":1024,
+                  "mtype":2
+                }
+              ]
+            }
+          ],
+          "cur":"EUR"
+        }
+      }
+    }
+  ],
+  "expectedBidResponses":[
+    {
+      "bids":[{
+        "bid":{
+            "id":"8ee514f1-b2b8-4abb-89fd-084437d1e800",
+            "impid":"test-imp-id-video",
+            "price":0.500000,
+            "adm":"some-test-ad",
+            "crid":"crid_10",
+            "h":576,
+            "w":1024,
+            "mtype":2
+        },
+        "currency":"EUR",
+        "type": "video"
+      }]
+    }
+  ]
+}
\ No newline at end of file
diff --git a/adapters/smartx/smartxtest/exemplary/02-consent.json b/adapters/smartx/smartxtest/exemplary/02-consent.json
new file mode 100644
index 00000000..bc0e5604
--- /dev/null
+++ b/adapters/smartx/smartxtest/exemplary/02-consent.json
@@ -0,0 +1,124 @@
+{
+	"mockBidRequest": {
+		"id": "test-request-id",
+		"imp": [
+			{
+				"id": "test-imp-id",
+				"banner": {
+					"w": 300,
+					"h": 250
+				},
+				"ext": {
+					"prebid": {
+						"bidder": {
+							"smartx": {
+								"publisherId": "11986",
+								"tagId": "Nu68JuOWAvrbzoyrOR9a7A",
+								"siteId": "22860"
+							}
+						}
+					}
+				}
+			}
+		],
+		"user": {
+			"ext": {
+				"consent": "COvFyGBOvFyGBAbAAAENAPCAAOAAAAAAAAAAAEEUACCKAAA.IFoEUQQgAIQwgIwQABAEAAAAOIAACAIAAAAQAIAgEAACEAAAAAgAQBAAAAAAAGBAAgAAAAAAAFAAECAAAgAAQARAEQAAAAAJAAIAAgAAAYQEAAAQmAgBC3ZAYzUw"
+			}
+		}
+	},
+	"httpCalls": [
+		{
+			"expectedRequest": {
+				"uri": "https://bid.smartclip.net/bid/1005",
+				"body": {
+					"id": "test-request-id",
+					"imp": [
+						{
+							"id": "test-imp-id",
+							"banner": {
+								"w": 300,
+								"h": 250
+							},
+							"ext": {
+								"prebid": {
+									"bidder": {
+										"smartx": {
+											"publisherId": "11986",
+											"tagId": "Nu68JuOWAvrbzoyrOR9a7A",
+											"siteId": "22860"
+										}
+									}
+								}
+							}
+						}
+					],
+					"user": {
+						"ext": {
+							"consent": "COvFyGBOvFyGBAbAAAENAPCAAOAAAAAAAAAAAEEUACCKAAA.IFoEUQQgAIQwgIwQABAEAAAAOIAACAIAAAAQAIAgEAACEAAAAAgAQBAAAAAAAGBAAgAAAAAAAFAAECAAAgAAQARAEQAAAAAJAAIAAgAAAYQEAAAQmAgBC3ZAYzUw"
+						}
+					}
+				}
+			},
+			"mockResponse": {
+				"status": 200,
+				"body": {
+					"id": "test-request-id",
+					"seatbid": [
+						{
+							"seat": "958",
+							"bid": [
+								{
+									"id": "7706636740145184841",
+									"impid": "test-imp-id",
+									"price": 0.5,
+									"adid": "29681110",
+									"adm": "some-test-ad",
+									"adomain": [
+										"https://advertiser.example.com"
+									],
+									"cid": "958",
+									"crid": "29681110",
+									"h": 250,
+									"w": 300,
+									"ext": {
+										"ix": {}
+									}
+								}
+							]
+						}
+					],
+					"bidid": "5778926625248726496",
+					"cur": "USD"
+				}
+			}
+		}
+	],
+	"expectedBidResponses": [
+		{
+			"currency": "USD",
+			"bids": [
+				{
+					"bid": {
+						"id": "7706636740145184841",
+						"impid": "test-imp-id",
+						"price": 0.5,
+						"adm": "some-test-ad",
+						"adid": "29681110",
+						"adomain": [
+							"https://advertiser.example.com"
+						],
+						"cid": "958",
+						"crid": "29681110",
+						"w": 300,
+						"h": 250,
+						"ext": {
+							"ix": {}
+						}
+					},
+					"type": "video"
+				}
+			]
+		}
+	]
+}
\ No newline at end of file
diff --git a/adapters/smartx/smartxtest/exemplary/03-device.json b/adapters/smartx/smartxtest/exemplary/03-device.json
new file mode 100644
index 00000000..f45cc76c
--- /dev/null
+++ b/adapters/smartx/smartxtest/exemplary/03-device.json
@@ -0,0 +1,121 @@
+{
+  "mockBidRequest": {
+    "id": "test-request-id-video",
+    "device": {
+      "ua": "Mozilla/5.0 (Linux; Android 12; SAMSUNG SM-G780G) AppleWebKit/537.36 (KHTML, like Gecko) SamsungBrowser/17.0 Chrome/96.0.4664.104 Mobile Safari/537.36",
+      "geo": {
+        "lat": 48.1663,
+        "lon": 11.5683,
+        "type": 2,
+        "country": "DEU",
+        "region": "BY",
+        "city": "Munich",
+        "zip": "81249",
+        "ipservice": 3
+      },
+      "dnt": 0,
+      "lmt": 0,
+      "ip": "0.0.0.0",
+      "devicetype": 4,
+      "make": "Samsung",
+      "model": "SM-G780G",
+      "os": "Android",
+      "language": "en"
+    },
+    "imp": [
+      {
+        "id": "test-imp-id",
+        "video": {
+          "mimes": [
+            "video/mp4"
+          ]
+        }
+      }
+    ]
+  },
+  "httpCalls": [
+    {
+      "expectedRequest": {
+        "uri": "https://bid.smartclip.net/bid/1005",
+        "body": {
+          "id": "test-request-id-video",
+          "device": {
+            "ua": "Mozilla/5.0 (Linux; Android 12; SAMSUNG SM-G780G) AppleWebKit/537.36 (KHTML, like Gecko) SamsungBrowser/17.0 Chrome/96.0.4664.104 Mobile Safari/537.36",
+            "geo": {
+              "lat": 48.1663,
+              "lon": 11.5683,
+              "type": 2,
+              "country": "DEU",
+              "region": "BY",
+              "city": "Munich",
+              "zip": "81249",
+              "ipservice": 3
+            },
+            "dnt": 0,
+            "lmt": 0,
+            "ip": "0.0.0.0",
+            "devicetype": 4,
+            "make": "Samsung",
+            "model": "SM-G780G",
+            "os": "Android",
+            "language": "en"
+          },
+          "imp": [
+            {
+              "id": "test-imp-id",
+              "video": {
+                "mimes": [
+                  "video/mp4"
+                ]
+              }
+            }
+          ]
+        }
+      },
+      "mockResponse": {
+        "status": 200,
+        "body": {
+          "id": "test-request-id-video",
+          "seatbid": [
+            {
+              "seat": "smartadserver",
+              "bid": [
+                {
+                  "id": "8ee514f1-b2b8-4abb-89fd-084437d1e800",
+                  "impid": "test-imp-id-video",
+                  "price": 0.500000,
+                  "adm": "some-test-ad",
+                  "crid": "crid_10",
+                  "h": 576,
+                  "w": 1024,
+                  "mtype": 2
+                }
+              ]
+            }
+          ],
+          "cur": "EUR"
+        }
+      }
+    }
+  ],
+  "expectedBidResponses": [
+    {
+      "bids": [
+        {
+          "bid": {
+            "id": "8ee514f1-b2b8-4abb-89fd-084437d1e800",
+            "impid": "test-imp-id-video",
+            "price": 0.500000,
+            "adm": "some-test-ad",
+            "crid": "crid_10",
+            "h": 576,
+            "w": 1024,
+            "mtype": 2
+          },
+          "currency": "EUR",
+          "type": "video"
+        }
+      ]
+    }
+  ]
+}
\ No newline at end of file
diff --git a/adapters/smartx/smartxtest/supplemental/02-internal-server-error.json b/adapters/smartx/smartxtest/supplemental/02-internal-server-error.json
new file mode 100644
index 00000000..d44ef7f7
--- /dev/null
+++ b/adapters/smartx/smartxtest/supplemental/02-internal-server-error.json
@@ -0,0 +1,45 @@
+{
+  "mockBidRequest": {
+    "id": "test-internal-server-error-id",
+    "imp": [
+      {
+        "id": "test-imp-id",
+        "video": {
+          "mimes": [
+            "video/mp4"
+          ]
+        }
+      }
+    ]
+  },
+  "httpCalls": [
+    {
+      "expectedRequest": {
+        "uri": "https://bid.smartclip.net/bid/1005",
+        "body": {
+          "id": "test-internal-server-error-id",
+          "imp": [
+            {
+              "id": "test-imp-id",
+              "video": {
+                "mimes": [
+                  "video/mp4"
+                ]
+              }
+            }
+          ]
+        }
+      },
+      "mockResponse": {
+        "status": 400,
+        "body": {}
+      }
+    }
+  ],
+  "expectedMakeBidsErrors": [
+    {
+      "value": "Unexpected status code: 400. Run with request.debug = 1 for more info",
+      "comparison": "literal"
+    }
+  ]
+}
\ No newline at end of file
diff --git a/adapters/smartx/smartxtest/supplemental/03-missing-bidder-in-response.json b/adapters/smartx/smartxtest/supplemental/03-missing-bidder-in-response.json
new file mode 100644
index 00000000..71875f12
--- /dev/null
+++ b/adapters/smartx/smartxtest/supplemental/03-missing-bidder-in-response.json
@@ -0,0 +1,47 @@
+{
+  "mockBidRequest": {
+    "id": "test-missing-bidder-in-response-id",
+    "imp": [
+      {
+        "id": "test-imp-id",
+        "video": {
+          "mimes": [
+            "video/mp4"
+          ]
+        }
+      }
+    ]
+  },
+  "httpCalls": [
+    {
+      "expectedRequest": {
+        "uri": "https://bid.smartclip.net/bid/1005",
+        "body": {
+          "id": "test-missing-bidder-in-response-id",
+          "imp": [
+            {
+              "id": "test-imp-id",
+              "video": {
+                "mimes": [
+                  "video/mp4"
+                ]
+              }
+            }
+          ]
+        }
+      },
+      "mockResponse": {
+        "body": {
+          "seatbid": []
+        },
+        "status": 200
+      }
+    }
+  ],
+  "expectedMakeBidsErrors": [
+    {
+      "value": "no bidders found in JSON response",
+      "comparison": "literal"
+    }
+  ]
+}
\ No newline at end of file
diff --git a/analytics/config/config_test.go b/analytics/build/build_test.go
similarity index 50%
rename from analytics/config/config_test.go
rename to analytics/build/build_test.go
index c0ad9c26..dbd129a7 100644
--- a/analytics/config/config_test.go
+++ b/analytics/build/build_test.go
@@ -1,15 +1,16 @@
-package config
+package build
 
 import (
+	"github.com/prebid/prebid-server/analytics"
 	"net/http"
 	"os"
 	"testing"
 
 	"github.com/prebid/openrtb/v19/openrtb2"
-	"github.com/stretchr/testify/assert"
-
-	"github.com/prebid/prebid-server/analytics"
 	"github.com/prebid/prebid-server/config"
+	"github.com/prebid/prebid-server/privacy"
+	"github.com/prebid/prebid-server/util/ptrutil"
+	"github.com/stretchr/testify/assert"
 )
 
 const TEST_DIR string = "testFiles"
@@ -21,7 +22,7 @@ func TestSampleModule(t *testing.T) {
 		Status:   http.StatusOK,
 		Errors:   nil,
 		Response: &openrtb2.BidResponse{},
-	})
+	}, privacy.ActivityControl{})
 	if count != 1 {
 		t.Errorf("PBSAnalyticsModule failed at LogAuctionObject")
 	}
@@ -42,17 +43,17 @@ func TestSampleModule(t *testing.T) {
 		t.Errorf("PBSAnalyticsModule failed at LogCookieSyncObject")
 	}
 
-	am.LogAmpObject(&analytics.AmpObject{})
+	am.LogAmpObject(&analytics.AmpObject{}, privacy.ActivityControl{})
 	if count != 4 {
 		t.Errorf("PBSAnalyticsModule failed at LogAmpObject")
 	}
 
-	am.LogVideoObject(&analytics.VideoObject{})
+	am.LogVideoObject(&analytics.VideoObject{}, privacy.ActivityControl{})
 	if count != 5 {
 		t.Errorf("PBSAnalyticsModule failed at LogVideoObject")
 	}
 
-	am.LogNotificationEventObject(&analytics.NotificationEvent{})
+	am.LogNotificationEventObject(&analytics.NotificationEvent{}, privacy.ActivityControl{})
 	if count != 6 {
 		t.Errorf("PBSAnalyticsModule failed at LogNotificationEventObject")
 	}
@@ -74,14 +75,14 @@ func (m *sampleModule) LogAmpObject(ao *analytics.AmpObject) { *m.count++ }
 
 func (m *sampleModule) LogNotificationEventObject(ne *analytics.NotificationEvent) { *m.count++ }
 
-func initAnalytics(count *int) analytics.PBSAnalyticsModule {
+func initAnalytics(count *int) analytics.Runner {
 	modules := make(enabledAnalytics, 0)
-	modules = append(modules, &sampleModule{count})
+	modules["sampleModule"] = &sampleModule{count}
 	return &modules
 }
 
 func TestNewPBSAnalytics(t *testing.T) {
-	pbsAnalytics := NewPBSAnalytics(&config.Analytics{})
+	pbsAnalytics := New(&config.Analytics{})
 	instance := pbsAnalytics.(enabledAnalytics)
 
 	assert.Equal(t, len(instance), 0)
@@ -94,7 +95,7 @@ func TestNewPBSAnalytics_FileLogger(t *testing.T) {
 		}
 	}
 	defer os.RemoveAll(TEST_DIR)
-	mod := NewPBSAnalytics(&config.Analytics{File: config.FileLogs{Filename: TEST_DIR + "/test"}})
+	mod := New(&config.Analytics{File: config.FileLogs{Filename: TEST_DIR + "/test"}})
 	switch modType := mod.(type) {
 	case enabledAnalytics:
 		if len(enabledAnalytics(modType)) != 1 {
@@ -104,7 +105,7 @@ func TestNewPBSAnalytics_FileLogger(t *testing.T) {
 		t.Fatalf("Failed to initialize analytics module")
 	}
 
-	pbsAnalytics := NewPBSAnalytics(&config.Analytics{File: config.FileLogs{Filename: TEST_DIR + "/test"}})
+	pbsAnalytics := New(&config.Analytics{File: config.FileLogs{Filename: TEST_DIR + "/test"}})
 	instance := pbsAnalytics.(enabledAnalytics)
 
 	assert.Equal(t, len(instance), 1)
@@ -112,7 +113,7 @@ func TestNewPBSAnalytics_FileLogger(t *testing.T) {
 
 func TestNewPBSAnalytics_Pubstack(t *testing.T) {
 
-	pbsAnalyticsWithoutError := NewPBSAnalytics(&config.Analytics{
+	pbsAnalyticsWithoutError := New(&config.Analytics{
 		Pubstack: config.Pubstack{
 			Enabled:   true,
 			ScopeId:   "scopeId",
@@ -129,7 +130,7 @@ func TestNewPBSAnalytics_Pubstack(t *testing.T) {
 
 	assert.Equal(t, len(instanceWithoutError), 1)
 
-	pbsAnalyticsWithError := NewPBSAnalytics(&config.Analytics{
+	pbsAnalyticsWithError := New(&config.Analytics{
 		Pubstack: config.Pubstack{
 			Enabled: true,
 		},
@@ -137,3 +138,88 @@ func TestNewPBSAnalytics_Pubstack(t *testing.T) {
 	instanceWithError := pbsAnalyticsWithError.(enabledAnalytics)
 	assert.Equal(t, len(instanceWithError), 0)
 }
+
+func TestSampleModuleActivitiesAllowed(t *testing.T) {
+	var count int
+	am := initAnalytics(&count)
+
+	acAllowed := privacy.NewActivityControl(getDefaultActivityConfig("sampleModule", true))
+
+	ao := &analytics.AuctionObject{
+		Status:   http.StatusOK,
+		Errors:   nil,
+		Response: &openrtb2.BidResponse{},
+	}
+
+	am.LogAuctionObject(ao, acAllowed)
+	if count != 1 {
+		t.Errorf("PBSAnalyticsModule failed at LogAuctionObject")
+	}
+
+	am.LogAmpObject(&analytics.AmpObject{}, acAllowed)
+	if count != 2 {
+		t.Errorf("PBSAnalyticsModule failed at LogAmpObject")
+	}
+
+	am.LogVideoObject(&analytics.VideoObject{}, acAllowed)
+	if count != 3 {
+		t.Errorf("PBSAnalyticsModule failed at LogVideoObject")
+	}
+
+	am.LogNotificationEventObject(&analytics.NotificationEvent{}, acAllowed)
+	if count != 4 {
+		t.Errorf("PBSAnalyticsModule failed at LogNotificationEventObject")
+	}
+}
+
+func TestSampleModuleActivitiesDenied(t *testing.T) {
+	var count int
+	am := initAnalytics(&count)
+
+	acDenied := privacy.NewActivityControl(getDefaultActivityConfig("sampleModule", false))
+
+	ao := &analytics.AuctionObject{
+		Status:   http.StatusOK,
+		Errors:   nil,
+		Response: &openrtb2.BidResponse{},
+	}
+
+	am.LogAuctionObject(ao, acDenied)
+	if count != 0 {
+		t.Errorf("PBSAnalyticsModule failed at LogAuctionObject")
+	}
+
+	am.LogAmpObject(&analytics.AmpObject{}, acDenied)
+	if count != 0 {
+		t.Errorf("PBSAnalyticsModule failed at LogAmpObject")
+	}
+
+	am.LogVideoObject(&analytics.VideoObject{}, acDenied)
+	if count != 0 {
+		t.Errorf("PBSAnalyticsModule failed at LogVideoObject")
+	}
+
+	am.LogNotificationEventObject(&analytics.NotificationEvent{}, acDenied)
+	if count != 0 {
+		t.Errorf("PBSAnalyticsModule failed at LogNotificationEventObject")
+	}
+}
+
+func getDefaultActivityConfig(componentName string, allow bool) *config.AccountPrivacy {
+	return &config.AccountPrivacy{
+		AllowActivities: &config.AllowActivities{
+			ReportAnalytics: config.Activity{
+				Default: ptrutil.ToPtr(true),
+				Rules: []config.ActivityRule{
+					{
+						Allow: allow,
+						Condition: config.ActivityCondition{
+							ComponentName: []string{componentName},
+							ComponentType: []string{"analytics"},
+						},
+					},
+				},
+			},
+		},
+	}
+}
diff --git a/analytics/pubstack/pubstack_module_test.go b/analytics/pubstack/pubstack_module_test.go
index 504a1cfe..23e110df 100644
--- a/analytics/pubstack/pubstack_module_test.go
+++ b/analytics/pubstack/pubstack_module_test.go
@@ -50,47 +50,47 @@ func TestNewModuleSuccess(t *testing.T) {
 	tests := []struct {
 		description string
 		feature     string
-		logObject   func(analytics.PBSAnalyticsModule)
+		logObject   func(analytics.Module)
 	}{
 		{
 			description: "auction events are only published when logging an auction object with auction feature on",
 			feature:     auction,
-			logObject: func(module analytics.PBSAnalyticsModule) {
+			logObject: func(module analytics.Module) {
 				module.LogAuctionObject(&analytics.AuctionObject{Status: http.StatusOK})
 			},
 		},
 		{
 			description: "AMP events are only published when logging an AMP object with AMP feature on",
 			feature:     amp,
-			logObject: func(module analytics.PBSAnalyticsModule) {
+			logObject: func(module analytics.Module) {
 				module.LogAmpObject(&analytics.AmpObject{Status: http.StatusOK})
 			},
 		},
 		{
 			description: "video events are only published when logging a video object with video feature on",
 			feature:     video,
-			logObject: func(module analytics.PBSAnalyticsModule) {
+			logObject: func(module analytics.Module) {
 				module.LogVideoObject(&analytics.VideoObject{Status: http.StatusOK})
 			},
 		},
 		{
 			description: "cookie events are only published when logging a cookie object with cookie feature on",
 			feature:     cookieSync,
-			logObject: func(module analytics.PBSAnalyticsModule) {
+			logObject: func(module analytics.Module) {
 				module.LogCookieSyncObject(&analytics.CookieSyncObject{Status: http.StatusOK})
 			},
 		},
 		{
 			description: "setUID events are only published when logging a setUID object with setUID feature on",
 			feature:     setUID,
-			logObject: func(module analytics.PBSAnalyticsModule) {
+			logObject: func(module analytics.Module) {
 				module.LogSetUIDObject(&analytics.SetUIDObject{Status: http.StatusOK})
 			},
 		},
 		{
 			description: "Ignore excluded fields from marshal",
 			feature:     auction,
-			logObject: func(module analytics.PBSAnalyticsModule) {
+			logObject: func(module analytics.Module) {
 				module.LogAuctionObject(&analytics.AuctionObject{
 					RequestWrapper: &openrtb_ext.RequestWrapper{},
 					SeatNonBid: []openrtb_ext.SeatNonBid{
diff --git a/config/account_test.go b/config/account_test.go
index 58f2a52e..d0c8507d 100644
--- a/config/account_test.go
+++ b/config/account_test.go
@@ -14,68 +14,46 @@ func TestAccountGDPREnabledForChannelType(t *testing.T) {
 	trueValue, falseValue := true, false
 
 	tests := []struct {
-		description                      string
-		giveChannelType                  ChannelType
-		giveGDPREnabled                  *bool
-		giveWebGDPREnabled               *bool
-		giveWebGDPREnabledForIntegration *bool
-		wantEnabled                      *bool
+		description        string
+		giveChannelType    ChannelType
+		giveGDPREnabled    *bool
+		giveWebGDPREnabled *bool
+		wantEnabled        *bool
 	}{
 		{
-			description:                      "GDPR Web channel enabled, general GDPR disabled",
-			giveChannelType:                  ChannelWeb,
-			giveGDPREnabled:                  &falseValue,
-			giveWebGDPREnabled:               &trueValue,
-			giveWebGDPREnabledForIntegration: nil,
-			wantEnabled:                      &trueValue,
+			description:        "GDPR Web channel enabled, general GDPR disabled",
+			giveChannelType:    ChannelWeb,
+			giveGDPREnabled:    &falseValue,
+			giveWebGDPREnabled: &trueValue,
+			wantEnabled:        &trueValue,
 		},
 		{
-			description:                      "GDPR Web channel disabled, general GDPR enabled",
-			giveChannelType:                  ChannelWeb,
-			giveGDPREnabled:                  &trueValue,
-			giveWebGDPREnabled:               &falseValue,
-			giveWebGDPREnabledForIntegration: nil,
-			wantEnabled:                      &falseValue,
+			description:        "GDPR Web channel disabled, general GDPR enabled",
+			giveChannelType:    ChannelWeb,
+			giveGDPREnabled:    &trueValue,
+			giveWebGDPREnabled: &falseValue,
+			wantEnabled:        &falseValue,
 		},
 		{
-			description:                      "GDPR Web channel unspecified, general GDPR disabled",
-			giveChannelType:                  ChannelWeb,
-			giveGDPREnabled:                  &falseValue,
-			giveWebGDPREnabled:               nil,
-			giveWebGDPREnabledForIntegration: nil,
-			wantEnabled:                      &falseValue,
+			description:        "GDPR Web channel unspecified, general GDPR disabled",
+			giveChannelType:    ChannelWeb,
+			giveGDPREnabled:    &falseValue,
+			giveWebGDPREnabled: nil,
+			wantEnabled:        &falseValue,
 		},
 		{
-			description:                      "GDPR Web channel unspecified, general GDPR enabled",
-			giveChannelType:                  ChannelWeb,
-			giveGDPREnabled:                  &trueValue,
-			giveWebGDPREnabled:               nil,
-			giveWebGDPREnabledForIntegration: nil,
-			wantEnabled:                      &trueValue,
+			description:        "GDPR Web channel unspecified, general GDPR enabled",
+			giveChannelType:    ChannelWeb,
+			giveGDPREnabled:    &trueValue,
+			giveWebGDPREnabled: nil,
+			wantEnabled:        &trueValue,
 		},
 		{
-			description:                      "GDPR Web channel unspecified, general GDPR unspecified",
-			giveChannelType:                  ChannelWeb,
-			giveGDPREnabled:                  nil,
-			giveWebGDPREnabled:               nil,
-			giveWebGDPREnabledForIntegration: nil,
-			wantEnabled:                      nil,
-		},
-		{
-			description:                      "Inegration Enabled is set, and channel enabled isn't",
-			giveChannelType:                  ChannelWeb,
-			giveGDPREnabled:                  &falseValue,
-			giveWebGDPREnabled:               nil,
-			giveWebGDPREnabledForIntegration: &trueValue,
-			wantEnabled:                      &trueValue,
-		},
-		{
-			description:                      "Inegration Enabled is set, and channel enabled is set, channel should have precedence",
-			giveChannelType:                  ChannelWeb,
-			giveGDPREnabled:                  &falseValue,
-			giveWebGDPREnabled:               &trueValue,
-			giveWebGDPREnabledForIntegration: &falseValue,
-			wantEnabled:                      &trueValue,
+			description:        "GDPR Web channel unspecified, general GDPR unspecified",
+			giveChannelType:    ChannelWeb,
+			giveGDPREnabled:    nil,
+			giveWebGDPREnabled: nil,
+			wantEnabled:        nil,
 		},
 	}
 
@@ -86,9 +64,6 @@ func TestAccountGDPREnabledForChannelType(t *testing.T) {
 				ChannelEnabled: AccountChannel{
 					Web: tt.giveWebGDPREnabled,
 				},
-				IntegrationEnabled: AccountChannel{
-					Web: tt.giveWebGDPREnabledForIntegration,
-				},
 			},
 		}
 
@@ -107,68 +82,46 @@ func TestAccountCCPAEnabledForChannelType(t *testing.T) {
 	trueValue, falseValue := true, false
 
 	tests := []struct {
-		description                      string
-		giveChannelType                  ChannelType
-		giveCCPAEnabled                  *bool
-		giveWebCCPAEnabled               *bool
-		giveWebCCPAEnabledForIntegration *bool
-		wantEnabled                      *bool
+		description        string
+		giveChannelType    ChannelType
+		giveCCPAEnabled    *bool
+		giveWebCCPAEnabled *bool
+		wantEnabled        *bool
 	}{
 		{
-			description:                      "CCPA Web channel enabled, general CCPA disabled",
-			giveChannelType:                  ChannelWeb,
-			giveCCPAEnabled:                  &falseValue,
-			giveWebCCPAEnabled:               &trueValue,
-			giveWebCCPAEnabledForIntegration: nil,
-			wantEnabled:                      &trueValue,
-		},
-		{
-			description:                      "CCPA Web channel disabled, general CCPA enabled",
-			giveChannelType:                  ChannelWeb,
-			giveCCPAEnabled:                  &trueValue,
-			giveWebCCPAEnabled:               &falseValue,
-			giveWebCCPAEnabledForIntegration: nil,
-			wantEnabled:                      &falseValue,
+			description:        "CCPA Web channel enabled, general CCPA disabled",
+			giveChannelType:    ChannelWeb,
+			giveCCPAEnabled:    &falseValue,
+			giveWebCCPAEnabled: &trueValue,
+			wantEnabled:        &trueValue,
 		},
 		{
-			description:                      "CCPA Web channel unspecified, general CCPA disabled",
-			giveChannelType:                  ChannelWeb,
-			giveCCPAEnabled:                  &falseValue,
-			giveWebCCPAEnabled:               nil,
-			giveWebCCPAEnabledForIntegration: nil,
-			wantEnabled:                      &falseValue,
+			description:        "CCPA Web channel disabled, general CCPA enabled",
+			giveChannelType:    ChannelWeb,
+			giveCCPAEnabled:    &trueValue,
+			giveWebCCPAEnabled: &falseValue,
+			wantEnabled:        &falseValue,
 		},
 		{
-			description:                      "CCPA Web channel unspecified, general CCPA enabled",
-			giveChannelType:                  ChannelWeb,
-			giveCCPAEnabled:                  &trueValue,
-			giveWebCCPAEnabled:               nil,
-			giveWebCCPAEnabledForIntegration: nil,
-			wantEnabled:                      &trueValue,
+			description:        "CCPA Web channel unspecified, general CCPA disabled",
+			giveChannelType:    ChannelWeb,
+			giveCCPAEnabled:    &falseValue,
+			giveWebCCPAEnabled: nil,
+			wantEnabled:        &falseValue,
 		},
 		{
-			description:                      "CCPA Web channel unspecified, general CCPA unspecified",
-			giveChannelType:                  ChannelWeb,
-			giveCCPAEnabled:                  nil,
-			giveWebCCPAEnabled:               nil,
-			giveWebCCPAEnabledForIntegration: nil,
-			wantEnabled:                      nil,
+			description:        "CCPA Web channel unspecified, general CCPA enabled",
+			giveChannelType:    ChannelWeb,
+			giveCCPAEnabled:    &trueValue,
+			giveWebCCPAEnabled: nil,
+			wantEnabled:        &trueValue,
 		},
 		{
-			description:                      "Inegration Enabled is set, and channel enabled isn't",
-			giveChannelType:                  ChannelWeb,
-			giveCCPAEnabled:                  &falseValue,
-			giveWebCCPAEnabled:               nil,
-			giveWebCCPAEnabledForIntegration: &trueValue,
-			wantEnabled:                      &trueValue,
-		},
-		{
-			description:                      "Inegration Enabled is set, and channel enabled is set, channel should have precedence",
-			giveChannelType:                  ChannelWeb,
-			giveCCPAEnabled:                  &falseValue,
-			giveWebCCPAEnabled:               &trueValue,
-			giveWebCCPAEnabledForIntegration: &falseValue,
-			wantEnabled:                      &trueValue,
+			description:        "CCPA Web channel unspecified, general CCPA unspecified",
+			giveChannelType:    ChannelWeb,
+			giveCCPAEnabled:    nil,
+			giveWebCCPAEnabled: nil,
+			wantEnabled:        nil,
 		},
 	}
 
@@ -179,9 +132,6 @@ func TestAccountCCPAEnabledForChannelType(t *testing.T) {
 				ChannelEnabled: AccountChannel{
 					Web: tt.giveWebCCPAEnabled,
 				},
-				IntegrationEnabled: AccountChannel{
-					Web: tt.giveWebCCPAEnabledForIntegration,
-				},
 			},
 		}
 
@@ -844,39 +794,6 @@ func TestModulesGetConfig(t *testing.T) {
 	}
 }
 
-func TestAccountChannelIsSet(t *testing.T) {
-	trueBool := true
-	falseBool := false
-
-	testCases := []struct {
-		name                string
-		givenAccountChannel *AccountChannel
-		expected            bool
-	}{
-		{
-			name:                "AccountChannelSetAllFields",
-			givenAccountChannel: &AccountChannel{AMP: &trueBool, App: &falseBool, Video: &falseBool, Web: &falseBool, DOOH: &falseBool},
-			expected:            true,
-		},
-		{
-			name:                "AccountChannelNotSet",
-			givenAccountChannel: &AccountChannel{},
-			expected:            false,
-		},
-		{
-			name:                "AccountChannelSetAmpOnly",
-			givenAccountChannel: &AccountChannel{AMP: &trueBool},
-			expected:            true,
-		},
-	}
-
-	for _, test := range testCases {
-		t.Run(test.name, func(t *testing.T) {
-			assert.Equal(t, test.expected, test.givenAccountChannel.IsSet())
-		})
-	}
-}
-
 func TestAccountPriceFloorsValidate(t *testing.T) {
 
 	tests := []struct {
diff --git a/config/bidderinfo_test.go b/config/bidderinfo_test.go
index 0900421a..7b134314 100644
--- a/config/bidderinfo_test.go
+++ b/config/bidderinfo_test.go
@@ -9,6 +9,7 @@ import (
 
 	"github.com/prebid/prebid-server/openrtb_ext"
 	"github.com/stretchr/testify/assert"
+	"github.com/stretchr/testify/require"
 )
 
 const testInfoFilesPathValid = "./test/bidder-info-valid"
@@ -50,7 +51,6 @@ disabled: false
 extra_info: extra-info
 app_secret: app-secret
 platform_id: 123
-usersync_url: user-url
 userSync:
   key: foo
   default: iframe
@@ -217,7 +217,6 @@ func TestProcessBidderInfo(t *testing.T) {
 							UserMacro:   "UID",
 						},
 					},
-					UserSyncURL: "user-url",
 					XAPI: AdapterXAPI{
 						Username: "uname",
 						Password: "pwd",
@@ -263,7 +262,6 @@ func TestProcessBidderInfo(t *testing.T) {
 					Syncer: &Syncer{
 						Key: "foo",
 					},
-					UserSyncURL: "user-url",
 					XAPI: AdapterXAPI{
 						Username: "uname",
 						Password: "pwd",
@@ -273,6 +271,7 @@ func TestProcessBidderInfo(t *testing.T) {
 			},
 		},
 	}
+
 	for _, test := range testCases {
 		reader := StubInfoReader{test.bidderInfos}
 		bidderInfos, err := processBidderInfos(reader, mockNormalizeBidderName)
@@ -281,9 +280,7 @@ func TestProcessBidderInfo(t *testing.T) {
 		} else {
 			assert.Equal(t, test.expectedBidderInfos, bidderInfos, "incorrect bidder infos for test case: %s", test.description)
 		}
-
 	}
-
 }
 
 func TestProcessAliasBidderInfo(t *testing.T) {
@@ -328,7 +325,6 @@ func TestProcessAliasBidderInfo(t *testing.T) {
 				UserMacro:   "UID",
 			},
 		},
-		UserSyncURL: "user-url",
 		XAPI: AdapterXAPI{
 			Username: "uname",
 			Password: "pwd",
@@ -376,7 +372,6 @@ func TestProcessAliasBidderInfo(t *testing.T) {
 				UserMacro:   "alias-UID",
 			},
 		},
-		UserSyncURL: "alias-user-url",
 		XAPI: AdapterXAPI{
 			Username: "alias-uname",
 			Password: "alias-pwd",
@@ -1493,78 +1488,15 @@ func TestSyncerEndpointOverride(t *testing.T) {
 }
 
 func TestApplyBidderInfoConfigSyncerOverrides(t *testing.T) {
-	var testCases = []struct {
-		description            string
-		givenFsBidderInfos     BidderInfos
-		givenConfigBidderInfos BidderInfos
-		expectedError          string
-		expectedBidderInfos    BidderInfos
-	}{
-		{
-			description:            "Syncer Override",
-			givenFsBidderInfos:     BidderInfos{"a": {Syncer: &Syncer{Key: "original"}}},
-			givenConfigBidderInfos: BidderInfos{"a": {Syncer: &Syncer{Key: "override"}}},
-			expectedBidderInfos:    BidderInfos{"a": {Syncer: &Syncer{Key: "override"}}},
-		},
-		{
-			description:            "UserSyncURL Override IFrame",
-			givenFsBidderInfos:     BidderInfos{"a": {Syncer: &Syncer{IFrame: &SyncerEndpoint{URL: "original"}}}},
-			givenConfigBidderInfos: BidderInfos{"a": {UserSyncURL: "override"}},
-			expectedBidderInfos:    BidderInfos{"a": {UserSyncURL: "override", Syncer: &Syncer{IFrame: &SyncerEndpoint{URL: "override"}}}},
-		},
-		{
-			description:            "UserSyncURL Supports IFrame",
-			givenFsBidderInfos:     BidderInfos{"a": {Syncer: &Syncer{Supports: []string{"iframe"}}}},
-			givenConfigBidderInfos: BidderInfos{"a": {UserSyncURL: "override"}},
-			expectedBidderInfos:    BidderInfos{"a": {UserSyncURL: "override", Syncer: &Syncer{Supports: []string{"iframe"}, IFrame: &SyncerEndpoint{URL: "override"}}}},
-		},
-		{
-			description:            "UserSyncURL Override Redirect",
-			givenFsBidderInfos:     BidderInfos{"a": {Syncer: &Syncer{Supports: []string{"redirect"}}}},
-			givenConfigBidderInfos: BidderInfos{"a": {UserSyncURL: "override"}},
-			expectedBidderInfos:    BidderInfos{"a": {UserSyncURL: "override", Syncer: &Syncer{Supports: []string{"redirect"}, Redirect: &SyncerEndpoint{URL: "override"}}}},
-		},
-		{
-			description:            "UserSyncURL Supports Redirect",
-			givenFsBidderInfos:     BidderInfos{"a": {Syncer: &Syncer{Redirect: &SyncerEndpoint{URL: "original"}}}},
-			givenConfigBidderInfos: BidderInfos{"a": {UserSyncURL: "override"}},
-			expectedBidderInfos:    BidderInfos{"a": {UserSyncURL: "override", Syncer: &Syncer{Redirect: &SyncerEndpoint{URL: "override"}}}},
-		},
-		{
-			description:            "UserSyncURL Override Syncer Not Defined",
-			givenFsBidderInfos:     BidderInfos{"a": {}},
-			givenConfigBidderInfos: BidderInfos{"a": {UserSyncURL: "override"}},
-			expectedError:          "adapters.a.usersync_url cannot be applied, bidder does not define a user sync",
-		},
-		{
-			description:            "UserSyncURL Override Syncer Endpoints Not Defined",
-			givenFsBidderInfos:     BidderInfos{"a": {Syncer: &Syncer{}}},
-			givenConfigBidderInfos: BidderInfos{"a": {UserSyncURL: "override"}},
-			expectedError:          "adapters.a.usersync_url cannot be applied, bidder does not define user sync endpoints and does not define supported endpoints",
-		},
-		{
-			description:            "UserSyncURL Override Ambiguous",
-			givenFsBidderInfos:     BidderInfos{"a": {Syncer: &Syncer{IFrame: &SyncerEndpoint{URL: "originalIFrame"}, Redirect: &SyncerEndpoint{URL: "originalRedirect"}}}},
-			givenConfigBidderInfos: BidderInfos{"a": {UserSyncURL: "override"}},
-			expectedError:          "adapters.a.usersync_url cannot be applied, bidder defines multiple user sync endpoints or supports multiple endpoints",
-		},
-		{
-			description:            "UserSyncURL Supports Ambiguous",
-			givenFsBidderInfos:     BidderInfos{"a": {Syncer: &Syncer{Supports: []string{"iframe", "redirect"}}}},
-			givenConfigBidderInfos: BidderInfos{"a": {UserSyncURL: "override"}},
-			expectedError:          "adapters.a.usersync_url cannot be applied, bidder defines multiple user sync endpoints or supports multiple endpoints",
-		},
-	}
+	var (
+		givenFileSystem = BidderInfos{"a": {Syncer: &Syncer{Key: "original"}}}
+		givenConfig     = BidderInfos{"a": {Syncer: &Syncer{Key: "override"}}}
+		expected        = BidderInfos{"a": {Syncer: &Syncer{Key: "override"}}}
+	)
 
-	for _, test := range testCases {
-		bidderInfos, resultErr := applyBidderInfoConfigOverrides(test.givenConfigBidderInfos, test.givenFsBidderInfos, mockNormalizeBidderName)
-		if test.expectedError == "" {
-			assert.NoError(t, resultErr, test.description+":err")
-			assert.Equal(t, test.expectedBidderInfos, bidderInfos, test.description+":result")
-		} else {
-			assert.EqualError(t, resultErr, test.expectedError, test.description+":err")
-		}
-	}
+	result, resultErr := applyBidderInfoConfigOverrides(givenConfig, givenFileSystem, mockNormalizeBidderName)
+	assert.NoError(t, resultErr)
+	assert.Equal(t, expected, result)
 }
 
 func TestApplyBidderInfoConfigOverrides(t *testing.T) {
@@ -1754,10 +1686,12 @@ func TestApplyBidderInfoConfigOverridesInvalid(t *testing.T) {
 func TestReadFullYamlBidderConfig(t *testing.T) {
 	bidder := "bidderA"
 	bidderInf := BidderInfo{}
+
 	err := yaml.Unmarshal([]byte(fullBidderYAMLConfig), &bidderInf)
-	actualBidderInfo, err := applyBidderInfoConfigOverrides(BidderInfos{bidder: bidderInf}, BidderInfos{bidder: {Syncer: &Syncer{Supports: []string{"iframe"}}}}, mockNormalizeBidderName)
+	require.NoError(t, err)
 
-	assert.NoError(t, err, "Error wasn't expected")
+	actualBidderInfo, err := applyBidderInfoConfigOverrides(BidderInfos{bidder: bidderInf}, BidderInfos{bidder: {Syncer: &Syncer{Supports: []string{"iframe"}}}}, mockNormalizeBidderName)
+	require.NoError(t, err)
 
 	expectedBidderInfo := BidderInfos{
 		bidder: {
@@ -1794,11 +1728,10 @@ func TestReadFullYamlBidderConfig(t *testing.T) {
 			ExtraAdapterInfo: "extra-info",
 			AppSecret:        "app-secret",
 			PlatformID:       "123",
-			UserSyncURL:      "user-url",
 			Syncer: &Syncer{
 				Key: "foo",
 				IFrame: &SyncerEndpoint{
-					URL:         "user-url",
+					URL:         "https://foo.com/sync?mode=iframe&r={{.RedirectURL}}",
 					RedirectURL: "https://redirect/setuid/iframe",
 					ExternalURL: "https://iframe.host",
 					UserMacro:   "UID",
diff --git a/config/config_test.go b/config/config_test.go
index 97be28eb..21d1681c 100644
--- a/config/config_test.go
+++ b/config/config_test.go
@@ -32,7 +32,6 @@ var bidderInfos = BidderInfos{
 				MediaTypes: []openrtb_ext.BidType{openrtb_ext.BidTypeBanner},
 			},
 		},
-		UserSyncURL: "http://bidder2.com/usersync",
 	},
 }
 
diff --git a/endpoints/cookie_sync_test.go b/endpoints/cookie_sync_test.go
index 80de2b5f..45f7a75d 100644
--- a/endpoints/cookie_sync_test.go
+++ b/endpoints/cookie_sync_test.go
@@ -42,7 +42,7 @@ func TestNewCookieSyncEndpoint(t *testing.T) {
 		configGDPR        = config.GDPR{HostVendorID: 42}
 		configCCPAEnforce = true
 		metrics           = metrics.MetricsEngineMock{}
-		analytics         = MockAnalytics{}
+		analytics         = MockAnalyticsRunner{}
 		fetcher           = FakeAccountsFetcher{}
 		bidders           = map[string]openrtb_ext.BidderName{"bidderA": openrtb_ext.BidderName("bidderA"), "bidderB": openrtb_ext.BidderName("bidderB")}
 	)
@@ -114,7 +114,7 @@ func TestCookieSyncHandle(t *testing.T) {
 		expectedStatusCode       int
 		expectedBody             string
 		setMetricsExpectations   func(*metrics.MetricsEngineMock)
-		setAnalyticsExpectations func(*MockAnalytics)
+		setAnalyticsExpectations func(*MockAnalyticsRunner)
 	}{
 		{
 			description: "Request With Cookie",
@@ -133,7 +133,7 @@ func TestCookieSyncHandle(t *testing.T) {
 				m.On("RecordCookieSync", metrics.CookieSyncOK).Once()
 				m.On("RecordSyncerRequest", "aSyncer", metrics.SyncerCookieSyncOK).Once()
 			},
-			setAnalyticsExpectations: func(a *MockAnalytics) {
+			setAnalyticsExpectations: func(a *MockAnalyticsRunner) {
 				expected := analytics.CookieSyncObject{
 					Status: 200,
 					Errors: nil,
@@ -165,7 +165,7 @@ func TestCookieSyncHandle(t *testing.T) {
 				m.On("RecordCookieSync", metrics.CookieSyncOK).Once()
 				m.On("RecordSyncerRequest", "aSyncer", metrics.SyncerCookieSyncOK).Once()
 			},
-			setAnalyticsExpectations: func(a *MockAnalytics) {
+			setAnalyticsExpectations: func(a *MockAnalyticsRunner) {
 				expected := analytics.CookieSyncObject{
 					Status: 200,
 					Errors: nil,
@@ -194,7 +194,7 @@ func TestCookieSyncHandle(t *testing.T) {
 			setMetricsExpectations: func(m *metrics.MetricsEngineMock) {
 				m.On("RecordCookieSync", metrics.CookieSyncBadRequest).Once()
 			},
-			setAnalyticsExpectations: func(a *MockAnalytics) {
+			setAnalyticsExpectations: func(a *MockAnalyticsRunner) {
 				expected := analytics.CookieSyncObject{
 					Status:       400,
 					Errors:       []error{errors.New("JSON parsing failed: invalid character 'm' looking for beginning of value")},
@@ -217,7 +217,7 @@ func TestCookieSyncHandle(t *testing.T) {
 			setMetricsExpectations: func(m *metrics.MetricsEngineMock) {
 				m.On("RecordCookieSync", metrics.CookieSyncOptOut).Once()
 			},
-			setAnalyticsExpectations: func(a *MockAnalytics) {
+			setAnalyticsExpectations: func(a *MockAnalyticsRunner) {
 				expected := analytics.CookieSyncObject{
 					Status:       401,
 					Errors:       []error{errors.New("User has opted out")},
@@ -240,7 +240,7 @@ func TestCookieSyncHandle(t *testing.T) {
 			setMetricsExpectations: func(m *metrics.MetricsEngineMock) {
 				m.On("RecordCookieSync", metrics.CookieSyncGDPRHostCookieBlocked).Once()
 			},
-			setAnalyticsExpectations: func(a *MockAnalytics) {
+			setAnalyticsExpectations: func(a *MockAnalyticsRunner) {
 				expected := analytics.CookieSyncObject{
 					Status:       200,
 					Errors:       nil,
@@ -255,7 +255,7 @@ func TestCookieSyncHandle(t *testing.T) {
 		mockMetrics := metrics.MetricsEngineMock{}
 		test.setMetricsExpectations(&mockMetrics)
 
-		mockAnalytics := MockAnalytics{}
+		mockAnalytics := MockAnalyticsRunner{}
 		test.setAnalyticsExpectations(&mockAnalytics)
 
 		fakeAccountFetcher := FakeAccountsFetcher{}
@@ -1248,7 +1248,7 @@ func TestSetCooperativeSync(t *testing.T) {
 func TestWriteParseRequestErrorMetrics(t *testing.T) {
 	err := errors.New("anyError")
 
-	mockAnalytics := MockAnalytics{}
+	mockAnalytics := MockAnalyticsRunner{}
 	mockAnalytics.On("LogCookieSyncObject", mock.Anything)
 	writer := httptest.NewRecorder()
 
@@ -1471,7 +1471,7 @@ func TestParseBidderFilter(t *testing.T) {
 func TestCookieSyncHandleError(t *testing.T) {
 	err := errors.New("anyError")
 
-	mockAnalytics := MockAnalytics{}
+	mockAnalytics := MockAnalyticsRunner{}
 	mockAnalytics.On("LogCookieSyncObject", mock.Anything)
 	writer := httptest.NewRecorder()
 
@@ -1664,7 +1664,7 @@ func TestCookieSyncHandleResponse(t *testing.T) {
 	}
 
 	for _, test := range testCases {
-		mockAnalytics := MockAnalytics{}
+		mockAnalytics := MockAnalyticsRunner{}
 		mockAnalytics.On("LogCookieSyncObject", &test.expectedAnalytics).Once()
 
 		cookie := usersync.NewCookie()
@@ -1996,32 +1996,32 @@ func (m *MockSyncer) GetSync(syncTypes []usersync.SyncType, privacyMacros macros
 	return args.Get(0).(usersync.Sync), args.Error(1)
 }
 
-type MockAnalytics struct {
+type MockAnalyticsRunner struct {
 	mock.Mock
 }
 
-func (m *MockAnalytics) LogAuctionObject(obj *analytics.AuctionObject) {
-	m.Called(obj)
+func (m *MockAnalyticsRunner) LogAuctionObject(obj *analytics.AuctionObject, ac privacy.ActivityControl) {
+	m.Called(obj, ac)
 }
 
-func (m *MockAnalytics) LogVideoObject(obj *analytics.VideoObject) {
-	m.Called(obj)
+func (m *MockAnalyticsRunner) LogVideoObject(obj *analytics.VideoObject, ac privacy.ActivityControl) {
+	m.Called(obj, ac)
 }
 
-func (m *MockAnalytics) LogCookieSyncObject(obj *analytics.CookieSyncObject) {
+func (m *MockAnalyticsRunner) LogCookieSyncObject(obj *analytics.CookieSyncObject) {
 	m.Called(obj)
 }
 
-func (m *MockAnalytics) LogSetUIDObject(obj *analytics.SetUIDObject) {
+func (m *MockAnalyticsRunner) LogSetUIDObject(obj *analytics.SetUIDObject) {
 	m.Called(obj)
 }
 
-func (m *MockAnalytics) LogAmpObject(obj *analytics.AmpObject) {
-	m.Called(obj)
+func (m *MockAnalyticsRunner) LogAmpObject(obj *analytics.AmpObject, ac privacy.ActivityControl) {
+	m.Called(obj, ac)
 }
 
-func (m *MockAnalytics) LogNotificationEventObject(obj *analytics.NotificationEvent) {
-	m.Called(obj)
+func (m *MockAnalyticsRunner) LogNotificationEventObject(obj *analytics.NotificationEvent, ac privacy.ActivityControl) {
+	m.Called(obj, ac)
 }
 
 type MockGDPRPerms struct {
diff --git a/endpoints/events/event_test.go b/endpoints/events/event_test.go
index 7f129634..d721187b 100644
--- a/endpoints/events/event_test.go
+++ b/endpoints/events/event_test.go
@@ -16,6 +16,7 @@ import (
 	"github.com/prebid/prebid-server/config"
 	"github.com/prebid/prebid-server/errortypes"
 	"github.com/prebid/prebid-server/metrics"
+	"github.com/prebid/prebid-server/privacy"
 	"github.com/prebid/prebid-server/stored_requests"
 	"github.com/stretchr/testify/assert"
 )
@@ -26,13 +27,13 @@ type eventsMockAnalyticsModule struct {
 	Invoked bool
 }
 
-func (e *eventsMockAnalyticsModule) LogAuctionObject(ao *analytics.AuctionObject) {
+func (e *eventsMockAnalyticsModule) LogAuctionObject(ao *analytics.AuctionObject, _ privacy.ActivityControl) {
 	if e.Fail {
 		panic(e.Error)
 	}
 }
 
-func (e *eventsMockAnalyticsModule) LogVideoObject(vo *analytics.VideoObject) {
+func (e *eventsMockAnalyticsModule) LogVideoObject(vo *analytics.VideoObject, _ privacy.ActivityControl) {
 	if e.Fail {
 		panic(e.Error)
 	}
@@ -50,13 +51,13 @@ func (e *eventsMockAnalyticsModule) LogSetUIDObject(so *analytics.SetUIDObject)
 	}
 }
 
-func (e *eventsMockAnalyticsModule) LogAmpObject(ao *analytics.AmpObject) {
+func (e *eventsMockAnalyticsModule) LogAmpObject(ao *analytics.AmpObject, _ privacy.ActivityControl) {
 	if e.Fail {
 		panic(e.Error)
 	}
 }
 
-func (e *eventsMockAnalyticsModule) LogNotificationEventObject(ne *analytics.NotificationEvent) {
+func (e *eventsMockAnalyticsModule) LogNotificationEventObject(ne *analytics.NotificationEvent, _ privacy.ActivityControl) {
 	if e.Fail {
 		panic(e.Error)
 	}
diff --git a/endpoints/openrtb2/amp_auction_test.go b/endpoints/openrtb2/amp_auction_test.go
index 89d9c472..3c0feb4f 100644
--- a/endpoints/openrtb2/amp_auction_test.go
+++ b/endpoints/openrtb2/amp_auction_test.go
@@ -20,7 +20,7 @@ import (
 
 	"github.com/prebid/prebid-server/amp"
 	"github.com/prebid/prebid-server/analytics"
-	analyticsConf "github.com/prebid/prebid-server/analytics/config"
+	analyticsBuild "github.com/prebid/prebid-server/analytics/build"
 	"github.com/prebid/prebid-server/config"
 	"github.com/prebid/prebid-server/errortypes"
 	"github.com/prebid/prebid-server/exchange"
@@ -30,6 +30,7 @@ import (
 	"github.com/prebid/prebid-server/metrics"
 	metricsConfig "github.com/prebid/prebid-server/metrics/config"
 	"github.com/prebid/prebid-server/openrtb_ext"
+	"github.com/prebid/prebid-server/privacy"
 	"github.com/prebid/prebid-server/stored_requests/backends/empty_fetcher"
 )
 
@@ -201,7 +202,7 @@ func TestAMPPageInfo(t *testing.T) {
 		empty_fetcher.EmptyFetcher{},
 		&config.Configuration{MaxRequestSize: maxSize},
 		&metricsConfig.NilMetricsEngine{},
-		analyticsConf.NewPBSAnalytics(&config.Analytics{}),
+		analyticsBuild.New(&config.Analytics{}),
 		map[string]string{},
 		[]byte{},
 		openrtb_ext.BuildBidderMap(),
@@ -305,7 +306,7 @@ func TestGDPRConsent(t *testing.T) {
 				GDPR:           config.GDPR{Enabled: true},
 			},
 			&metricsConfig.NilMetricsEngine{},
-			analyticsConf.NewPBSAnalytics(&config.Analytics{}),
+			analyticsBuild.New(&config.Analytics{}),
 			map[string]string{},
 			[]byte{},
 			openrtb_ext.BuildBidderMap(),
@@ -729,7 +730,7 @@ func TestCCPAConsent(t *testing.T) {
 			empty_fetcher.EmptyFetcher{},
 			&config.Configuration{MaxRequestSize: maxSize},
 			&metricsConfig.NilMetricsEngine{},
-			analyticsConf.NewPBSAnalytics(&config.Analytics{}),
+			analyticsBuild.New(&config.Analytics{}),
 			map[string]string{},
 			[]byte{},
 			openrtb_ext.BuildBidderMap(),
@@ -843,7 +844,7 @@ func TestConsentWarnings(t *testing.T) {
 			empty_fetcher.EmptyFetcher{},
 			&config.Configuration{MaxRequestSize: maxSize},
 			&metricsConfig.NilMetricsEngine{},
-			analyticsConf.NewPBSAnalytics(&config.Analytics{}),
+			analyticsBuild.New(&config.Analytics{}),
 			map[string]string{},
 			[]byte{},
 			openrtb_ext.BuildBidderMap(),
@@ -942,7 +943,7 @@ func TestNewAndLegacyConsentBothProvided(t *testing.T) {
 				GDPR:           config.GDPR{Enabled: true},
 			},
 			&metricsConfig.NilMetricsEngine{},
-			analyticsConf.NewPBSAnalytics(&config.Analytics{}),
+			analyticsBuild.New(&config.Analytics{}),
 			map[string]string{},
 			[]byte{},
 			openrtb_ext.BuildBidderMap(),
@@ -997,7 +998,7 @@ func TestAMPSiteExt(t *testing.T) {
 		empty_fetcher.EmptyFetcher{},
 		&config.Configuration{MaxRequestSize: maxSize},
 		&metricsConfig.NilMetricsEngine{},
-		analyticsConf.NewPBSAnalytics(&config.Analytics{}),
+		analyticsBuild.New(&config.Analytics{}),
 		nil,
 		nil,
 		openrtb_ext.BuildBidderMap(),
@@ -1040,7 +1041,7 @@ func TestAmpBadRequests(t *testing.T) {
 		empty_fetcher.EmptyFetcher{},
 		&config.Configuration{MaxRequestSize: maxSize},
 		&metricsConfig.NilMetricsEngine{},
-		analyticsConf.NewPBSAnalytics(&config.Analytics{}),
+		analyticsBuild.New(&config.Analytics{}),
 		map[string]string{},
 		[]byte{},
 		openrtb_ext.BuildBidderMap(),
@@ -1074,7 +1075,7 @@ func TestAmpDebug(t *testing.T) {
 		empty_fetcher.EmptyFetcher{},
 		&config.Configuration{MaxRequestSize: maxSize},
 		&metricsConfig.NilMetricsEngine{},
-		analyticsConf.NewPBSAnalytics(&config.Analytics{}),
+		analyticsBuild.New(&config.Analytics{}),
 		map[string]string{},
 		[]byte{},
 		openrtb_ext.BuildBidderMap(),
@@ -1210,7 +1211,7 @@ func TestQueryParamOverrides(t *testing.T) {
 		empty_fetcher.EmptyFetcher{},
 		&config.Configuration{MaxRequestSize: maxSize},
 		&metricsConfig.NilMetricsEngine{},
-		analyticsConf.NewPBSAnalytics(&config.Analytics{}),
+		analyticsBuild.New(&config.Analytics{}),
 		map[string]string{},
 		[]byte{},
 		openrtb_ext.BuildBidderMap(),
@@ -1368,7 +1369,7 @@ func (s formatOverrideSpec) execute(t *testing.T) {
 		empty_fetcher.EmptyFetcher{},
 		&config.Configuration{MaxRequestSize: maxSize},
 		&metricsConfig.NilMetricsEngine{},
-		analyticsConf.NewPBSAnalytics(&config.Analytics{}),
+		analyticsBuild.New(&config.Analytics{}),
 		map[string]string{},
 		[]byte{},
 		openrtb_ext.BuildBidderMap(),
@@ -1631,25 +1632,25 @@ type mockLogger struct {
 	auctionObject *analytics.AuctionObject
 }
 
-func newMockLogger(ao *analytics.AmpObject, aucObj *analytics.AuctionObject) analytics.PBSAnalyticsModule {
+func newMockLogger(ao *analytics.AmpObject, aucObj *analytics.AuctionObject) analytics.Runner {
 	return &mockLogger{
 		ampObject:     ao,
 		auctionObject: aucObj,
 	}
 }
 
-func (logger mockLogger) LogAuctionObject(ao *analytics.AuctionObject) {
+func (logger mockLogger) LogAuctionObject(ao *analytics.AuctionObject, _ privacy.ActivityControl) {
 	*logger.auctionObject = *ao
 }
-func (logger mockLogger) LogVideoObject(vo *analytics.VideoObject) {
+func (logger mockLogger) LogVideoObject(vo *analytics.VideoObject, _ privacy.ActivityControl) {
 }
 func (logger mockLogger) LogCookieSyncObject(cookieObject *analytics.CookieSyncObject) {
 }
 func (logger mockLogger) LogSetUIDObject(uuidObj *analytics.SetUIDObject) {
 }
-func (logger mockLogger) LogNotificationEventObject(uuidObj *analytics.NotificationEvent) {
+func (logger mockLogger) LogNotificationEventObject(uuidObj *analytics.NotificationEvent, _ privacy.ActivityControl) {
 }
-func (logger mockLogger) LogAmpObject(ao *analytics.AmpObject) {
+func (logger mockLogger) LogAmpObject(ao *analytics.AmpObject, _ privacy.ActivityControl) {
 	*logger.ampObject = *ao
 }
 
@@ -1962,7 +1963,7 @@ func TestAmpAuctionResponseHeaders(t *testing.T) {
 		empty_fetcher.EmptyFetcher{},
 		&config.Configuration{MaxRequestSize: maxSize},
 		&metricsConfig.NilMetricsEngine{},
-		analyticsConf.NewPBSAnalytics(&config.Analytics{}),
+		analyticsBuild.New(&config.Analytics{}),
 		map[string]string{},
 		[]byte{},
 		openrtb_ext.BuildBidderMap(),
@@ -1998,7 +1999,7 @@ func TestRequestWithTargeting(t *testing.T) {
 		empty_fetcher.EmptyFetcher{},
 		&config.Configuration{MaxRequestSize: maxSize},
 		&metricsConfig.NilMetricsEngine{},
-		analyticsConf.NewPBSAnalytics(&config.Analytics{}),
+		analyticsBuild.New(&config.Analytics{}),
 		nil,
 		nil,
 		openrtb_ext.BuildBidderMap(),
diff --git a/endpoints/openrtb2/auction_benchmark_test.go b/endpoints/openrtb2/auction_benchmark_test.go
index ee74548e..f3e82dca 100644
--- a/endpoints/openrtb2/auction_benchmark_test.go
+++ b/endpoints/openrtb2/auction_benchmark_test.go
@@ -10,7 +10,7 @@ import (
 	"testing"
 	"time"
 
-	analyticsConf "github.com/prebid/prebid-server/analytics/config"
+	analyticsBuild "github.com/prebid/prebid-server/analytics/build"
 	"github.com/prebid/prebid-server/config"
 	"github.com/prebid/prebid-server/currency"
 	"github.com/prebid/prebid-server/exchange"
@@ -105,7 +105,7 @@ func BenchmarkOpenrtbEndpoint(b *testing.B) {
 		empty_fetcher.EmptyFetcher{},
 		&config.Configuration{MaxRequestSize: maxSize},
 		nilMetrics,
-		analyticsConf.NewPBSAnalytics(&config.Analytics{}),
+		analyticsBuild.New(&config.Analytics{}),
 		map[string]string{},
 		[]byte{},
 		nil,
diff --git a/endpoints/openrtb2/auction_test.go b/endpoints/openrtb2/auction_test.go
index b5631c93..8204a80e 100644
--- a/endpoints/openrtb2/auction_test.go
+++ b/endpoints/openrtb2/auction_test.go
@@ -23,7 +23,7 @@ import (
 	nativeRequests "github.com/prebid/openrtb/v19/native1/request"
 	"github.com/prebid/openrtb/v19/openrtb2"
 	"github.com/prebid/prebid-server/analytics"
-	analyticsConf "github.com/prebid/prebid-server/analytics/config"
+	analyticsBuild "github.com/prebid/prebid-server/analytics/build"
 	"github.com/prebid/prebid-server/config"
 	"github.com/prebid/prebid-server/errortypes"
 	"github.com/prebid/prebid-server/exchange"
@@ -447,7 +447,7 @@ func TestExplicitUserId(t *testing.T) {
 		empty_fetcher.EmptyFetcher{},
 		cfg,
 		&metricsConfig.NilMetricsEngine{},
-		analyticsConf.NewPBSAnalytics(&config.Analytics{}),
+		analyticsBuild.New(&config.Analytics{}),
 		map[string]string{},
 		[]byte{},
 		openrtb_ext.BuildBidderMap(),
@@ -505,7 +505,7 @@ func doBadAliasRequest(t *testing.T, filename string, expectMsg string) {
 		empty_fetcher.EmptyFetcher{},
 		&config.Configuration{MaxRequestSize: maxSize},
 		&metricsConfig.NilMetricsEngine{},
-		analyticsConf.NewPBSAnalytics(&config.Analytics{}),
+		analyticsBuild.New(&config.Analytics{}),
 		disabledBidders,
 		aliasJSON,
 		bidderMap,
@@ -560,7 +560,7 @@ func TestNilExchange(t *testing.T) {
 		empty_fetcher.EmptyFetcher{},
 		&config.Configuration{MaxRequestSize: maxSize},
 		&metricsConfig.NilMetricsEngine{},
-		analyticsConf.NewPBSAnalytics(&config.Analytics{}), map[string]string{},
+		analyticsBuild.New(&config.Analytics{}), map[string]string{},
 		[]byte{},
 		openrtb_ext.BuildBidderMap(),
 		empty_fetcher.EmptyFetcher{},
@@ -585,7 +585,7 @@ func TestNilValidator(t *testing.T) {
 		empty_fetcher.EmptyFetcher{},
 		&config.Configuration{MaxRequestSize: maxSize},
 		&metricsConfig.NilMetricsEngine{},
-		analyticsConf.NewPBSAnalytics(&config.Analytics{}),
+		analyticsBuild.New(&config.Analytics{}),
 		map[string]string{},
 		[]byte{},
 		openrtb_ext.BuildBidderMap(),
@@ -611,7 +611,7 @@ func TestExchangeError(t *testing.T) {
 		empty_fetcher.EmptyFetcher{},
 		&config.Configuration{MaxRequestSize: maxSize},
 		&metricsConfig.NilMetricsEngine{},
-		analyticsConf.NewPBSAnalytics(&config.Analytics{}),
+		analyticsBuild.New(&config.Analytics{}),
 		map[string]string{},
 		[]byte{},
 		openrtb_ext.BuildBidderMap(),
@@ -738,7 +738,7 @@ func TestImplicitIPsEndToEnd(t *testing.T) {
 			empty_fetcher.EmptyFetcher{},
 			cfg,
 			&metricsConfig.NilMetricsEngine{},
-			analyticsConf.NewPBSAnalytics(&config.Analytics{}),
+			analyticsBuild.New(&config.Analytics{}),
 			map[string]string{},
 			[]byte{},
 			openrtb_ext.BuildBidderMap(),
@@ -938,7 +938,7 @@ func TestImplicitDNTEndToEnd(t *testing.T) {
 			empty_fetcher.EmptyFetcher{},
 			&config.Configuration{MaxRequestSize: maxSize},
 			&metricsConfig.NilMetricsEngine{},
-			analyticsConf.NewPBSAnalytics(&config.Analytics{}),
+			analyticsBuild.New(&config.Analytics{}),
 			map[string]string{},
 			[]byte{},
 			openrtb_ext.BuildBidderMap(),
@@ -1175,7 +1175,7 @@ func TestStoredRequests(t *testing.T) {
 		empty_fetcher.EmptyFetcher{},
 		&config.Configuration{MaxRequestSize: maxSize},
 		&metricsConfig.NilMetricsEngine{},
-		analyticsConf.NewPBSAnalytics(&config.Analytics{}),
+		analyticsBuild.New(&config.Analytics{}),
 		map[string]string{},
 		false,
 		[]byte{},
@@ -1186,6 +1186,7 @@ func TestStoredRequests(t *testing.T) {
 		empty_fetcher.EmptyFetcher{},
 		hooks.EmptyPlanBuilder{},
 		nil,
+		openrtb_ext.NormalizeBidderName,
 	}
 
 	testStoreVideoAttr := []bool{true, true, false, false, false}
@@ -1624,7 +1625,7 @@ func TestValidateRequest(t *testing.T) {
 		empty_fetcher.EmptyFetcher{},
 		&config.Configuration{MaxRequestSize: maxSize},
 		&metricsConfig.NilMetricsEngine{},
-		analyticsConf.NewPBSAnalytics(&config.Analytics{}),
+		analyticsBuild.New(&config.Analytics{}),
 		map[string]string{},
 		false,
 		[]byte{},
@@ -1635,6 +1636,7 @@ func TestValidateRequest(t *testing.T) {
 		empty_fetcher.EmptyFetcher{},
 		hooks.EmptyPlanBuilder{},
 		nil,
+		openrtb_ext.NormalizeBidderName,
 	}
 
 	testCases := []struct {
@@ -2403,7 +2405,7 @@ func TestSetIntegrationType(t *testing.T) {
 		empty_fetcher.EmptyFetcher{},
 		&config.Configuration{},
 		&metricsConfig.NilMetricsEngine{},
-		analyticsConf.NewPBSAnalytics(&config.Analytics{}),
+		analyticsBuild.New(&config.Analytics{}),
 		map[string]string{},
 		false,
 		[]byte{},
@@ -2414,6 +2416,7 @@ func TestSetIntegrationType(t *testing.T) {
 		empty_fetcher.EmptyFetcher{},
 		hooks.EmptyPlanBuilder{},
 		nil,
+		openrtb_ext.NormalizeBidderName,
 	}
 
 	testCases := []struct {
@@ -2469,7 +2472,7 @@ func TestStoredRequestGenerateUuid(t *testing.T) {
 		empty_fetcher.EmptyFetcher{},
 		&config.Configuration{MaxRequestSize: maxSize},
 		&metricsConfig.NilMetricsEngine{},
-		analyticsConf.NewPBSAnalytics(&config.Analytics{}),
+		analyticsBuild.New(&config.Analytics{}),
 		map[string]string{},
 		false,
 		[]byte{},
@@ -2480,6 +2483,7 @@ func TestStoredRequestGenerateUuid(t *testing.T) {
 		empty_fetcher.EmptyFetcher{},
 		hooks.EmptyPlanBuilder{},
 		nil,
+		openrtb_ext.NormalizeBidderName,
 	}
 
 	req := &openrtb2.BidRequest{}
@@ -2573,7 +2577,7 @@ func TestOversizedRequest(t *testing.T) {
 		empty_fetcher.EmptyFetcher{},
 		&config.Configuration{MaxRequestSize: int64(len(reqBody) - 1)},
 		&metricsConfig.NilMetricsEngine{},
-		analyticsConf.NewPBSAnalytics(&config.Analytics{}),
+		analyticsBuild.New(&config.Analytics{}),
 		map[string]string{},
 		false,
 		[]byte{},
@@ -2584,6 +2588,7 @@ func TestOversizedRequest(t *testing.T) {
 		empty_fetcher.EmptyFetcher{},
 		hooks.EmptyPlanBuilder{},
 		nil,
+		openrtb_ext.NormalizeBidderName,
 	}
 
 	req := httptest.NewRequest("POST", "/openrtb2/auction", strings.NewReader(reqBody))
@@ -2612,7 +2617,7 @@ func TestRequestSizeEdgeCase(t *testing.T) {
 		empty_fetcher.EmptyFetcher{},
 		&config.Configuration{MaxRequestSize: int64(len(reqBody))},
 		&metricsConfig.NilMetricsEngine{},
-		analyticsConf.NewPBSAnalytics(&config.Analytics{}),
+		analyticsBuild.New(&config.Analytics{}),
 		map[string]string{},
 		false,
 		[]byte{},
@@ -2623,6 +2628,7 @@ func TestRequestSizeEdgeCase(t *testing.T) {
 		empty_fetcher.EmptyFetcher{},
 		hooks.EmptyPlanBuilder{},
 		nil,
+		openrtb_ext.NormalizeBidderName,
 	}
 
 	req := httptest.NewRequest("POST", "/openrtb2/auction", strings.NewReader(reqBody))
@@ -2649,7 +2655,7 @@ func TestNoEncoding(t *testing.T) {
 		empty_fetcher.EmptyFetcher{},
 		&config.Configuration{MaxRequestSize: maxSize},
 		&metricsConfig.NilMetricsEngine{},
-		analyticsConf.NewPBSAnalytics(&config.Analytics{}),
+		analyticsBuild.New(&config.Analytics{}),
 		map[string]string{},
 		[]byte{},
 		openrtb_ext.BuildBidderMap(),
@@ -2734,7 +2740,7 @@ func TestContentType(t *testing.T) {
 		empty_fetcher.EmptyFetcher{},
 		&config.Configuration{MaxRequestSize: maxSize},
 		&metricsConfig.NilMetricsEngine{},
-		analyticsConf.NewPBSAnalytics(&config.Analytics{}),
+		analyticsBuild.New(&config.Analytics{}),
 		map[string]string{},
 		[]byte{},
 		openrtb_ext.BuildBidderMap(),
@@ -2951,7 +2957,7 @@ func TestValidateImpExt(t *testing.T) {
 		empty_fetcher.EmptyFetcher{},
 		&config.Configuration{MaxRequestSize: int64(8096)},
 		&metricsConfig.NilMetricsEngine{},
-		analyticsConf.NewPBSAnalytics(&config.Analytics{}),
+		analyticsBuild.New(&config.Analytics{}),
 		map[string]string{"disabledbidder": "The bidder 'disabledbidder' has been disabled."},
 		false,
 		[]byte{},
@@ -2962,6 +2968,7 @@ func TestValidateImpExt(t *testing.T) {
 		empty_fetcher.EmptyFetcher{},
 		hooks.EmptyPlanBuilder{},
 		nil,
+		openrtb_ext.NormalizeBidderName,
 	}
 
 	for _, group := range testGroups {
@@ -3006,7 +3013,7 @@ func TestCurrencyTrunc(t *testing.T) {
 		empty_fetcher.EmptyFetcher{},
 		&config.Configuration{},
 		&metricsConfig.NilMetricsEngine{},
-		analyticsConf.NewPBSAnalytics(&config.Analytics{}),
+		analyticsBuild.New(&config.Analytics{}),
 		map[string]string{},
 		false,
 		[]byte{},
@@ -3017,6 +3024,7 @@ func TestCurrencyTrunc(t *testing.T) {
 		empty_fetcher.EmptyFetcher{},
 		hooks.EmptyPlanBuilder{},
 		nil,
+		openrtb_ext.NormalizeBidderName,
 	}
 
 	ui := int64(1)
@@ -3054,7 +3062,7 @@ func TestCCPAInvalid(t *testing.T) {
 		empty_fetcher.EmptyFetcher{},
 		&config.Configuration{},
 		&metricsConfig.NilMetricsEngine{},
-		analyticsConf.NewPBSAnalytics(&config.Analytics{}),
+		analyticsBuild.New(&config.Analytics{}),
 		map[string]string{},
 		false,
 		[]byte{},
@@ -3065,6 +3073,7 @@ func TestCCPAInvalid(t *testing.T) {
 		empty_fetcher.EmptyFetcher{},
 		hooks.EmptyPlanBuilder{},
 		nil,
+		openrtb_ext.NormalizeBidderName,
 	}
 
 	ui := int64(1)
@@ -3106,7 +3115,7 @@ func TestNoSaleInvalid(t *testing.T) {
 		empty_fetcher.EmptyFetcher{},
 		&config.Configuration{},
 		&metricsConfig.NilMetricsEngine{},
-		analyticsConf.NewPBSAnalytics(&config.Analytics{}),
+		analyticsBuild.New(&config.Analytics{}),
 		map[string]string{},
 		false,
 		[]byte{},
@@ -3117,6 +3126,7 @@ func TestNoSaleInvalid(t *testing.T) {
 		empty_fetcher.EmptyFetcher{},
 		hooks.EmptyPlanBuilder{},
 		nil,
+		openrtb_ext.NormalizeBidderName,
 	}
 
 	ui := int64(1)
@@ -3161,7 +3171,7 @@ func TestValidateSourceTID(t *testing.T) {
 		empty_fetcher.EmptyFetcher{},
 		cfg,
 		&metricsConfig.NilMetricsEngine{},
-		analyticsConf.NewPBSAnalytics(&config.Analytics{}),
+		analyticsBuild.New(&config.Analytics{}),
 		map[string]string{},
 		false,
 		[]byte{},
@@ -3172,6 +3182,7 @@ func TestValidateSourceTID(t *testing.T) {
 		empty_fetcher.EmptyFetcher{},
 		hooks.EmptyPlanBuilder{},
 		nil,
+		openrtb_ext.NormalizeBidderName,
 	}
 
 	ui := int64(1)
@@ -3206,7 +3217,7 @@ func TestSChainInvalid(t *testing.T) {
 		empty_fetcher.EmptyFetcher{},
 		&config.Configuration{},
 		&metricsConfig.NilMetricsEngine{},
-		analyticsConf.NewPBSAnalytics(&config.Analytics{}),
+		analyticsBuild.New(&config.Analytics{}),
 		map[string]string{},
 		false,
 		[]byte{},
@@ -3217,6 +3228,7 @@ func TestSChainInvalid(t *testing.T) {
 		empty_fetcher.EmptyFetcher{},
 		hooks.EmptyPlanBuilder{},
 		nil,
+		openrtb_ext.NormalizeBidderName,
 	}
 
 	ui := int64(1)
@@ -3774,7 +3786,7 @@ func TestEidPermissionsInvalid(t *testing.T) {
 		empty_fetcher.EmptyFetcher{},
 		&config.Configuration{},
 		&metricsConfig.NilMetricsEngine{},
-		analyticsConf.NewPBSAnalytics(&config.Analytics{}),
+		analyticsBuild.New(&config.Analytics{}),
 		map[string]string{},
 		false,
 		[]byte{},
@@ -3785,6 +3797,7 @@ func TestEidPermissionsInvalid(t *testing.T) {
 		empty_fetcher.EmptyFetcher{},
 		hooks.EmptyPlanBuilder{},
 		nil,
+		openrtb_ext.NormalizeBidderName,
 	}
 
 	ui := int64(1)
@@ -3853,6 +3866,13 @@ func TestValidateEidPermissions(t *testing.T) {
 			}}}},
 			expectedError: nil,
 		},
+		{
+			description: "Valid - One - Case Insensitive",
+			request: &openrtb_ext.ExtRequest{Prebid: openrtb_ext.ExtRequestPrebid{Data: &openrtb_ext.ExtRequestPrebidData{EidPermissions: []openrtb_ext.ExtRequestPrebidDataEidPermission{
+				{Source: "sourceA", Bidders: []string{"A"}},
+			}}}},
+			expectedError: nil,
+		},
 		{
 			description: "Valid - Many",
 			request: &openrtb_ext.ExtRequest{Prebid: openrtb_ext.ExtRequestPrebid{Data: &openrtb_ext.ExtRequestPrebidData{EidPermissions: []openrtb_ext.ExtRequestPrebidDataEidPermission{
@@ -3901,9 +3921,16 @@ func TestValidateEidPermissions(t *testing.T) {
 			}}}},
 			expectedError: errors.New(`request.ext.prebid.data.eidpermissions[1] contains unrecognized bidder "z"`),
 		},
+		{
+			description: "Valid - Alias Case Sensitive",
+			request: &openrtb_ext.ExtRequest{Prebid: openrtb_ext.ExtRequestPrebid{Data: &openrtb_ext.ExtRequestPrebidData{EidPermissions: []openrtb_ext.ExtRequestPrebidDataEidPermission{
+				{Source: "sourceA", Bidders: []string{"B"}},
+			}}}},
+			expectedError: errors.New(`request.ext.prebid.data.eidpermissions[0] contains unrecognized bidder "B"`),
+		},
 	}
 
-	endpoint := &endpointDeps{bidderMap: knownBidders}
+	endpoint := &endpointDeps{bidderMap: knownBidders, normalizeBidderName: fakeNormalizeBidderName}
 	for _, test := range testCases {
 		result := endpoint.validateEidPermissions(test.request.Prebid.Data, knownAliases)
 		assert.Equal(t, test.expectedError, result, test.description)
@@ -3939,6 +3966,13 @@ func TestValidateBidders(t *testing.T) {
 			knownAliases:  map[string]string{"c": "c"},
 			expectedError: nil,
 		},
+		{
+			description:   "Valid - One Core Bidder - Case Insensitive",
+			bidders:       []string{"A"},
+			knownBidders:  map[string]openrtb_ext.BidderName{"a": openrtb_ext.BidderName("a")},
+			knownAliases:  map[string]string{"c": "c"},
+			expectedError: nil,
+		},
 		{
 			description:   "Valid - Many Core Bidders",
 			bidders:       []string{"a", "b"},
@@ -3953,6 +3987,13 @@ func TestValidateBidders(t *testing.T) {
 			knownAliases:  map[string]string{"c": "c"},
 			expectedError: nil,
 		},
+		{
+			description:   "Valid - One Alias Bidder - Case Sensitive",
+			bidders:       []string{"C"},
+			knownBidders:  map[string]openrtb_ext.BidderName{"a": openrtb_ext.BidderName("a")},
+			knownAliases:  map[string]string{"c": "c"},
+			expectedError: errors.New(`unrecognized bidder "C"`),
+		},
 		{
 			description:   "Valid - Many Alias Bidders",
 			bidders:       []string{"c", "d"},
@@ -3974,13 +4015,6 @@ func TestValidateBidders(t *testing.T) {
 			knownAliases:  map[string]string{"c": "c"},
 			expectedError: errors.New(`unrecognized bidder "z"`),
 		},
-		{
-			description:   "Invalid - Unknown Bidder Case Sensitive",
-			bidders:       []string{"A"},
-			knownBidders:  map[string]openrtb_ext.BidderName{"a": openrtb_ext.BidderName("a")},
-			knownAliases:  map[string]string{"c": "c"},
-			expectedError: errors.New(`unrecognized bidder "A"`),
-		},
 		{
 			description:   "Invalid - Unknown Bidder With Known Bidders",
 			bidders:       []string{"a", "c", "z"},
@@ -4011,8 +4045,9 @@ func TestValidateBidders(t *testing.T) {
 		},
 	}
 
+	endpoint := &endpointDeps{normalizeBidderName: fakeNormalizeBidderName}
 	for _, test := range testCases {
-		result := validateBidders(test.bidders, test.knownBidders, test.knownAliases)
+		result := endpoint.validateBidders(test.bidders, test.knownBidders, test.knownAliases)
 		assert.Equal(t, test.expectedError, result, test.description)
 	}
 }
@@ -4028,7 +4063,7 @@ func TestIOS14EndToEnd(t *testing.T) {
 		empty_fetcher.EmptyFetcher{},
 		&config.Configuration{MaxRequestSize: maxSize},
 		&metricsConfig.NilMetricsEngine{},
-		analyticsConf.NewPBSAnalytics(&config.Analytics{}),
+		analyticsBuild.New(&config.Analytics{}),
 		map[string]string{},
 		[]byte{},
 		openrtb_ext.BuildBidderMap(),
@@ -4091,7 +4126,7 @@ func TestAuctionWarnings(t *testing.T) {
 		empty_fetcher.EmptyFetcher{},
 		&config.Configuration{MaxRequestSize: maxSize},
 		&metricsConfig.NilMetricsEngine{},
-		analyticsConf.NewPBSAnalytics(&config.Analytics{}),
+		analyticsBuild.New(&config.Analytics{}),
 		map[string]string{},
 		false,
 		[]byte{},
@@ -4102,6 +4137,7 @@ func TestAuctionWarnings(t *testing.T) {
 		empty_fetcher.EmptyFetcher{},
 		hooks.EmptyPlanBuilder{},
 		nil,
+		openrtb_ext.NormalizeBidderName,
 	}
 	for _, test := range testCases {
 		t.Run(test.name, func(t *testing.T) {
@@ -4137,7 +4173,7 @@ func TestParseRequestParseImpInfoError(t *testing.T) {
 		empty_fetcher.EmptyFetcher{},
 		&config.Configuration{MaxRequestSize: int64(len(reqBody))},
 		&metricsConfig.NilMetricsEngine{},
-		analyticsConf.NewPBSAnalytics(&config.Analytics{}),
+		analyticsBuild.New(&config.Analytics{}),
 		map[string]string{},
 		false,
 		[]byte{},
@@ -4148,6 +4184,7 @@ func TestParseRequestParseImpInfoError(t *testing.T) {
 		empty_fetcher.EmptyFetcher{},
 		hooks.EmptyPlanBuilder{},
 		nil,
+		openrtb_ext.NormalizeBidderName,
 	}
 
 	hookExecutor := hookexecution.NewHookExecutor(deps.hookExecutionPlanBuilder, hookexecution.EndpointAuction, deps.metricsEngine)
@@ -4217,7 +4254,7 @@ func TestParseGzipedRequest(t *testing.T) {
 		empty_fetcher.EmptyFetcher{},
 		&config.Configuration{MaxRequestSize: int64(50), Compression: config.Compression{Request: config.CompressionInfo{GZIP: false}}},
 		&metricsConfig.NilMetricsEngine{},
-		analyticsConf.NewPBSAnalytics(&config.Analytics{}),
+		analyticsBuild.New(&config.Analytics{}),
 		map[string]string{},
 		false,
 		[]byte{},
@@ -4228,6 +4265,7 @@ func TestParseGzipedRequest(t *testing.T) {
 		empty_fetcher.EmptyFetcher{},
 		hooks.EmptyPlanBuilder{},
 		nil,
+		openrtb_ext.NormalizeBidderName,
 	}
 
 	hookExecutor := hookexecution.NewHookExecutor(deps.hookExecutionPlanBuilder, hookexecution.EndpointAuction, deps.metricsEngine)
@@ -4716,7 +4754,7 @@ func TestAuctionResponseHeaders(t *testing.T) {
 		empty_fetcher.EmptyFetcher{},
 		&config.Configuration{MaxRequestSize: maxSize},
 		&metricsConfig.NilMetricsEngine{},
-		analyticsConf.NewPBSAnalytics(&config.Analytics{}),
+		analyticsBuild.New(&config.Analytics{}),
 		map[string]string{},
 		[]byte{},
 		openrtb_ext.BuildBidderMap(),
@@ -4817,7 +4855,7 @@ func TestParseRequestMergeBidderParams(t *testing.T) {
 				empty_fetcher.EmptyFetcher{},
 				&config.Configuration{MaxRequestSize: int64(len(test.givenRequestBody))},
 				&metricsConfig.NilMetricsEngine{},
-				analyticsConf.NewPBSAnalytics(&config.Analytics{}),
+				analyticsBuild.New(&config.Analytics{}),
 				map[string]string{},
 				false,
 				[]byte{},
@@ -4828,6 +4866,7 @@ func TestParseRequestMergeBidderParams(t *testing.T) {
 				empty_fetcher.EmptyFetcher{},
 				hooks.EmptyPlanBuilder{},
 				nil,
+				openrtb_ext.NormalizeBidderName,
 			}
 
 			hookExecutor := hookexecution.NewHookExecutor(deps.hookExecutionPlanBuilder, hookexecution.EndpointAuction, deps.metricsEngine)
@@ -4920,7 +4959,7 @@ func TestParseRequestStoredResponses(t *testing.T) {
 				empty_fetcher.EmptyFetcher{},
 				&config.Configuration{MaxRequestSize: int64(len(test.givenRequestBody))},
 				&metricsConfig.NilMetricsEngine{},
-				analyticsConf.NewPBSAnalytics(&config.Analytics{}),
+				analyticsBuild.New(&config.Analytics{}),
 				map[string]string{},
 				false,
 				[]byte{},
@@ -4931,6 +4970,7 @@ func TestParseRequestStoredResponses(t *testing.T) {
 				&mockStoredResponseFetcher{mockStoredResponses},
 				hooks.EmptyPlanBuilder{},
 				nil,
+				openrtb_ext.NormalizeBidderName,
 			}
 
 			hookExecutor := hookexecution.NewHookExecutor(deps.hookExecutionPlanBuilder, hookexecution.EndpointAuction, deps.metricsEngine)
@@ -5018,7 +5058,7 @@ func TestParseRequestStoredBidResponses(t *testing.T) {
 				empty_fetcher.EmptyFetcher{},
 				&config.Configuration{MaxRequestSize: int64(len(test.givenRequestBody))},
 				&metricsConfig.NilMetricsEngine{},
-				analyticsConf.NewPBSAnalytics(&config.Analytics{}),
+				analyticsBuild.New(&config.Analytics{}),
 				map[string]string{},
 				false,
 				[]byte{},
@@ -5029,6 +5069,7 @@ func TestParseRequestStoredBidResponses(t *testing.T) {
 				&mockStoredResponseFetcher{mockStoredBidResponses},
 				hooks.EmptyPlanBuilder{},
 				nil,
+				openrtb_ext.NormalizeBidderName,
 			}
 
 			hookExecutor := hookexecution.NewHookExecutor(deps.hookExecutionPlanBuilder, hookexecution.EndpointAuction, deps.metricsEngine)
@@ -5054,7 +5095,7 @@ func TestValidateStoredResp(t *testing.T) {
 		empty_fetcher.EmptyFetcher{},
 		&config.Configuration{MaxRequestSize: maxSize},
 		&metricsConfig.NilMetricsEngine{},
-		analyticsConf.NewPBSAnalytics(&config.Analytics{}),
+		analyticsBuild.New(&config.Analytics{}),
 		map[string]string{},
 		false,
 		[]byte{},
@@ -5065,6 +5106,7 @@ func TestValidateStoredResp(t *testing.T) {
 		&mockStoredResponseFetcher{},
 		hooks.EmptyPlanBuilder{},
 		nil,
+		openrtb_ext.NormalizeBidderName,
 	}
 
 	testCases := []struct {
@@ -5867,7 +5909,7 @@ func TestParseRequestMultiBid(t *testing.T) {
 				empty_fetcher.EmptyFetcher{},
 				&config.Configuration{MaxRequestSize: int64(len(test.givenRequestBody))},
 				&metricsConfig.NilMetricsEngine{},
-				analyticsConf.NewPBSAnalytics(&config.Analytics{}),
+				analyticsBuild.New(&config.Analytics{}),
 				map[string]string{},
 				false,
 				[]byte{},
@@ -5878,6 +5920,7 @@ func TestParseRequestMultiBid(t *testing.T) {
 				empty_fetcher.EmptyFetcher{},
 				hooks.EmptyPlanBuilder{},
 				nil,
+				openrtb_ext.NormalizeBidderName,
 			}
 
 			hookExecutor := hookexecution.NewHookExecutor(deps.hookExecutionPlanBuilder, hookexecution.EndpointAuction, deps.metricsEngine)
@@ -6043,3 +6086,7 @@ func TestValidateAliases(t *testing.T) {
 		})
 	}
 }
+
+func fakeNormalizeBidderName(name string) (openrtb_ext.BidderName, bool) {
+	return openrtb_ext.BidderName(strings.ToLower(name)), true
+}
diff --git a/endpoints/openrtb2/test_utils.go b/endpoints/openrtb2/test_utils.go
index 2d22f1e4..365f480e 100644
--- a/endpoints/openrtb2/test_utils.go
+++ b/endpoints/openrtb2/test_utils.go
@@ -20,7 +20,7 @@ import (
 	"github.com/prebid/openrtb/v19/openrtb3"
 	"github.com/prebid/prebid-server/adapters"
 	"github.com/prebid/prebid-server/analytics"
-	analyticsConf "github.com/prebid/prebid-server/analytics/config"
+	analyticsBuild "github.com/prebid/prebid-server/analytics/build"
 	"github.com/prebid/prebid-server/config"
 	"github.com/prebid/prebid-server/currency"
 	"github.com/prebid/prebid-server/errortypes"
@@ -1275,7 +1275,7 @@ func buildTestEndpoint(test testCase, cfg *config.Configuration) (httprouter.Han
 		planBuilder = hooks.EmptyPlanBuilder{}
 	}
 
-	var endpointBuilder func(uuidutil.UUIDGenerator, exchange.Exchange, openrtb_ext.BidderParamValidator, stored_requests.Fetcher, stored_requests.AccountFetcher, *config.Configuration, metrics.MetricsEngine, analytics.PBSAnalyticsModule, map[string]string, []byte, map[string]openrtb_ext.BidderName, stored_requests.Fetcher, hooks.ExecutionPlanBuilder, *exchange.TmaxAdjustmentsPreprocessed) (httprouter.Handle, error)
+	var endpointBuilder func(uuidutil.UUIDGenerator, exchange.Exchange, openrtb_ext.BidderParamValidator, stored_requests.Fetcher, stored_requests.AccountFetcher, *config.Configuration, metrics.MetricsEngine, analytics.Runner, map[string]string, []byte, map[string]openrtb_ext.BidderName, stored_requests.Fetcher, hooks.ExecutionPlanBuilder, *exchange.TmaxAdjustmentsPreprocessed) (httprouter.Handle, error)
 
 	switch test.endpointType {
 	case AMP_ENDPOINT:
@@ -1292,7 +1292,7 @@ func buildTestEndpoint(test testCase, cfg *config.Configuration) (httprouter.Han
 		accountFetcher,
 		cfg,
 		met,
-		analyticsConf.NewPBSAnalytics(&config.Analytics{}),
+		analyticsBuild.New(&config.Analytics{}),
 		disabledBidders,
 		[]byte(test.Config.AliasJSON),
 		bidderMap,
diff --git a/endpoints/openrtb2/video_auction_test.go b/endpoints/openrtb2/video_auction_test.go
index f3135ff4..19af821f 100644
--- a/endpoints/openrtb2/video_auction_test.go
+++ b/endpoints/openrtb2/video_auction_test.go
@@ -12,7 +12,7 @@ import (
 	"testing"
 
 	"github.com/prebid/prebid-server/analytics"
-	analyticsConf "github.com/prebid/prebid-server/analytics/config"
+	analyticsBuild "github.com/prebid/prebid-server/analytics/build"
 	"github.com/prebid/prebid-server/config"
 	"github.com/prebid/prebid-server/errortypes"
 	"github.com/prebid/prebid-server/exchange"
@@ -21,6 +21,7 @@ import (
 	metricsConfig "github.com/prebid/prebid-server/metrics/config"
 	"github.com/prebid/prebid-server/openrtb_ext"
 	"github.com/prebid/prebid-server/prebid_cache_client"
+	"github.com/prebid/prebid-server/privacy"
 	"github.com/prebid/prebid-server/stored_requests/backends/empty_fetcher"
 	"github.com/prebid/prebid-server/util/ptrutil"
 
@@ -1226,6 +1227,7 @@ func mockDepsWithMetrics(t *testing.T, ex *mockExchangeVideo) (*endpointDeps, *m
 		empty_fetcher.EmptyFetcher{},
 		hooks.EmptyPlanBuilder{},
 		nil,
+		openrtb_ext.NormalizeBidderName,
 	}
 	return deps, metrics, mockModule
 }
@@ -1235,11 +1237,11 @@ type mockAnalyticsModule struct {
 	videoObjects   []*analytics.VideoObject
 }
 
-func (m *mockAnalyticsModule) LogAuctionObject(ao *analytics.AuctionObject) {
+func (m *mockAnalyticsModule) LogAuctionObject(ao *analytics.AuctionObject, _ privacy.ActivityControl) {
 	m.auctionObjects = append(m.auctionObjects, ao)
 }
 
-func (m *mockAnalyticsModule) LogVideoObject(vo *analytics.VideoObject) {
+func (m *mockAnalyticsModule) LogVideoObject(vo *analytics.VideoObject, _ privacy.ActivityControl) {
 	m.videoObjects = append(m.videoObjects, vo)
 }
 
@@ -1247,9 +1249,10 @@ func (m *mockAnalyticsModule) LogCookieSyncObject(cso *analytics.CookieSyncObjec
 
 func (m *mockAnalyticsModule) LogSetUIDObject(so *analytics.SetUIDObject) {}
 
-func (m *mockAnalyticsModule) LogAmpObject(ao *analytics.AmpObject) {}
+func (m *mockAnalyticsModule) LogAmpObject(ao *analytics.AmpObject, _ privacy.ActivityControl) {}
 
-func (m *mockAnalyticsModule) LogNotificationEventObject(ne *analytics.NotificationEvent) {}
+func (m *mockAnalyticsModule) LogNotificationEventObject(ne *analytics.NotificationEvent, _ privacy.ActivityControl) {
+}
 
 func mockDeps(t *testing.T, ex *mockExchangeVideo) *endpointDeps {
 	return &endpointDeps{
@@ -1261,7 +1264,7 @@ func mockDeps(t *testing.T, ex *mockExchangeVideo) *endpointDeps {
 		&mockAccountFetcher{data: mockVideoAccountData},
 		&config.Configuration{MaxRequestSize: maxSize},
 		&metricsConfig.NilMetricsEngine{},
-		analyticsConf.NewPBSAnalytics(&config.Analytics{}),
+		analyticsBuild.New(&config.Analytics{}),
 		map[string]string{},
 		false,
 		[]byte{},
@@ -1272,6 +1275,7 @@ func mockDeps(t *testing.T, ex *mockExchangeVideo) *endpointDeps {
 		empty_fetcher.EmptyFetcher{},
 		hooks.EmptyPlanBuilder{},
 		nil,
+		openrtb_ext.NormalizeBidderName,
 	}
 }
 
@@ -1285,7 +1289,7 @@ func mockDepsAppendBidderNames(t *testing.T, ex *mockExchangeAppendBidderNames)
 		empty_fetcher.EmptyFetcher{},
 		&config.Configuration{MaxRequestSize: maxSize},
 		&metricsConfig.NilMetricsEngine{},
-		analyticsConf.NewPBSAnalytics(&config.Analytics{}),
+		analyticsBuild.New(&config.Analytics{}),
 		map[string]string{},
 		false,
 		[]byte{},
@@ -1296,6 +1300,7 @@ func mockDepsAppendBidderNames(t *testing.T, ex *mockExchangeAppendBidderNames)
 		empty_fetcher.EmptyFetcher{},
 		hooks.EmptyPlanBuilder{},
 		nil,
+		openrtb_ext.NormalizeBidderName,
 	}
 
 	return deps
@@ -1311,7 +1316,7 @@ func mockDepsNoBids(t *testing.T, ex *mockExchangeVideoNoBids) *endpointDeps {
 		empty_fetcher.EmptyFetcher{},
 		&config.Configuration{MaxRequestSize: maxSize},
 		&metricsConfig.NilMetricsEngine{},
-		analyticsConf.NewPBSAnalytics(&config.Analytics{}),
+		analyticsBuild.New(&config.Analytics{}),
 		map[string]string{},
 		false,
 		[]byte{},
@@ -1322,6 +1327,7 @@ func mockDepsNoBids(t *testing.T, ex *mockExchangeVideoNoBids) *endpointDeps {
 		empty_fetcher.EmptyFetcher{},
 		hooks.EmptyPlanBuilder{},
 		nil,
+		openrtb_ext.NormalizeBidderName,
 	}
 
 	return edep
diff --git a/endpoints/setuid_test.go b/endpoints/setuid_test.go
index 18099193..387de6bd 100644
--- a/endpoints/setuid_test.go
+++ b/endpoints/setuid_test.go
@@ -13,6 +13,7 @@ import (
 	"time"
 
 	"github.com/prebid/prebid-server/analytics"
+	analyticsBuild "github.com/prebid/prebid-server/analytics/build"
 	"github.com/prebid/prebid-server/config"
 	"github.com/prebid/prebid-server/errortypes"
 	"github.com/prebid/prebid-server/gdpr"
@@ -22,7 +23,6 @@ import (
 	"github.com/prebid/prebid-server/usersync"
 	"github.com/stretchr/testify/assert"
 
-	analyticsConf "github.com/prebid/prebid-server/analytics/config"
 	metricsConf "github.com/prebid/prebid-server/metrics/config"
 )
 
@@ -50,6 +50,16 @@ func TestSetUIDEndpoint(t *testing.T) {
 			expectedHeaders:        map[string]string{"Content-Type": "text/html", "Content-Length": "0"},
 			description:            "Set uid for valid bidder",
 		},
+		{
+			uri:                    "/setuid?bidder=PUBMATIC&uid=123",
+			syncersBidderNameToKey: map[string]string{"pubmatic": "pubmatic"},
+			existingSyncs:          nil,
+			gdprAllowsHostCookies:  true,
+			expectedSyncs:          map[string]string{"pubmatic": "123"},
+			expectedStatusCode:     http.StatusOK,
+			expectedHeaders:        map[string]string{"Content-Type": "text/html", "Content-Length": "0"},
+			description:            "Set uid for valid bidder case insensitive",
+		},
 		{
 			uri:                    "/setuid?bidder=appnexus&uid=123",
 			syncersBidderNameToKey: map[string]string{"appnexus": "adnxs"},
@@ -328,7 +338,7 @@ func TestSetUIDEndpoint(t *testing.T) {
 		},
 	}
 
-	analytics := analyticsConf.NewPBSAnalytics(&config.Analytics{})
+	analytics := analyticsBuild.New(&config.Analytics{})
 	metrics := &metricsConf.NilMetricsEngine{}
 
 	for _, test := range testCases {
@@ -362,7 +372,7 @@ func TestSetUIDEndpoint(t *testing.T) {
 
 func TestSetUIDPriorityEjection(t *testing.T) {
 	decoder := usersync.Base64Decoder{}
-	analytics := analyticsConf.NewPBSAnalytics(&config.Analytics{})
+	analytics := analyticsBuild.New(&config.Analytics{})
 	syncersByBidder := map[string]string{
 		"pubmatic":             "pubmatic",
 		"syncer1":              "syncer1",
@@ -1093,7 +1103,7 @@ func TestSetUIDEndpointMetrics(t *testing.T) {
 		cfgAccountRequired     bool
 		expectedResponseCode   int
 		expectedMetrics        func(*metrics.MetricsEngineMock)
-		expectedAnalytics      func(*MockAnalytics)
+		expectedAnalytics      func(*MockAnalyticsRunner)
 	}{
 		{
 			description:            "Success - Sync",
@@ -1106,7 +1116,7 @@ func TestSetUIDEndpointMetrics(t *testing.T) {
 				m.On("RecordSetUid", metrics.SetUidOK).Once()
 				m.On("RecordSyncerSet", "pubmatic", metrics.SyncerSetUidOK).Once()
 			},
-			expectedAnalytics: func(a *MockAnalytics) {
+			expectedAnalytics: func(a *MockAnalyticsRunner) {
 				expected := analytics.SetUIDObject{
 					Status:  200,
 					Bidder:  "pubmatic",
@@ -1128,7 +1138,7 @@ func TestSetUIDEndpointMetrics(t *testing.T) {
 				m.On("RecordSetUid", metrics.SetUidOK).Once()
 				m.On("RecordSyncerSet", "pubmatic", metrics.SyncerSetUidCleared).Once()
 			},
-			expectedAnalytics: func(a *MockAnalytics) {
+			expectedAnalytics: func(a *MockAnalyticsRunner) {
 				expected := analytics.SetUIDObject{
 					Status:  200,
 					Bidder:  "pubmatic",
@@ -1149,7 +1159,7 @@ func TestSetUIDEndpointMetrics(t *testing.T) {
 			expectedMetrics: func(m *metrics.MetricsEngineMock) {
 				m.On("RecordSetUid", metrics.SetUidOptOut).Once()
 			},
-			expectedAnalytics: func(a *MockAnalytics) {
+			expectedAnalytics: func(a *MockAnalyticsRunner) {
 				expected := analytics.SetUIDObject{
 					Status:  401,
 					Bidder:  "",
@@ -1170,7 +1180,7 @@ func TestSetUIDEndpointMetrics(t *testing.T) {
 			expectedMetrics: func(m *metrics.MetricsEngineMock) {
 				m.On("RecordSetUid", metrics.SetUidSyncerUnknown).Once()
 			},
-			expectedAnalytics: func(a *MockAnalytics) {
+			expectedAnalytics: func(a *MockAnalyticsRunner) {
 				expected := analytics.SetUIDObject{
 					Status:  400,
 					Bidder:  "",
@@ -1191,7 +1201,7 @@ func TestSetUIDEndpointMetrics(t *testing.T) {
 			expectedMetrics: func(m *metrics.MetricsEngineMock) {
 				m.On("RecordSetUid", metrics.SetUidBadRequest).Once()
 			},
-			expectedAnalytics: func(a *MockAnalytics) {
+			expectedAnalytics: func(a *MockAnalyticsRunner) {
 				expected := analytics.SetUIDObject{
 					Status:  400,
 					Bidder:  "pubmatic",
@@ -1212,7 +1222,7 @@ func TestSetUIDEndpointMetrics(t *testing.T) {
 			expectedMetrics: func(m *metrics.MetricsEngineMock) {
 				m.On("RecordSetUid", metrics.SetUidBadRequest).Once()
 			},
-			expectedAnalytics: func(a *MockAnalytics) {
+			expectedAnalytics: func(a *MockAnalyticsRunner) {
 				expected := analytics.SetUIDObject{
 					Status:  400,
 					Bidder:  "pubmatic",
@@ -1233,7 +1243,7 @@ func TestSetUIDEndpointMetrics(t *testing.T) {
 			expectedMetrics: func(m *metrics.MetricsEngineMock) {
 				m.On("RecordSetUid", metrics.SetUidGDPRHostCookieBlocked).Once()
 			},
-			expectedAnalytics: func(a *MockAnalytics) {
+			expectedAnalytics: func(a *MockAnalyticsRunner) {
 				expected := analytics.SetUIDObject{
 					Status:  451,
 					Bidder:  "pubmatic",
@@ -1255,7 +1265,7 @@ func TestSetUIDEndpointMetrics(t *testing.T) {
 			expectedMetrics: func(m *metrics.MetricsEngineMock) {
 				m.On("RecordSetUid", metrics.SetUidAccountInvalid).Once()
 			},
-			expectedAnalytics: func(a *MockAnalytics) {
+			expectedAnalytics: func(a *MockAnalyticsRunner) {
 				expected := analytics.SetUIDObject{
 					Status:  400,
 					Bidder:  "pubmatic",
@@ -1277,7 +1287,7 @@ func TestSetUIDEndpointMetrics(t *testing.T) {
 			expectedMetrics: func(m *metrics.MetricsEngineMock) {
 				m.On("RecordSetUid", metrics.SetUidAccountConfigMalformed).Once()
 			},
-			expectedAnalytics: func(a *MockAnalytics) {
+			expectedAnalytics: func(a *MockAnalyticsRunner) {
 				expected := analytics.SetUIDObject{
 					Status:  400,
 					Bidder:  "pubmatic",
@@ -1299,7 +1309,7 @@ func TestSetUIDEndpointMetrics(t *testing.T) {
 			expectedMetrics: func(m *metrics.MetricsEngineMock) {
 				m.On("RecordSetUid", metrics.SetUidBadRequest).Once()
 			},
-			expectedAnalytics: func(a *MockAnalytics) {
+			expectedAnalytics: func(a *MockAnalyticsRunner) {
 				expected := analytics.SetUIDObject{
 					Status:  400,
 					Bidder:  "pubmatic",
@@ -1313,7 +1323,7 @@ func TestSetUIDEndpointMetrics(t *testing.T) {
 	}
 
 	for _, test := range testCases {
-		analyticsEngine := &MockAnalytics{}
+		analyticsEngine := &MockAnalyticsRunner{}
 		test.expectedAnalytics(analyticsEngine)
 
 		metricsEngine := &metrics.MetricsEngineMock{}
@@ -1337,7 +1347,7 @@ func TestOptedOut(t *testing.T) {
 	cookie.SetOptOut(true)
 	addCookie(request, cookie)
 	syncersBidderNameToKey := map[string]string{"pubmatic": "pubmatic"}
-	analytics := analyticsConf.NewPBSAnalytics(&config.Analytics{})
+	analytics := analyticsBuild.New(&config.Analytics{})
 	metrics := &metricsConf.NilMetricsEngine{}
 	response := doRequest(request, analytics, metrics, syncersBidderNameToKey, true, false, false, false, 0, nil)
 
@@ -1458,35 +1468,44 @@ func TestIsSyncerPriority(t *testing.T) {
 		expected                       bool
 	}{
 		{
-			name:                           "bidder-name-is-priority",
-			givenBidderNameFromSyncerQuery: "priorityBidder",
-			givenPriorityGroups: [][]string{
-				{"priorityBidder"},
-				{"2", "3"},
-			},
-			expected: true,
+			name:                           "priority-tier-1",
+			givenBidderNameFromSyncerQuery: "a",
+			givenPriorityGroups:            [][]string{{"a"}},
+			expected:                       true,
 		},
 		{
-			name:                           "bidder-name-is-not-priority",
-			givenBidderNameFromSyncerQuery: "notPriorityBidderName",
-			givenPriorityGroups: [][]string{
-				{"1"},
-				{"2", "3"},
-			},
-			expected: false,
+			name:                           "priority-tier-other",
+			givenBidderNameFromSyncerQuery: "c",
+			givenPriorityGroups:            [][]string{{"a"}, {"b", "c"}},
+			expected:                       true,
 		},
 		{
-			name:                           "no-bidder-name-given",
+			name:                           "priority-case-insensitive",
+			givenBidderNameFromSyncerQuery: "A",
+			givenPriorityGroups:            [][]string{{"a"}},
+			expected:                       true,
+		},
+		{
+			name:                           "not-priority-empty",
+			givenBidderNameFromSyncerQuery: "a",
+			givenPriorityGroups:            [][]string{},
+			expected:                       false,
+		},
+		{
+			name:                           "not-priority-not-defined",
+			givenBidderNameFromSyncerQuery: "a",
+			givenPriorityGroups:            [][]string{{"b"}},
+			expected:                       false,
+		},
+		{
+			name:                           "no-bidder",
 			givenBidderNameFromSyncerQuery: "",
-			givenPriorityGroups: [][]string{
-				{"1"},
-				{"2", "3"},
-			},
-			expected: false,
+			givenPriorityGroups:            [][]string{{"b"}},
+			expected:                       false,
 		},
 		{
-			name:                           "no-priority-groups-given",
-			givenBidderNameFromSyncerQuery: "bidderName",
+			name:                           "no-priority-groups",
+			givenBidderNameFromSyncerQuery: "a",
 			givenPriorityGroups:            [][]string{},
 			expected:                       false,
 		},
@@ -1525,7 +1544,7 @@ func makeRequest(uri string, existingSyncs map[string]string) *http.Request {
 	return request
 }
 
-func doRequest(req *http.Request, analytics analytics.PBSAnalyticsModule, metrics metrics.MetricsEngine, syncersBidderNameToKey map[string]string, gdprAllowsHostCookies, gdprReturnsError, gdprReturnsMalformedError, cfgAccountRequired bool, maxCookieSize int, priorityGroups [][]string) *httptest.ResponseRecorder {
+func doRequest(req *http.Request, analytics analytics.Runner, metrics metrics.MetricsEngine, syncersBidderNameToKey map[string]string, gdprAllowsHostCookies, gdprReturnsError, gdprReturnsMalformedError, cfgAccountRequired bool, maxCookieSize int, priorityGroups [][]string) *httptest.ResponseRecorder {
 	cfg := config.Configuration{
 		AccountRequired: cfgAccountRequired,
 		AccountDefaults: config.Account{},
diff --git a/exchange/adapter_util_test.go b/exchange/adapter_util_test.go
index 611498fe..08d751cd 100644
--- a/exchange/adapter_util_test.go
+++ b/exchange/adapter_util_test.go
@@ -68,13 +68,6 @@ func TestBuildAdapters(t *testing.T) {
 				errors.New("unknown: unknown bidder"),
 			},
 		},
-		{
-			description: "Alias feature disabled",
-			bidderInfos: map[string]config.BidderInfo{"appNexus": {AliasOf: "rubicon"}},
-			expectedErrors: []error{
-				errors.New("This feature is currently under development"),
-			},
-		},
 	}
 
 	cfg := &config.Configuration{}
diff --git a/exchange/exchangetest/eidpermissions-allowed-alias.json b/exchange/exchangetest/eidpermissions-allowed-alias.json
index f10bdcf8..de3c0e1d 100644
--- a/exchange/exchangetest/eidpermissions-allowed-alias.json
+++ b/exchange/exchangetest/eidpermissions-allowed-alias.json
@@ -10,7 +10,11 @@
                     "eids": [
                         {
                             "source": "source1",
-                            "id": "anyId"
+                            "uids": [
+                                {
+                                    "id": "id1"
+                                }
+                            ]
                         }
                     ]
                 }
@@ -66,7 +70,11 @@
                             "eids": [
                                 {
                                     "source": "source1",
-                                    "id": "anyId"
+                                    "uids": [
+                                        {
+                                            "id": "id1"
+                                        }
+                                    ]
                                 }
                             ]
                         }
diff --git a/exchange/exchangetest/eidpermissions-allowed-case-insensitive.json b/exchange/exchangetest/eidpermissions-allowed-case-insensitive.json
new file mode 100644
index 00000000..86797501
--- /dev/null
+++ b/exchange/exchangetest/eidpermissions-allowed-case-insensitive.json
@@ -0,0 +1,144 @@
+{
+    "incomingRequest": {
+        "ortbRequest": {
+            "id": "some-request-id",
+            "site": {
+                "page": "test.somepage.com"
+            },
+            "user": {
+                "ext": {
+                    "eids": [
+                        {
+                            "source": "source1",
+                            "uids": [
+                                {
+                                    "id": "id1"
+                                }
+                            ]
+                        }
+                    ]
+                }
+            },
+            "ext": {
+                "prebid": {
+                    "data": {
+                        "eidpermissions": [
+                            {
+                                "source": "source1",
+                                "bidders": [
+                                    "APPNEXUS"
+                                ]
+                            }
+                        ]
+                    }
+                }
+            },
+            "imp": [
+                {
+                    "id": "my-imp-id",
+                    "video": {
+                        "mimes": [
+                            "video/mp4"
+                        ]
+                    },
+                    "ext": {
+                        "prebid": {
+                            "bidder": {
+                                "appnexus": {
+                                    "placementId": 1
+                                }
+                            }
+                        }
+                    }
+                }
+            ]
+        }
+    },
+    "outgoingRequests": {
+        "appnexus": {
+            "expectRequest": {
+                "ortbRequest": {
+                    "id": "some-request-id",
+                    "site": {
+                        "page": "test.somepage.com"
+                    },
+                    "user": {
+                        "ext": {
+                            "eids": [
+                                {
+                                    "source": "source1",
+                                    "uids": [
+                                        {
+                                            "id": "id1"
+                                        }
+                                    ]
+                                }
+                            ]
+                        }
+                    },
+                    "imp": [
+                        {
+                            "id": "my-imp-id",
+                            "video": {
+                                "mimes": [
+                                    "video/mp4"
+                                ]
+                            },
+                            "ext": {
+                                "bidder": {
+                                    "placementId": 1
+                                }
+                            }
+                        }
+                    ]
+                }
+            },
+            "mockResponse": {
+                "pbsSeatBids": [
+                    {
+                        "pbsBids": [
+                            {
+                                "ortbBid": {
+                                    "id": "apn-bid",
+                                    "impid": "my-imp-id",
+                                    "price": 0.3,
+                                    "w": 200,
+                                    "h": 250,
+                                    "crid": "creative-1"
+                                },
+                                "bidType": "video"
+                            }
+                        ],
+                        "seat": "appnexus"
+                    }
+                ]
+            }
+        }
+    },
+    "response": {
+        "bids": {
+            "id": "some-request-id",
+            "seatbid": [
+                {
+                    "seat": "appnexus",
+                    "bid": [
+                        {
+                            "id": "apn-bid",
+                            "impid": "my-imp-id",
+                            "price": 0.3,
+                            "w": 200,
+                            "h": 250,
+                            "crid": "creative-1",
+                            "ext": {
+                                "origbidcpm": 0.3,
+                                "prebid": {
+                                    "type": "video"
+                                }
+                            }
+                        }
+                    ]
+                }
+            ]
+        }
+    }
+}
\ No newline at end of file
diff --git a/exchange/exchangetest/eidpermissions-allowed.json b/exchange/exchangetest/eidpermissions-allowed.json
index 6b27212c..9daee27a 100644
--- a/exchange/exchangetest/eidpermissions-allowed.json
+++ b/exchange/exchangetest/eidpermissions-allowed.json
@@ -10,7 +10,11 @@
                     "eids": [
                         {
                             "source": "source1",
-                            "id": "anyId"
+                            "uids": [
+                                {
+                                    "id": "id1"
+                                }
+                            ]
                         }
                     ]
                 }
@@ -63,7 +67,11 @@
                             "eids": [
                                 {
                                     "source": "source1",
-                                    "id": "anyId"
+                                    "uids": [
+                                        {
+                                            "id": "id1"
+                                        }
+                                    ]
                                 }
                             ]
                         }
diff --git a/exchange/exchangetest/eidpermissions-denied.json b/exchange/exchangetest/eidpermissions-denied.json
index df112905..04eb51bb 100644
--- a/exchange/exchangetest/eidpermissions-denied.json
+++ b/exchange/exchangetest/eidpermissions-denied.json
@@ -10,7 +10,11 @@
                     "eids": [
                         {
                             "source": "source1",
-                            "id": "anyId"
+                            "uids": [
+                                {
+                                    "id": "id1"
+                                }
+                            ]
                         }
                     ]
                 }
diff --git a/exchange/utils_test.go b/exchange/utils_test.go
index ee6dad1a..0dbf5b38 100644
--- a/exchange/utils_test.go
+++ b/exchange/utils_test.go
@@ -2768,6 +2768,14 @@ func TestRemoveUnpermissionedEids(t *testing.T) {
 			},
 			expectedUserExt: json.RawMessage(`{"eids":[{"source":"source1","uids":[{"id":"anyID"}]}]}`),
 		},
+		{
+			description: "Allowed By Specific Bidder - Case Insensitive",
+			userExt:     json.RawMessage(`{"eids":[{"source":"source1","uids":[{"id":"anyID"}]}]}`),
+			eidPermissions: []openrtb_ext.ExtRequestPrebidDataEidPermission{
+				{Source: "source1", Bidders: []string{"BIDDERA"}},
+			},
+			expectedUserExt: json.RawMessage(`{"eids":[{"source":"source1","uids":[{"id":"anyID"}]}]}`),
+		},
 		{
 			description: "Allowed By All Bidders",
 			userExt:     json.RawMessage(`{"eids":[{"source":"source1","uids":[{"id":"anyID"}]}]}`),
diff --git a/metrics/go_metrics_test.go b/metrics/go_metrics_test.go
index 3564f9fa..8acd1923 100644
--- a/metrics/go_metrics_test.go
+++ b/metrics/go_metrics_test.go
@@ -1066,81 +1066,6 @@ func TestRecordModuleAccountMetrics(t *testing.T) {
 	}
 }
 
-func TestRecordAccountGDPRChannelEnabledWarningMetrics(t *testing.T) {
-	testCases := []struct {
-		name                string
-		givenPubID          string
-		expectedMetricCount int64
-	}{
-		{
-			name:                "GdprChannelMetricIncremented",
-			givenPubID:          "acct-id",
-			expectedMetricCount: 1,
-		},
-	}
-	for _, test := range testCases {
-		t.Run(test.name, func(t *testing.T) {
-			registry := metrics.NewRegistry()
-			m := NewMetrics(registry, []openrtb_ext.BidderName{openrtb_ext.BidderAppnexus}, config.DisabledMetrics{}, nil, nil)
-
-			m.RecordAccountGDPRChannelEnabledWarning(test.givenPubID)
-			am := m.getAccountMetrics(test.givenPubID)
-
-			assert.Equal(t, test.expectedMetricCount, am.channelEnabledGDPRMeter.Count())
-		})
-	}
-}
-
-func TestRecordAccountCCPAChannelEnabledWarningMetrics(t *testing.T) {
-	testCases := []struct {
-		name                string
-		givenPubID          string
-		expectedMetricCount int64
-	}{
-		{
-			name:                "CcpaChannelMetricIncremented",
-			givenPubID:          "acct-id",
-			expectedMetricCount: 1,
-		},
-	}
-	for _, test := range testCases {
-		t.Run(test.name, func(t *testing.T) {
-			registry := metrics.NewRegistry()
-			m := NewMetrics(registry, []openrtb_ext.BidderName{openrtb_ext.BidderAppnexus}, config.DisabledMetrics{}, nil, nil)
-
-			m.RecordAccountCCPAChannelEnabledWarning(test.givenPubID)
-			am := m.getAccountMetrics(test.givenPubID)
-
-			assert.Equal(t, test.expectedMetricCount, am.channelEnabledCCPAMeter.Count())
-		})
-	}
-}
-
-func TestRecordAccountUpgradeStatusMetrics(t *testing.T) {
-	testCases := []struct {
-		name                string
-		givenPubID          string
-		expectedMetricCount int64
-	}{
-		{
-			name:                "AccountDeprecationMeterIncremented",
-			givenPubID:          "acct-id",
-			expectedMetricCount: 1,
-		},
-	}
-	for _, test := range testCases {
-		t.Run(test.name, func(t *testing.T) {
-			registry := metrics.NewRegistry()
-			m := NewMetrics(registry, []openrtb_ext.BidderName{openrtb_ext.BidderAppnexus}, config.DisabledMetrics{}, nil, nil)
-
-			m.RecordAccountUpgradeStatus(test.givenPubID)
-			am := m.getAccountMetrics(test.givenPubID)
-
-			assert.Equal(t, test.expectedMetricCount, am.accountDeprecationSummaryMeter.Count())
-		})
-	}
-}
-
 func TestRecordOverheadTime(t *testing.T) {
 	testCases := []struct {
 		name          string
diff --git a/metrics/prometheus/prometheus_test.go b/metrics/prometheus/prometheus_test.go
index 90d331fe..e57ae5e1 100644
--- a/metrics/prometheus/prometheus_test.go
+++ b/metrics/prometheus/prometheus_test.go
@@ -1976,70 +1976,3 @@ func TestRecordModuleMetrics(t *testing.T) {
 		}
 	}
 }
-
-func TestRecordAccountGDPRChannelEnabledWarningMetrics(t *testing.T) {
-	testCases := []struct {
-		name                string
-		givenPubID          string
-		expectedMetricCount float64
-	}{
-		{
-			name:                "GdprChannelMetricIncremented",
-			givenPubID:          "acct-id",
-			expectedMetricCount: 1,
-		},
-	}
-	for _, test := range testCases {
-		t.Run(test.name, func(t *testing.T) {
-			m := createMetricsForTesting()
-			m.RecordAccountGDPRChannelEnabledWarning(test.givenPubID)
-
-			assertCounterValue(t, "", "GDPR Channel Enabled Deprecation Warnings", m.channelEnabledGDPR, test.expectedMetricCount)
-		})
-	}
-}
-
-func TestRecordAccountCCPAChannelEnabledWarningMetrics(t *testing.T) {
-	testCases := []struct {
-		name                string
-		givenPubID          string
-		expectedMetricCount float64
-	}{
-		{
-			name:                "CcpaChannelMetricIncremented",
-			givenPubID:          "acct-id",
-			expectedMetricCount: 1,
-		},
-	}
-	for _, test := range testCases {
-		t.Run(test.name, func(t *testing.T) {
-			m := createMetricsForTesting()
-			m.RecordAccountCCPAChannelEnabledWarning(test.givenPubID)
-
-			assertCounterValue(t, "", "CCPA Channel Enabled Deprecation Warnings", m.channelEnabledCCPA, test.expectedMetricCount)
-		})
-	}
-}
-
-func TestRecordAccountUpgradeStatusMetrics(t *testing.T) {
-	testCases := []struct {
-		name                string
-		givenPubID          string
-		expectedMetricCount float64
-	}{
-		{
-			name:                "AccountDeprecationMeterIncremented",
-			givenPubID:          "acct-id",
-			expectedMetricCount: 1,
-		},
-	}
-	for _, test := range testCases {
-		t.Run(test.name, func(t *testing.T) {
-			m := createMetricsForTesting()
-			m.RecordAccountUpgradeStatus(test.givenPubID)
-
-			assertCounterValue(t, "", "Account Depreciation Summary Meter should be incremented", m.accountDeprecationSummary, test.expectedMetricCount)
-		})
-
-	}
-}