Repository: prebid/prebid-server
Pull request #3381 (2024-01-03 06:29:49)

Privacy Sandbox: support testing label header
Implements https://github.com/prebid/prebid-server/issues/3297


Patch:
diff --git a/Makefile b/Makefile
index 2d8aae6c..cf4ac52b 100644
--- a/Makefile
+++ b/Makefile
@@ -33,3 +33,7 @@ image:
 # format runs format
 format:
 	./scripts/format.sh -f true
+
+# formatcheck runs format for diagnostics, without modifying the code
+formatcheck:
+	./scripts/format.sh -f false
diff --git a/account/account.go b/account/account.go
index 2c243e0d..37d36761 100644
--- a/account/account.go
+++ b/account/account.go
@@ -111,7 +111,7 @@ func setDerivedConfig(account *config.Account) {
 		if pc.VendorExceptions == nil {
 			continue
 		}
-		pc.VendorExceptionMap = make(map[openrtb_ext.BidderName]struct{})
+		pc.VendorExceptionMap = make(map[string]struct{})
 		for _, v := range pc.VendorExceptions {
 			pc.VendorExceptionMap[v] = struct{}{}
 		}
diff --git a/adapters/adnuntius/adnuntius.go b/adapters/adnuntius/adnuntius.go
index 4b823455..c1d71cc1 100644
--- a/adapters/adnuntius/adnuntius.go
+++ b/adapters/adnuntius/adnuntius.go
@@ -33,6 +33,9 @@ type adnAdunit struct {
 type extDeviceAdnuntius struct {
 	NoCookies bool `json:"noCookies,omitempty"`
 }
+type siteExt struct {
+	Data interface{} `json:"data"`
+}
 
 type Ad struct {
 	Bid struct {
@@ -71,9 +74,10 @@ type adnMetaData struct {
 	Usi string `json:"usi,omitempty"`
 }
 type adnRequest struct {
-	AdUnits  []adnAdunit `json:"adUnits"`
-	MetaData adnMetaData `json:"metaData,omitempty"`
-	Context  string      `json:"context,omitempty"`
+	AdUnits   []adnAdunit `json:"adUnits"`
+	MetaData  adnMetaData `json:"metaData,omitempty"`
+	Context   string      `json:"context,omitempty"`
+	KeyValues interface{} `json:"kv,omitempty"`
 }
 
 type RequestExt struct {
@@ -138,7 +142,6 @@ func makeEndpointUrl(ortbRequest openrtb2.BidRequest, a *adapter, noCookies bool
 		if deviceExt.NoCookies {
 			noCookies = true
 		}
-
 	}
 
 	_, offset := a.time.Now().Zone()
@@ -249,11 +252,31 @@ func (a *adapter) generateRequests(ortbRequest openrtb2.BidRequest) ([]*adapters
 		site = ortbRequest.Site.Page
 	}
 
+	extSite, erro := getSiteExtAsKv(&ortbRequest)
+	if erro != nil {
+		return nil, []error{fmt.Errorf("failed to parse site Ext: %v", err)}
+	}
+
 	for _, networkAdunits := range networkAdunitMap {
 
 		adnuntiusRequest := adnRequest{
-			AdUnits: networkAdunits,
-			Context: site,
+			AdUnits:   networkAdunits,
+			Context:   site,
+			KeyValues: extSite.Data,
+		}
+
+		var extUser openrtb_ext.ExtUser
+		if ortbRequest.User != nil && ortbRequest.User.Ext != nil {
+			if err := json.Unmarshal(ortbRequest.User.Ext, &extUser); err != nil {
+				return nil, []error{fmt.Errorf("failed to parse Ext User: %v", err)}
+			}
+		}
+
+		// Will change when our adserver can accept multiple user IDS
+		if extUser.Eids != nil && len(extUser.Eids) > 0 {
+			if len(extUser.Eids[0].UIDs) > 0 {
+				adnuntiusRequest.MetaData.Usi = extUser.Eids[0].UIDs[0].ID
+			}
 		}
 
 		ortbUser := ortbRequest.User
@@ -310,6 +333,16 @@ func (a *adapter) MakeBids(request *openrtb2.BidRequest, externalRequest *adapte
 	return bidResponse, nil
 }
 
+func getSiteExtAsKv(request *openrtb2.BidRequest) (siteExt, error) {
+	var extSite siteExt
+	if request.Site != nil && request.Site.Ext != nil {
+		if err := json.Unmarshal(request.Site.Ext, &extSite); err != nil {
+			return extSite, fmt.Errorf("failed to parse ExtSite in Adnuntius: %v", err)
+		}
+	}
+	return extSite, nil
+}
+
 func getGDPR(request *openrtb2.BidRequest) (string, string, error) {
 
 	gdpr := ""
diff --git a/adapters/rubicon/rubicon.go b/adapters/rubicon/rubicon.go
index 01b1e797..6cffa1b2 100644
--- a/adapters/rubicon/rubicon.go
+++ b/adapters/rubicon/rubicon.go
@@ -938,7 +938,7 @@ func isVideo(imp openrtb2.Imp) bool {
 
 func isFullyPopulatedVideo(video *openrtb2.Video) bool {
 	// These are just recommended video fields for XAPI
-	return video.MIMEs != nil && video.Protocols != nil && video.MaxDuration != 0 && video.Linearity != 0 && video.API != nil
+	return video.MIMEs != nil && video.Protocols != nil && video.MaxDuration != 0 && video.Linearity != 0
 }
 
 func resolveNativeObject(native *openrtb2.Native, target map[string]interface{}) (*openrtb2.Native, error) {
diff --git a/adapters/seedingAlliance/seedingAlliance.go b/adapters/seedingAlliance/seedingAlliance.go
index 03b6853b..af48133d 100644
--- a/adapters/seedingAlliance/seedingAlliance.go
+++ b/adapters/seedingAlliance/seedingAlliance.go
@@ -33,11 +33,11 @@ func Builder(_ openrtb_ext.BidderName, config config.Adapter, server config.Serv
 }
 
 func (a *adapter) MakeRequests(request *openrtb2.BidRequest, extraRequestInfo *adapters.ExtraRequestInfo) ([]*adapters.RequestData, []error) {
-	var seatId string
+	var accountId string
 	var err error
 
 	for i := range request.Imp {
-		if seatId, err = getExtInfo(&request.Imp[i]); err != nil {
+		if accountId, err = getExtInfo(&request.Imp[i]); err != nil {
 			return nil, []error{err}
 		}
 	}
@@ -51,7 +51,7 @@ func (a *adapter) MakeRequests(request *openrtb2.BidRequest, extraRequestInfo *a
 		return nil, []error{err}
 	}
 
-	url, err := macros.ResolveMacros(a.endpoint, macros.EndpointTemplateParams{AccountID: seatId})
+	url, err := macros.ResolveMacros(a.endpoint, macros.EndpointTemplateParams{AccountID: accountId})
 	if err != nil {
 		return nil, []error{err}
 	}
@@ -146,7 +146,7 @@ func getExtInfo(imp *openrtb2.Imp) (string, error) {
 	var ext adapters.ExtImpBidder
 	var extSA openrtb_ext.ImpExtSeedingAlliance
 
-	seatID := "pbs"
+	accountId := "pbs"
 
 	if err := json.Unmarshal(imp.Ext, &ext); err != nil {
 		return "", fmt.Errorf("could not unmarshal adapters.ExtImpBidder: %w", err)
@@ -159,8 +159,12 @@ func getExtInfo(imp *openrtb2.Imp) (string, error) {
 	imp.TagID = extSA.AdUnitID
 
 	if extSA.SeatID != "" {
-		seatID = extSA.SeatID
+		accountId = extSA.SeatID
 	}
 
-	return seatID, nil
+	if extSA.AccountID != "" {
+		accountId = extSA.AccountID
+	}
+
+	return accountId, nil
 }
diff --git a/adapters/stroeerCore/stroeercore.go b/adapters/stroeerCore/stroeercore.go
index 733d549e..f2ef4923 100644
--- a/adapters/stroeerCore/stroeercore.go
+++ b/adapters/stroeerCore/stroeercore.go
@@ -23,17 +23,22 @@ type response struct {
 }
 
 type bidResponse struct {
-	ID     string  `json:"id"`
-	BidID  string  `json:"bidId"`
-	CPM    float64 `json:"cpm"`
-	Width  int64   `json:"width"`
-	Height int64   `json:"height"`
-	Ad     string  `json:"ad"`
-	CrID   string  `json:"crid"`
-	Mtype  string  `json:"mtype"`
+	ID     string          `json:"id"`
+	BidID  string          `json:"bidId"`
+	CPM    float64         `json:"cpm"`
+	Width  int64           `json:"width"`
+	Height int64           `json:"height"`
+	Ad     string          `json:"ad"`
+	CrID   string          `json:"crid"`
+	Mtype  string          `json:"mtype"`
+	DSA    json.RawMessage `json:"dsa"`
 }
 
-func (b bidResponse) resolveMediaType() (mt openrtb2.MarkupType, bt openrtb_ext.BidType, err error) {
+type bidExt struct {
+	DSA json.RawMessage `json:"dsa,omitempty"`
+}
+
+func (b *bidResponse) resolveMediaType() (mt openrtb2.MarkupType, bt openrtb_ext.BidType, err error) {
 	switch b.Mtype {
 	case "banner":
 		return openrtb2.MarkupBanner, openrtb_ext.BidTypeBanner, nil
@@ -82,6 +87,15 @@ func (a *adapter) MakeBids(bidRequest *openrtb2.BidRequest, requestData *adapter
 			MType: markupType,
 		}
 
+		if bid.DSA != nil {
+			dsaJson, err := json.Marshal(bidExt{bid.DSA})
+			if err != nil {
+				errors = append(errors, err)
+			} else {
+				openRtbBid.Ext = dsaJson
+			}
+		}
+
 		bidderResponse.Bids = append(bidderResponse.Bids, &adapters.TypedBid{
 			Bid:     &openRtbBid,
 			BidType: bidType,
diff --git a/analytics/pubstack/eventchannel/sender.go b/analytics/pubstack/eventchannel/sender.go
index 951de4d4..fe068b15 100644
--- a/analytics/pubstack/eventchannel/sender.go
+++ b/analytics/pubstack/eventchannel/sender.go
@@ -3,10 +3,11 @@ package eventchannel
 import (
 	"bytes"
 	"fmt"
-	"github.com/golang/glog"
 	"net/http"
 	"net/url"
 	"path"
+
+	"github.com/golang/glog"
 )
 
 type Sender = func(payload []byte) error
@@ -26,6 +27,7 @@ func NewHttpSender(client *http.Client, endpoint string) Sender {
 		if err != nil {
 			return err
 		}
+		resp.Body.Close()
 
 		if resp.StatusCode != http.StatusOK {
 			glog.Errorf("[pubstack] Wrong code received %d instead of %d", resp.StatusCode, http.StatusOK)
diff --git a/config/account.go b/config/account.go
index ee131873..72b6c07a 100644
--- a/config/account.go
+++ b/config/account.go
@@ -227,7 +227,7 @@ func (a *AccountGDPR) PurposeEnforcingVendors(purpose consentconstants.Purpose)
 }
 
 // PurposeVendorExceptions returns the vendor exception map for a given purpose.
-func (a *AccountGDPR) PurposeVendorExceptions(purpose consentconstants.Purpose) (value map[openrtb_ext.BidderName]struct{}, exists bool) {
+func (a *AccountGDPR) PurposeVendorExceptions(purpose consentconstants.Purpose) (value map[string]struct{}, exists bool) {
 	c, exists := a.PurposeConfigs[purpose]
 
 	if exists && c.VendorExceptionMap != nil {
@@ -262,8 +262,8 @@ type AccountGDPRPurpose struct {
 	EnforcePurpose *bool `mapstructure:"enforce_purpose" json:"enforce_purpose,omitempty"`
 	EnforceVendors *bool `mapstructure:"enforce_vendors" json:"enforce_vendors,omitempty"`
 	// Array of vendor exceptions that is used to create the hash table VendorExceptionMap so vendor names can be instantly accessed
-	VendorExceptions   []openrtb_ext.BidderName `mapstructure:"vendor_exceptions" json:"vendor_exceptions"`
-	VendorExceptionMap map[openrtb_ext.BidderName]struct{}
+	VendorExceptions   []string `mapstructure:"vendor_exceptions" json:"vendor_exceptions"`
+	VendorExceptionMap map[string]struct{}
 }
 
 // AccountGDPRSpecialFeature represents account-specific GDPR special feature configuration
@@ -337,6 +337,16 @@ type AccountPrivacy struct {
 	AllowActivities *AllowActivities `mapstructure:"allowactivities" json:"allowactivities"`
 	IPv6Config      IPv6             `mapstructure:"ipv6" json:"ipv6"`
 	IPv4Config      IPv4             `mapstructure:"ipv4" json:"ipv4"`
+	PrivacySandbox  PrivacySandbox   `mapstructure:"privacysandbox" json:"privacysandbox"`
+}
+
+type PrivacySandbox struct {
+	CookieDeprecation CookieDeprecation `mapstructure:"cookiedeprecation"`
+}
+
+type CookieDeprecation struct {
+	Enabled bool `mapstructure:"enabled"`
+	TTLSec  int  `mapstructure:"ttl_sec"`
 }
 
 type IPv6 struct {
diff --git a/config/config.go b/config/config.go
index f0e9f5c7..8449bc1b 100644
--- a/config/config.go
+++ b/config/config.go
@@ -375,13 +375,13 @@ func (t *TCF2) PurposeEnforcingVendors(purpose consentconstants.Purpose) (enforc
 
 // PurposeVendorExceptions returns the vendor exception map for a given purpose if it exists, otherwise it returns
 // an empty map of vendor exceptions
-func (t *TCF2) PurposeVendorExceptions(purpose consentconstants.Purpose) (vendorExceptions map[openrtb_ext.BidderName]struct{}) {
+func (t *TCF2) PurposeVendorExceptions(purpose consentconstants.Purpose) (vendorExceptions map[string]struct{}) {
 	c, exists := t.PurposeConfigs[purpose]
 
 	if exists && c.VendorExceptionMap != nil {
 		return c.VendorExceptionMap
 	}
-	return make(map[openrtb_ext.BidderName]struct{}, 0)
+	return make(map[string]struct{}, 0)
 }
 
 // FeatureOneEnforced checks if special feature one is enforced. If it is enforced, PBS will determine whether geo
@@ -417,8 +417,8 @@ type TCF2Purpose struct {
 	EnforcePurpose bool `mapstructure:"enforce_purpose"`
 	EnforceVendors bool `mapstructure:"enforce_vendors"`
 	// Array of vendor exceptions that is used to create the hash table VendorExceptionMap so vendor names can be instantly accessed
-	VendorExceptions   []openrtb_ext.BidderName `mapstructure:"vendor_exceptions"`
-	VendorExceptionMap map[openrtb_ext.BidderName]struct{}
+	VendorExceptions   []string `mapstructure:"vendor_exceptions"`
+	VendorExceptionMap map[string]struct{}
 }
 
 type TCF2SpecialFeature struct {
@@ -735,13 +735,13 @@ func New(v *viper.Viper, bidderInfos BidderInfos, normalizeBidderName func(strin
 		}
 	}
 
-	// To look for a purpose's vendor exceptions in O(1) time, for each purpose we fill this hash table with bidders
-	// located in the VendorExceptions field of the GDPR.TCF2.PurposeX struct defined in this file
+	// To look for a purpose's vendor exceptions in O(1) time, for each purpose we fill this hash table with bidders/analytics
+	// adapters located in the VendorExceptions field of the GDPR.TCF2.PurposeX struct defined in this file
 	for _, pc := range c.GDPR.TCF2.PurposeConfigs {
-		pc.VendorExceptionMap = make(map[openrtb_ext.BidderName]struct{})
+		pc.VendorExceptionMap = make(map[string]struct{})
 		for v := 0; v < len(pc.VendorExceptions); v++ {
-			bidderName := pc.VendorExceptions[v]
-			pc.VendorExceptionMap[bidderName] = struct{}{}
+			adapterName := pc.VendorExceptions[v]
+			pc.VendorExceptionMap[adapterName] = struct{}{}
 		}
 	}
 
@@ -1064,16 +1064,16 @@ func SetupViper(v *viper.Viper, filename string, bidderInfos BidderInfos) {
 	v.SetDefault("gdpr.tcf2.purpose8.enforce_vendors", true)
 	v.SetDefault("gdpr.tcf2.purpose9.enforce_vendors", true)
 	v.SetDefault("gdpr.tcf2.purpose10.enforce_vendors", true)
-	v.SetDefault("gdpr.tcf2.purpose1.vendor_exceptions", []openrtb_ext.BidderName{})
-	v.SetDefault("gdpr.tcf2.purpose2.vendor_exceptions", []openrtb_ext.BidderName{})
-	v.SetDefault("gdpr.tcf2.purpose3.vendor_exceptions", []openrtb_ext.BidderName{})
-	v.SetDefault("gdpr.tcf2.purpose4.vendor_exceptions", []openrtb_ext.BidderName{})
-	v.SetDefault("gdpr.tcf2.purpose5.vendor_exceptions", []openrtb_ext.BidderName{})
-	v.SetDefault("gdpr.tcf2.purpose6.vendor_exceptions", []openrtb_ext.BidderName{})
-	v.SetDefault("gdpr.tcf2.purpose7.vendor_exceptions", []openrtb_ext.BidderName{})
-	v.SetDefault("gdpr.tcf2.purpose8.vendor_exceptions", []openrtb_ext.BidderName{})
-	v.SetDefault("gdpr.tcf2.purpose9.vendor_exceptions", []openrtb_ext.BidderName{})
-	v.SetDefault("gdpr.tcf2.purpose10.vendor_exceptions", []openrtb_ext.BidderName{})
+	v.SetDefault("gdpr.tcf2.purpose1.vendor_exceptions", []string{})
+	v.SetDefault("gdpr.tcf2.purpose2.vendor_exceptions", []string{})
+	v.SetDefault("gdpr.tcf2.purpose3.vendor_exceptions", []string{})
+	v.SetDefault("gdpr.tcf2.purpose4.vendor_exceptions", []string{})
+	v.SetDefault("gdpr.tcf2.purpose5.vendor_exceptions", []string{})
+	v.SetDefault("gdpr.tcf2.purpose6.vendor_exceptions", []string{})
+	v.SetDefault("gdpr.tcf2.purpose7.vendor_exceptions", []string{})
+	v.SetDefault("gdpr.tcf2.purpose8.vendor_exceptions", []string{})
+	v.SetDefault("gdpr.tcf2.purpose9.vendor_exceptions", []string{})
+	v.SetDefault("gdpr.tcf2.purpose10.vendor_exceptions", []string{})
 	v.SetDefault("gdpr.amp_exception", false)
 	v.SetDefault("gdpr.eea_countries", []string{"ALA", "AUT", "BEL", "BGR", "HRV", "CYP", "CZE", "DNK", "EST",
 		"FIN", "FRA", "GUF", "DEU", "GIB", "GRC", "GLP", "GGY", "HUN", "ISL", "IRL", "IMN", "ITA", "JEY", "LVA",
@@ -1107,6 +1107,8 @@ func SetupViper(v *viper.Viper, filename string, bidderInfos BidderInfos) {
 	v.SetDefault("account_defaults.price_floors.fetch.max_age_sec", 86400)
 	v.SetDefault("account_defaults.price_floors.fetch.period_sec", 3600)
 	v.SetDefault("account_defaults.price_floors.fetch.max_schema_dims", 0)
+	v.SetDefault("account_defaults.privacy.privacysandbox.cookiedeprecation.enabled", false)
+	v.SetDefault("account_defaults.privacy.privacysandbox.cookiedeprecation.ttl_sec", 604800)
 
 	v.SetDefault("account_defaults.events_enabled", false)
 	v.SetDefault("account_defaults.privacy.ipv6.anon_keep_bits", 56)
diff --git a/dsa/validate.go b/dsa/validate.go
index f6ece66e..034dae8b 100644
--- a/dsa/validate.go
+++ b/dsa/validate.go
@@ -1,51 +1,103 @@
 package dsa
 
 import (
+	"errors"
+
 	"github.com/prebid/prebid-server/v2/exchange/entities"
 	"github.com/prebid/prebid-server/v2/openrtb_ext"
+	"github.com/prebid/prebid-server/v2/util/jsonutil"
+)
 
-	"github.com/buger/jsonparser"
+// Required values representing whether a DSA object is required
+const (
+	Required               int8 = 2 // bid responses without DSA object will not be accepted
+	RequiredOnlinePlatform int8 = 3 // bid responses without DSA object will not be accepted, Publisher is Online Platform
 )
 
+// PubRender values representing publisher rendering intentions
 const (
-	// Required - bid responses without DSA object will not be accepted
-	Required = 2
-	// RequiredOnlinePlatform - bid responses without DSA object will not be accepted, Publisher is an Online Platform
-	RequiredOnlinePlatform = 3
+	PubRenderCannotRender int8 = 0 // publisher can't render
+	PubRenderWillRender   int8 = 2 // publisher will render
+)
+
+// AdRender values representing buyer/advertiser rendering intentions
+const (
+	AdRenderWillRender int8 = 1 // buyer/advertiser will render
+)
+
+var (
+	ErrDsaMissing        = errors.New("DSA object missing when required")
+	ErrBehalfTooLong     = errors.New("DSA behalf exceeds limit of 100 chars")
+	ErrPaidTooLong       = errors.New("DSA paid exceeds limit of 100 chars")
+	ErrNeitherWillRender = errors.New("DSA publisher and buyer both signal will not render")
+	ErrBothWillRender    = errors.New("DSA publisher and buyer both signal will render")
+)
+
+const (
+	behalfMaxLength = 100
+	paidMaxLength   = 100
 )
 
 // Validate determines whether a given bid is valid from a DSA perspective.
 // A bid is considered valid unless the bid request indicates that a DSA object is required
-// in bid responses and the object happens to be missing from the specified bid.
-func Validate(req *openrtb_ext.RequestWrapper, bid *entities.PbsOrtbBid) (valid bool) {
-	if !dsaRequired(req) {
-		return true
+// in bid responses and the object happens to be missing from the specified bid, or if the bid
+// DSA object contents are invalid
+func Validate(req *openrtb_ext.RequestWrapper, bid *entities.PbsOrtbBid) error {
+	reqDSA := getReqDSA(req)
+	bidDSA := getBidDSA(bid)
+
+	if dsaRequired(reqDSA) && bidDSA == nil {
+		return ErrDsaMissing
 	}
-	if bid == nil || bid.Bid == nil {
-		return false
+	if bidDSA == nil {
+		return nil
+	}
+	if len(bidDSA.Behalf) > behalfMaxLength {
+		return ErrBehalfTooLong
 	}
-	_, dataType, _, err := jsonparser.Get(bid.Bid.Ext, "dsa")
-	if dataType == jsonparser.Object && err == nil {
-		return true
-	} else if err != nil && err != jsonparser.KeyPathNotFoundError {
-		return true
+	if len(bidDSA.Paid) > paidMaxLength {
+		return ErrPaidTooLong
 	}
-	return false
+	if reqDSA != nil && reqDSA.PubRender != nil && bidDSA.AdRender != nil {
+		if *reqDSA.PubRender == PubRenderCannotRender && *bidDSA.AdRender != AdRenderWillRender {
+			return ErrNeitherWillRender
+		}
+		if *reqDSA.PubRender == PubRenderWillRender && *bidDSA.AdRender == AdRenderWillRender {
+			return ErrBothWillRender
+		}
+	}
+	return nil
 }
 
 // dsaRequired examines the bid request to determine if the dsarequired field indicates
 // that bid responses include a dsa object
-func dsaRequired(req *openrtb_ext.RequestWrapper) bool {
+func dsaRequired(dsa *openrtb_ext.ExtRegsDSA) bool {
+	if dsa == nil || dsa.Required == nil {
+		return false
+	}
+	return *dsa.Required == Required || *dsa.Required == RequiredOnlinePlatform
+}
+
+// getReqDSA retrieves the DSA object from the request
+func getReqDSA(req *openrtb_ext.RequestWrapper) *openrtb_ext.ExtRegsDSA {
+	if req == nil {
+		return nil
+	}
 	regExt, err := req.GetRegExt()
 	if regExt == nil || err != nil {
-		return false
+		return nil
 	}
-	regsDSA := regExt.GetDSA()
-	if regsDSA == nil {
-		return false
+	return regExt.GetDSA()
+}
+
+// getBidDSA retrieves the DSA object from the bid
+func getBidDSA(bid *entities.PbsOrtbBid) *openrtb_ext.ExtBidDSA {
+	if bid == nil || bid.Bid == nil {
+		return nil
 	}
-	if regsDSA.Required == Required || regsDSA.Required == RequiredOnlinePlatform {
-		return true
+	var bidExt openrtb_ext.ExtBid
+	if err := jsonutil.Unmarshal(bid.Bid.Ext, &bidExt); err != nil {
+		return nil
 	}
-	return false
+	return bidExt.DSA
 }
diff --git a/endpoints/cookie_sync.go b/endpoints/cookie_sync.go
index 84d6d784..d6e0b31a 100644
--- a/endpoints/cookie_sync.go
+++ b/endpoints/cookie_sync.go
@@ -9,6 +9,7 @@ import (
 	"net/http"
 	"strconv"
 	"strings"
+	"time"
 
 	"github.com/golang/glog"
 	"github.com/julienschmidt/httprouter"
@@ -29,8 +30,11 @@ import (
 	"github.com/prebid/prebid-server/v2/usersync"
 	"github.com/prebid/prebid-server/v2/util/jsonutil"
 	stringutil "github.com/prebid/prebid-server/v2/util/stringutil"
+	"github.com/prebid/prebid-server/v2/util/timeutil"
 )
 
+const receiveCookieDeprecation = "receive-cookie-deprecation"
+
 var (
 	errCookieSyncOptOut                            = errors.New("User has opted out")
 	errCookieSyncBody                              = errors.New("Failed to read request body")
@@ -73,6 +77,7 @@ func NewCookieSyncEndpoint(
 		metrics:         metrics,
 		pbsAnalytics:    analyticsRunner,
 		accountsFetcher: accountsFetcher,
+		time:            &timeutil.RealTime{},
 	}
 }
 
@@ -83,10 +88,12 @@ type cookieSyncEndpoint struct {
 	metrics         metrics.MetricsEngine
 	pbsAnalytics    analytics.Runner
 	accountsFetcher stored_requests.AccountFetcher
+	time            timeutil.Time
 }
 
 func (c *cookieSyncEndpoint) Handle(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
-	request, privacyMacros, err := c.parseRequest(r)
+	request, privacyMacros, account, err := c.parseRequest(r)
+	c.setCookieDeprecationHeader(w, r, account)
 	if err != nil {
 		c.writeParseRequestErrorMetrics(err)
 		c.handleError(w, err, http.StatusBadRequest)
@@ -113,16 +120,16 @@ func (c *cookieSyncEndpoint) Handle(w http.ResponseWriter, r *http.Request, _ ht
 	}
 }
 
-func (c *cookieSyncEndpoint) parseRequest(r *http.Request) (usersync.Request, macros.UserSyncPrivacy, error) {
+func (c *cookieSyncEndpoint) parseRequest(r *http.Request) (usersync.Request, macros.UserSyncPrivacy, *config.Account, error) {
 	defer r.Body.Close()
 	body, err := io.ReadAll(r.Body)
 	if err != nil {
-		return usersync.Request{}, macros.UserSyncPrivacy{}, errCookieSyncBody
+		return usersync.Request{}, macros.UserSyncPrivacy{}, nil, errCookieSyncBody
 	}
 
 	request := cookieSyncRequest{}
 	if err := jsonutil.UnmarshalValid(body, &request); err != nil {
-		return usersync.Request{}, macros.UserSyncPrivacy{}, fmt.Errorf("JSON parsing failed: %s", err.Error())
+		return usersync.Request{}, macros.UserSyncPrivacy{}, nil, fmt.Errorf("JSON parsing failed: %s", err.Error())
 	}
 
 	if request.Account == "" {
@@ -130,7 +137,7 @@ func (c *cookieSyncEndpoint) parseRequest(r *http.Request) (usersync.Request, ma
 	}
 	account, fetchErrs := accountService.GetAccount(context.Background(), c.config, c.accountsFetcher, request.Account, c.metrics)
 	if len(fetchErrs) > 0 {
-		return usersync.Request{}, macros.UserSyncPrivacy{}, combineErrors(fetchErrs)
+		return usersync.Request{}, macros.UserSyncPrivacy{}, nil, combineErrors(fetchErrs)
 	}
 
 	request = c.setLimit(request, account.CookieSync)
@@ -138,7 +145,7 @@ func (c *cookieSyncEndpoint) parseRequest(r *http.Request) (usersync.Request, ma
 
 	privacyMacros, gdprSignal, privacyPolicies, err := extractPrivacyPolicies(request, c.privacyConfig.gdprConfig.DefaultValue)
 	if err != nil {
-		return usersync.Request{}, macros.UserSyncPrivacy{}, err
+		return usersync.Request{}, macros.UserSyncPrivacy{}, account, err
 	}
 
 	ccpaParsedPolicy := ccpa.ParsedPolicy{}
@@ -156,7 +163,7 @@ func (c *cookieSyncEndpoint) parseRequest(r *http.Request) (usersync.Request, ma
 
 	syncTypeFilter, err := parseTypeFilter(request.FilterSettings)
 	if err != nil {
-		return usersync.Request{}, macros.UserSyncPrivacy{}, err
+		return usersync.Request{}, macros.UserSyncPrivacy{}, account, err
 	}
 
 	gdprRequestInfo := gdpr.RequestInfo{
@@ -185,7 +192,7 @@ func (c *cookieSyncEndpoint) parseRequest(r *http.Request) (usersync.Request, ma
 		SyncTypeFilter: syncTypeFilter,
 		GPPSID:         request.GPPSID,
 	}
-	return rx, privacyMacros, nil
+	return rx, privacyMacros, account, nil
 }
 
 func extractPrivacyPolicies(request cookieSyncRequest, usersyncDefaultGDPRValue string) (macros.UserSyncPrivacy, gdpr.Signal, privacy.Policies, error) {
@@ -455,11 +462,38 @@ func (c *cookieSyncEndpoint) handleResponse(w http.ResponseWriter, tf usersync.S
 	})
 
 	w.Header().Set("Content-Type", "application/json; charset=utf-8")
+
 	enc := json.NewEncoder(w)
 	enc.SetEscapeHTML(false)
 	enc.Encode(response)
 }
 
+func (c *cookieSyncEndpoint) setCookieDeprecationHeader(w http.ResponseWriter, r *http.Request, account *config.Account) {
+	if rcd, err := r.Cookie(receiveCookieDeprecation); err == nil && rcd != nil {
+		return
+	}
+	if account == nil || !account.Privacy.PrivacySandbox.CookieDeprecation.Enabled {
+		return
+	}
+	cookie := &http.Cookie{
+		Name:     receiveCookieDeprecation,
+		Value:    "1",
+		Secure:   true,
+		HttpOnly: true,
+		Path:     "/",
+		SameSite: http.SameSiteNoneMode,
+		Expires:  c.time.Now().Add(time.Second * time.Duration(account.Privacy.PrivacySandbox.CookieDeprecation.TTLSec)),
+	}
+	setCookiePartitioned(w, cookie)
+}
+
+// setCookiePartitioned temporary substitute for http.SetCookie(w, cookie) until it supports Partitioned cookie type. Refer https://github.com/golang/go/issues/62490
+func setCookiePartitioned(w http.ResponseWriter, cookie *http.Cookie) {
+	if v := cookie.String(); v != "" {
+		w.Header().Add("Set-Cookie", v+"; Partitioned;")
+	}
+}
+
 func mapBidderStatusToAnalytics(from []cookieSyncResponseBidder) []*analytics.CookieSyncBidder {
 	to := make([]*analytics.CookieSyncBidder, len(from))
 	for i, b := range from {
diff --git a/endpoints/openrtb2/amp_auction.go b/endpoints/openrtb2/amp_auction.go
index a6ad8d3f..6d14ed7d 100644
--- a/endpoints/openrtb2/amp_auction.go
+++ b/endpoints/openrtb2/amp_auction.go
@@ -230,6 +230,19 @@ func (deps *endpointDeps) AmpAuction(w http.ResponseWriter, r *http.Request, _ h
 		return
 	}
 
+	hasStoredResponses := len(storedAuctionResponses) > 0
+	errs := deps.validateRequest(account, r, reqWrapper, true, hasStoredResponses, storedBidResponses, false)
+	errL = append(errL, errs...)
+	ao.Errors = append(ao.Errors, errs...)
+	if errortypes.ContainsFatalError(errs) {
+		w.WriteHeader(http.StatusBadRequest)
+		for _, err := range errortypes.FatalOnly(errs) {
+			w.Write([]byte(fmt.Sprintf("Invalid request: %s\n", err.Error())))
+		}
+		labels.RequestStatus = metrics.RequestStatusBadInput
+		return
+	}
+
 	tcf2Config := gdpr.NewTCF2Config(deps.cfg.GDPR.TCF2, account.GDPR)
 
 	activityControl = privacy.NewActivityControl(&account.Privacy)
@@ -497,10 +510,6 @@ func (deps *endpointDeps) parseAmpRequest(httpRequest *http.Request) (req *openr
 		return
 	}
 
-	hasStoredResponses := len(storedAuctionResponses) > 0
-	e = deps.validateRequest(req, true, hasStoredResponses, storedBidResponses, false)
-	errs = append(errs, e...)
-
 	return
 }
 
diff --git a/endpoints/openrtb2/auction.go b/endpoints/openrtb2/auction.go
index c7beceb1..2aafe680 100644
--- a/endpoints/openrtb2/auction.go
+++ b/endpoints/openrtb2/auction.go
@@ -60,6 +60,7 @@ import (
 const storedRequestTimeoutMillis = 50
 const ampChannel = "amp"
 const appChannel = "app"
+const secCookieDeprecation = "Sec-Cookie-Deprecation"
 
 var (
 	dntKey      string = http.CanonicalHeaderKey("DNT")
@@ -552,7 +553,7 @@ func (deps *endpointDeps) parseRequest(httpRequest *http.Request, labels *metric
 	}
 
 	hasStoredResponses := len(storedAuctionResponses) > 0
-	errL := deps.validateRequest(req, false, hasStoredResponses, storedBidResponses, hasStoredBidRequest)
+	errL := deps.validateRequest(account, httpRequest, req, false, hasStoredResponses, storedBidResponses, hasStoredBidRequest)
 	if len(errL) > 0 {
 		errs = append(errs, errL...)
 	}
@@ -746,7 +747,7 @@ func mergeBidderParamsImpExtPrebid(impExt *openrtb_ext.ImpExt, reqExtParams map[
 	return nil
 }
 
-func (deps *endpointDeps) validateRequest(req *openrtb_ext.RequestWrapper, isAmp bool, hasStoredResponses bool, storedBidResp stored_responses.ImpBidderStoredResp, hasStoredBidRequest bool) []error {
+func (deps *endpointDeps) validateRequest(account *config.Account, httpReq *http.Request, req *openrtb_ext.RequestWrapper, isAmp bool, hasStoredResponses bool, storedBidResp stored_responses.ImpBidderStoredResp, hasStoredBidRequest bool) []error {
 	errL := []error{}
 	if req.ID == "" {
 		return []error{errors.New("request missing required field: \"id\"")}
@@ -875,6 +876,10 @@ func (deps *endpointDeps) validateRequest(req *openrtb_ext.RequestWrapper, isAmp
 		return append(errL, err)
 	}
 
+	if err := validateOrFillCDep(httpReq, req, account); err != nil {
+		errL = append(errL, err)
+	}
+
 	if ccpaPolicy, err := ccpa.ReadFromRequestWrapper(req, gpp); err != nil {
 		errL = append(errL, err)
 		if errortypes.ContainsFatalError([]error{err}) {
@@ -1922,6 +1927,35 @@ func validateDevice(device *openrtb2.Device) error {
 	return nil
 }
 
+func validateOrFillCDep(httpReq *http.Request, req *openrtb_ext.RequestWrapper, account *config.Account) error {
+	if account == nil || !account.Privacy.PrivacySandbox.CookieDeprecation.Enabled {
+		return nil
+	}
+
+	deviceExt, err := req.GetDeviceExt()
+	if err != nil {
+		return err
+	}
+
+	if deviceExt.GetCDep() != "" {
+		return nil
+	}
+
+	secCookieDeprecation := httpReq.Header.Get(secCookieDeprecation)
+	if secCookieDeprecation == "" {
+		return nil
+	}
+	if len(secCookieDeprecation) > 100 {
+		return &errortypes.Warning{
+			Message:     "request.device.ext.cdep must not exceed 100 characters",
+			WarningCode: errortypes.SecCookieDeprecationLenWarningCode,
+		}
+	}
+
+	deviceExt.SetCDep(secCookieDeprecation)
+	return nil
+}
+
 func validateExactlyOneInventoryType(reqWrapper *openrtb_ext.RequestWrapper) error {
 
 	// Prep for mutual exclusion check
diff --git a/endpoints/openrtb2/video_auction.go b/endpoints/openrtb2/video_auction.go
index 22248f1f..3ddfc31a 100644
--- a/endpoints/openrtb2/video_auction.go
+++ b/endpoints/openrtb2/video_auction.go
@@ -267,12 +267,6 @@ func (deps *endpointDeps) VideoAuctionEndpoint(w http.ResponseWriter, r *http.Re
 		return
 	}
 
-	errL = deps.validateRequest(bidReqWrapper, false, false, nil, false)
-	if errortypes.ContainsFatalError(errL) {
-		handleError(&labels, w, errL, &vo, &debugLog)
-		return
-	}
-
 	ctx := context.Background()
 	timeout := deps.cfg.AuctionTimeouts.LimitAuctionTimeout(time.Duration(bidReqWrapper.TMax) * time.Millisecond)
 	if timeout > 0 {
@@ -306,6 +300,12 @@ func (deps *endpointDeps) VideoAuctionEndpoint(w http.ResponseWriter, r *http.Re
 		return
 	}
 
+	errL = deps.validateRequest(account, r, bidReqWrapper, false, false, nil, false)
+	if errortypes.ContainsFatalError(errL) {
+		handleError(&labels, w, errL, &vo, &debugLog)
+		return
+	}
+
 	activityControl = privacy.NewActivityControl(&account.Privacy)
 
 	secGPC := r.Header.Get("Sec-GPC")
diff --git a/errortypes/code.go b/errortypes/code.go
index 399dd663..a30bb8e4 100644
--- a/errortypes/code.go
+++ b/errortypes/code.go
@@ -32,6 +32,7 @@ const (
 	BidAdjustmentWarningCode
 	FloorBidRejectionWarningCode
 	InvalidBidResponseDSAWarningCode
+	SecCookieDeprecationLenWarningCode
 )
 
 // Coder provides an error or warning code with severity.
diff --git a/exchange/exchange.go b/exchange/exchange.go
index b9dae672..ef387363 100644
--- a/exchange/exchange.go
+++ b/exchange/exchange.go
@@ -105,7 +105,7 @@ type bidResponseWrapper struct {
 }
 
 type BidIDGenerator interface {
-	New() (string, error)
+	New(bidder string) (string, error)
 	Enabled() bool
 }
 
@@ -117,7 +117,7 @@ func (big *bidIDGenerator) Enabled() bool {
 	return big.enabled
 }
 
-func (big *bidIDGenerator) New() (string, error) {
+func (big *bidIDGenerator) New(bidder string) (string, error) {
 	rawUuid, err := uuid.NewV4()
 	return rawUuid.String(), err
 }
@@ -416,10 +416,11 @@ func (e *exchange) HoldAuction(ctx context.Context, r *AuctionRequest, debugLog
 		}
 
 		if e.bidIDGenerator.Enabled() {
-			for _, seatBid := range adapterBids {
-				for _, pbsBid := range seatBid.Bids {
-					pbsBid.GeneratedBidID, err = e.bidIDGenerator.New()
-					if err != nil {
+			for bidder, seatBid := range adapterBids {
+				for i := range seatBid.Bids {
+					if bidID, err := e.bidIDGenerator.New(bidder.String()); err == nil {
+						seatBid.Bids[i].GeneratedBidID = bidID
+					} else {
 						errs = append(errs, errors.New("Error generating bid.ext.prebid.bidid"))
 					}
 				}
@@ -1245,12 +1246,12 @@ func (e *exchange) makeBid(bids []*entities.PbsOrtbBid, auc *auction, returnCrea
 	errs := make([]error, 0, 1)
 
 	for _, bid := range bids {
-		if !dsa.Validate(bidRequest, bid) {
-			RequiredError := openrtb_ext.ExtBidderMessage{
+		if err := dsa.Validate(bidRequest, bid); err != nil {
+			dsaMessage := openrtb_ext.ExtBidderMessage{
 				Code:    errortypes.InvalidBidResponseDSAWarningCode,
-				Message: "bid response rejected: DSA object missing when required",
+				Message: fmt.Sprintf("bid rejected: %s", err.Error()),
 			}
-			bidResponseExt.Warnings[adapter] = append(bidResponseExt.Warnings[adapter], RequiredError)
+			bidResponseExt.Warnings[adapter] = append(bidResponseExt.Warnings[adapter], dsaMessage)
 
 			seatNonBids.addBid(bid, int(ResponseRejectedGeneral), adapter.String())
 			continue // Don't add bid to result
diff --git a/gdpr/aggregated_config.go b/gdpr/aggregated_config.go
index 4bd1533d..9ac03143 100644
--- a/gdpr/aggregated_config.go
+++ b/gdpr/aggregated_config.go
@@ -17,7 +17,7 @@ type TCF2ConfigReader interface {
 	PurposeEnforced(consentconstants.Purpose) bool
 	PurposeEnforcementAlgo(consentconstants.Purpose) config.TCF2EnforcementAlgo
 	PurposeEnforcingVendors(consentconstants.Purpose) bool
-	PurposeVendorExceptions(consentconstants.Purpose) map[openrtb_ext.BidderName]struct{}
+	PurposeVendorExceptions(consentconstants.Purpose) map[string]struct{}
 	PurposeOneTreatmentEnabled() bool
 	PurposeOneTreatmentAccessAllowed() bool
 }
@@ -85,9 +85,9 @@ func (tc *tcf2Config) PurposeEnforcingVendors(purpose consentconstants.Purpose)
 }
 
 // PurposeVendorExceptions returns the vendor exception map for the specified purpose if it exists for the account;
-// otherwise it returns a nil map. If a bidder is a vendor exception, the GDPR full enforcement algorithm will
+// otherwise it returns a nil map. If a bidder/analytics adapter is a vendor exception, the GDPR full enforcement algorithm will
 // bypass the legal basis calculation assuming the request is valid and there isn't a "deny all" publisher restriction
-func (tc *tcf2Config) PurposeVendorExceptions(purpose consentconstants.Purpose) map[openrtb_ext.BidderName]struct{} {
+func (tc *tcf2Config) PurposeVendorExceptions(purpose consentconstants.Purpose) map[string]struct{} {
 	if value, exists := tc.AccountConfig.PurposeVendorExceptions(purpose); exists {
 		return value
 	}
diff --git a/gdpr/basic_enforcement.go b/gdpr/basic_enforcement.go
index 322bb309..2bb11857 100644
--- a/gdpr/basic_enforcement.go
+++ b/gdpr/basic_enforcement.go
@@ -2,10 +2,9 @@ package gdpr
 
 import (
 	tcf2 "github.com/prebid/go-gdpr/vendorconsent/tcf2"
-	"github.com/prebid/prebid-server/v2/openrtb_ext"
 )
 
-// BasicEnforcement determines if legal basis is satisfied for a given purpose and bidder using
+// BasicEnforcement determines if legal basis is satisfied for a given purpose and bidder/analytics adapter using
 // the TCF2 basic enforcement algorithm. The algorithm is a high-level mode of consent confirmation
 // that looks for a good-faith indication that the user has provided consent or legal basis signals
 // necessary to perform a privacy-protected activity. The algorithm does not involve the GVL.
@@ -14,21 +13,21 @@ type BasicEnforcement struct {
 	cfg purposeConfig
 }
 
-// LegalBasis determines if legal basis is satisfied for a given purpose and bidder based on user consent
+// LegalBasis determines if legal basis is satisfied for a given purpose and bidder/analytics adapter based on user consent
 // and legal basis signals.
-func (be *BasicEnforcement) LegalBasis(vendorInfo VendorInfo, bidder openrtb_ext.BidderName, consent tcf2.ConsentMetadata, overrides Overrides) bool {
+func (be *BasicEnforcement) LegalBasis(vendorInfo VendorInfo, name string, consent tcf2.ConsentMetadata, overrides Overrides) bool {
 	enforcePurpose, enforceVendors := be.applyEnforceOverrides(overrides)
 
 	if !enforcePurpose && !enforceVendors {
 		return true
 	}
-	if be.cfg.vendorException(bidder) && !overrides.blockVendorExceptions {
+	if be.cfg.vendorException(name) && !overrides.blockVendorExceptions {
 		return true
 	}
-	if !enforcePurpose && be.cfg.basicEnforcementVendor(bidder) {
+	if !enforcePurpose && be.cfg.basicEnforcementVendor(name) {
 		return true
 	}
-	if enforcePurpose && consent.PurposeAllowed(be.cfg.PurposeID) && be.cfg.basicEnforcementVendor(bidder) {
+	if enforcePurpose && consent.PurposeAllowed(be.cfg.PurposeID) && be.cfg.basicEnforcementVendor(name) {
 		return true
 	}
 	if enforcePurpose && consent.PurposeLITransparency(be.cfg.PurposeID) && overrides.allowLITransparency {
diff --git a/gdpr/full_enforcement.go b/gdpr/full_enforcement.go
index c872e13e..9c2b5221 100644
--- a/gdpr/full_enforcement.go
+++ b/gdpr/full_enforcement.go
@@ -2,7 +2,6 @@ package gdpr
 
 import (
 	tcf2 "github.com/prebid/go-gdpr/vendorconsent/tcf2"
-	"github.com/prebid/prebid-server/v2/openrtb_ext"
 )
 
 const (
@@ -11,7 +10,7 @@ const (
 	pubRestrictRequireLegitInterest = 2
 )
 
-// FullEnforcement determines if legal basis is satisfied for a given purpose and bidder using
+// FullEnforcement determines if legal basis is satisfied for a given purpose and bidde/analytics adapterr using
 // the TCF2 full enforcement algorithm. The algorithm is a detailed confirmation that reads the
 // GVL, interprets the consent string and performs legal basis analysis necessary to perform a
 // privacy-protected activity.
@@ -20,9 +19,9 @@ type FullEnforcement struct {
 	cfg purposeConfig
 }
 
-// LegalBasis determines if legal basis is satisfied for a given purpose and bidder based on the
+// LegalBasis determines if legal basis is satisfied for a given purpose and bidder/analytics adapter based on the
 // vendor claims in the GVL, publisher restrictions and user consent.
-func (fe *FullEnforcement) LegalBasis(vendorInfo VendorInfo, bidder openrtb_ext.BidderName, consent tcf2.ConsentMetadata, overrides Overrides) bool {
+func (fe *FullEnforcement) LegalBasis(vendorInfo VendorInfo, name string, consent tcf2.ConsentMetadata, overrides Overrides) bool {
 	enforcePurpose, enforceVendors := fe.applyEnforceOverrides(overrides)
 
 	if consent.CheckPubRestriction(uint8(fe.cfg.PurposeID), pubRestrictNotAllowed, vendorInfo.vendorID) {
@@ -31,7 +30,7 @@ func (fe *FullEnforcement) LegalBasis(vendorInfo VendorInfo, bidder openrtb_ext.
 	if !enforcePurpose && !enforceVendors {
 		return true
 	}
-	if fe.cfg.vendorException(bidder) && !overrides.blockVendorExceptions {
+	if fe.cfg.vendorException(name) && !overrides.blockVendorExceptions {
 		return true
 	}
 
diff --git a/gdpr/impl.go b/gdpr/impl.go
index fd3ad2b2..614c06d9 100644
--- a/gdpr/impl.go
+++ b/gdpr/impl.go
@@ -48,7 +48,7 @@ func (p *permissionsImpl) BidderSyncAllowed(ctx context.Context, bidder openrtb_
 	id, ok := p.vendorIDs[bidder]
 	if ok {
 		vendorExceptions := p.cfg.PurposeVendorExceptions(consentconstants.Purpose(1))
-		_, vendorException := vendorExceptions[bidder]
+		_, vendorException := vendorExceptions[string(bidder)]
 		return p.allowSync(ctx, id, bidder, vendorException)
 	}
 
@@ -138,9 +138,9 @@ func (p *permissionsImpl) allowSync(ctx context.Context, vendorID uint16, bidder
 	}
 
 	purpose := consentconstants.Purpose(1)
-	enforcer := p.purposeEnforcerBuilder(purpose, bidder)
+	enforcer := p.purposeEnforcerBuilder(purpose, string(bidder))
 
-	if enforcer.LegalBasis(vendorInfo, bidder, pc.consentMeta, Overrides{blockVendorExceptions: !vendorException}) {
+	if enforcer.LegalBasis(vendorInfo, string(bidder), pc.consentMeta, Overrides{blockVendorExceptions: !vendorException}) {
 		return true, nil
 	}
 	return false, nil
@@ -149,13 +149,13 @@ func (p *permissionsImpl) allowSync(ctx context.Context, vendorID uint16, bidder
 // allowBidRequest computes legal basis for a given bidder using the enforcement algorithms selected
 // by the purpose enforcer builder
 func (p *permissionsImpl) allowBidRequest(bidder openrtb_ext.BidderName, consentMeta tcf2.ConsentMetadata, vendorInfo VendorInfo) bool {
-	enforcer := p.purposeEnforcerBuilder(consentconstants.Purpose(2), bidder)
+	enforcer := p.purposeEnforcerBuilder(consentconstants.Purpose(2), string(bidder))
 
 	overrides := Overrides{}
 	if _, ok := enforcer.(*BasicEnforcement); ok {
 		overrides.allowLITransparency = true
 	}
-	return enforcer.LegalBasis(vendorInfo, bidder, consentMeta, overrides)
+	return enforcer.LegalBasis(vendorInfo, string(bidder), consentMeta, overrides)
 }
 
 // allowGeo computes legal basis for a given bidder using the configs, consent and GVL pertaining to
@@ -180,13 +180,13 @@ func (p *permissionsImpl) allowGeo(bidder openrtb_ext.BidderName, consentMeta tc
 func (p *permissionsImpl) allowID(bidder openrtb_ext.BidderName, consentMeta tcf2.ConsentMetadata, vendorInfo VendorInfo) bool {
 	for i := 2; i <= 10; i++ {
 		purpose := consentconstants.Purpose(i)
-		enforcer := p.purposeEnforcerBuilder(purpose, bidder)
+		enforcer := p.purposeEnforcerBuilder(purpose, string(bidder))
 
 		overrides := Overrides{enforcePurpose: true, enforceVendors: true}
 		if _, ok := enforcer.(*BasicEnforcement); ok && purpose == consentconstants.Purpose(2) {
 			overrides.allowLITransparency = true
 		}
-		if enforcer.LegalBasis(vendorInfo, bidder, consentMeta, overrides) {
+		if enforcer.LegalBasis(vendorInfo, string(bidder), consentMeta, overrides) {
 			return true
 		}
 	}
diff --git a/gdpr/purpose_config.go b/gdpr/purpose_config.go
index 09edef94..ff3a9230 100644
--- a/gdpr/purpose_config.go
+++ b/gdpr/purpose_config.go
@@ -3,7 +3,6 @@ package gdpr
 import (
 	"github.com/prebid/go-gdpr/consentconstants"
 	"github.com/prebid/prebid-server/v2/config"
-	"github.com/prebid/prebid-server/v2/openrtb_ext"
 )
 
 // purposeConfig represents all of the config info selected from the host and account configs for
@@ -13,26 +12,26 @@ type purposeConfig struct {
 	EnforceAlgo                config.TCF2EnforcementAlgo
 	EnforcePurpose             bool
 	EnforceVendors             bool
-	VendorExceptionMap         map[openrtb_ext.BidderName]struct{}
+	VendorExceptionMap         map[string]struct{}
 	BasicEnforcementVendorsMap map[string]struct{}
 }
 
-// basicEnforcementVendor returns true if a given bidder is configured as a basic enforcement vendor
+// basicEnforcementVendor returns true if a given bidder/analytics adapter is configured as a basic enforcement vendor
 // for the purpose
-func (pc *purposeConfig) basicEnforcementVendor(bidder openrtb_ext.BidderName) bool {
+func (pc *purposeConfig) basicEnforcementVendor(name string) bool {
 	if pc.BasicEnforcementVendorsMap == nil {
 		return false
 	}
-	_, found := pc.BasicEnforcementVendorsMap[string(bidder)]
+	_, found := pc.BasicEnforcementVendorsMap[name]
 	return found
 }
 
-// vendorException returns true if a given bidder is configured as a vendor exception
+// vendorException returns true if a given bidder/analytics adapter is configured as a vendor exception
 // for the purpose
-func (pc *purposeConfig) vendorException(bidder openrtb_ext.BidderName) bool {
+func (pc *purposeConfig) vendorException(name string) bool {
 	if pc.VendorExceptionMap == nil {
 		return false
 	}
-	_, found := pc.VendorExceptionMap[bidder]
+	_, found := pc.VendorExceptionMap[name]
 	return found
 }
diff --git a/gdpr/purpose_enforcer.go b/gdpr/purpose_enforcer.go
index 4a138e76..ceef0e5f 100644
--- a/gdpr/purpose_enforcer.go
+++ b/gdpr/purpose_enforcer.go
@@ -10,11 +10,11 @@ import (
 
 // PurposeEnforcer represents the enforcement strategy for determining if legal basis is achieved for a purpose
 type PurposeEnforcer interface {
-	LegalBasis(vendorInfo VendorInfo, bidder openrtb_ext.BidderName, consent tcf2.ConsentMetadata, overrides Overrides) bool
+	LegalBasis(vendorInfo VendorInfo, name string, consent tcf2.ConsentMetadata, overrides Overrides) bool
 }
 
 // PurposeEnforcerBuilder generates an instance of PurposeEnforcer for a given purpose and bidder
-type PurposeEnforcerBuilder func(p consentconstants.Purpose, bidder openrtb_ext.BidderName) PurposeEnforcer
+type PurposeEnforcerBuilder func(p consentconstants.Purpose, name string) PurposeEnforcer
 
 // Overrides specifies enforcement algorithm rule adjustments
 type Overrides struct {
@@ -45,7 +45,7 @@ type PurposeEnforcers struct {
 func NewPurposeEnforcerBuilder(cfg TCF2ConfigReader) PurposeEnforcerBuilder {
 	cachedEnforcers := make([]PurposeEnforcers, 10)
 
-	return func(purpose consentconstants.Purpose, bidder openrtb_ext.BidderName) PurposeEnforcer {
+	return func(purpose consentconstants.Purpose, name string) PurposeEnforcer {
 		index := purpose - 1
 
 		var basicEnforcementVendor bool
@@ -53,7 +53,7 @@ func NewPurposeEnforcerBuilder(cfg TCF2ConfigReader) PurposeEnforcerBuilder {
 			basicEnforcementVendor = false
 		} else {
 			basicEnforcementVendors := cfg.BasicEnforcementVendors()
-			_, basicEnforcementVendor = basicEnforcementVendors[string(bidder)]
+			_, basicEnforcementVendor = basicEnforcementVendors[name]
 		}
 
 		enforceAlgo := cfg.PurposeEnforcementAlgo(purpose)
diff --git a/main.go b/main.go
index e72f02f1..1909199d 100644
--- a/main.go
+++ b/main.go
@@ -8,11 +8,13 @@ import (
 	"runtime"
 	"time"
 
+	jsoniter "github.com/json-iterator/go"
 	"github.com/prebid/prebid-server/v2/config"
 	"github.com/prebid/prebid-server/v2/currency"
 	"github.com/prebid/prebid-server/v2/openrtb_ext"
 	"github.com/prebid/prebid-server/v2/router"
 	"github.com/prebid/prebid-server/v2/server"
+	"github.com/prebid/prebid-server/v2/util/jsonutil"
 	"github.com/prebid/prebid-server/v2/util/task"
 
 	"github.com/golang/glog"
@@ -21,6 +23,7 @@ import (
 
 func init() {
 	rand.Seed(time.Now().UnixNano())
+	jsoniter.RegisterExtension(&jsonutil.RawMessageExtension{})
 }
 
 func main() {
diff --git a/modules/generator/builder.tmpl b/modules/generator/builder.tmpl
index b7b78103..db0cd1a4 100644
--- a/modules/generator/builder.tmpl
+++ b/modules/generator/builder.tmpl
@@ -1,20 +1,21 @@
 package modules
-
-{{if .}}
 import (
-    {{- range .}}
-    {{.Vendor}}{{.Module | Title}} "github.com/prebid/prebid-server/v2/modules/{{.Vendor}}/{{.Module}}"
+{{- range $vendor, $modules := .}}
+    {{- range $module := $modules}}
+        {{$vendor}}{{$module | Title}} "github.com/prebid/prebid-server/v2/modules/{{$vendor}}/{{$module}}"
     {{- end}}
+{{- end}}
 )
-{{end}}
 
 // builders returns mapping between module name and its builder
 // vendor and module names are chosen based on the module directory name
 func builders() ModuleBuilders {
     return ModuleBuilders{
-        {{- range .}}
-        "{{.Vendor}}": {
-            "{{.Module}}": {{.Vendor}}{{.Module | Title}}.Builder,
+        {{- range $vendor, $modules := .}}
+        "{{$vendor}}": {
+            {{- range $module := $modules}}
+            "{{$module}}": {{$vendor}}{{$module | Title}}.Builder,
+            {{- end}}
         },
         {{- end}}
     }
diff --git a/modules/generator/buildergen.go b/modules/generator/buildergen.go
index b906682b..21993242 100644
--- a/modules/generator/buildergen.go
+++ b/modules/generator/buildergen.go
@@ -10,6 +10,7 @@ import (
 	"os"
 	"path/filepath"
 	"regexp"
+	"sort"
 	"strings"
 	"text/template"
 )
@@ -20,26 +21,26 @@ var (
 	outName  = "builder.go"
 )
 
-type Module struct {
-	Vendor string
-	Module string
-}
-
 func main() {
-	var modules []Module
+	modules := make(map[string][]string)
 
 	filepath.WalkDir("./", func(path string, d fs.DirEntry, err error) error {
 		if !r.MatchString(path) {
 			return nil
 		}
 		match := r.FindStringSubmatch(path)
-		modules = append(modules, Module{
-			Vendor: match[1],
-			Module: match[2],
-		})
+		vendorModules := modules[match[1]]
+		vendorModules = append(vendorModules, match[2])
+		modules[match[1]] = vendorModules
+
 		return nil
 	})
 
+	for vendorName, names := range modules {
+		sort.Strings(names)
+		modules[vendorName] = names
+	}
+
 	funcMap := template.FuncMap{"Title": strings.Title}
 	t, err := template.New(tmplName).Funcs(funcMap).ParseFiles(fmt.Sprintf("generator/%s", tmplName))
 	if err != nil {
diff --git a/openrtb_ext/bid.go b/openrtb_ext/bid.go
index 2e190389..7d3dcbd7 100644
--- a/openrtb_ext/bid.go
+++ b/openrtb_ext/bid.go
@@ -7,6 +7,7 @@ import (
 
 // ExtBid defines the contract for bidresponse.seatbid.bid[i].ext
 type ExtBid struct {
+	DSA    *ExtBidDSA    `json:"dsa,omitempty"`
 	Prebid *ExtBidPrebid `json:"prebid,omitempty"`
 }
 
@@ -83,6 +84,13 @@ type ExtBidPrebidEvents struct {
 	Imp string `json:"imp,omitempty"`
 }
 
+// ExtBidDSA defines the contract for bidresponse.seatbid.bid[i].ext.dsa
+type ExtBidDSA struct {
+	AdRender *int8  `json:"adrender,omitempty"`
+	Behalf   string `json:"behalf,omitempty"`
+	Paid     string `json:"paid,omitempty"`
+}
+
 // BidType describes the allowed values for bidresponse.seatbid.bid[i].ext.prebid.type
 type BidType string
 
diff --git a/openrtb_ext/bidders.go b/openrtb_ext/bidders.go
index 173120f7..fcd2b15c 100644
--- a/openrtb_ext/bidders.go
+++ b/openrtb_ext/bidders.go
@@ -612,6 +612,7 @@ func NewBidderParamsValidator(schemaDirectory string) (BidderParamValidator, err
 		if _, ok := bidderMap[bidderName]; !ok {
 			return nil, fmt.Errorf("File %s/%s does not match a valid BidderName.", schemaDirectory, fileInfo.Name())
 		}
+
 		toOpen, err := paramsValidator.abs(filepath.Join(schemaDirectory, fileInfo.Name()))
 		if err != nil {
 			return nil, fmt.Errorf("Failed to get an absolute representation of the path: %s, %v", toOpen, err)
diff --git a/openrtb_ext/imp_seedingAlliance.go b/openrtb_ext/imp_seedingAlliance.go
index 0f594d3c..d383ad39 100644
--- a/openrtb_ext/imp_seedingAlliance.go
+++ b/openrtb_ext/imp_seedingAlliance.go
@@ -1,6 +1,7 @@
 package openrtb_ext
 
 type ImpExtSeedingAlliance struct {
-	AdUnitID string `json:"adUnitId"`
-	SeatID   string `json:"seatId"`
+	AdUnitID  string `json:"adUnitId"`
+	SeatID    string `json:"seatId"`
+	AccountID string `json:"accountId"`
 }
diff --git a/openrtb_ext/regs.go b/openrtb_ext/regs.go
index 8057ce2c..eca5ff98 100644
--- a/openrtb_ext/regs.go
+++ b/openrtb_ext/regs.go
@@ -16,5 +16,7 @@ type ExtRegs struct {
 // ExtRegsDSA defines the contract for bidrequest.regs.ext.dsa
 type ExtRegsDSA struct {
 	// Required should be a between 0 and 3 inclusive, see https://github.com/InteractiveAdvertisingBureau/openrtb/blob/main/extensions/community_extensions/dsa_transparency.md
-	Required int8 `json:"dsarequired,omitempty"`
+	Required *int8 `json:"dsarequired,omitempty"`
+	// PubRender should be between 0 and 2 inclusive, see https://github.com/InteractiveAdvertisingBureau/openrtb/blob/main/extensions/community_extensions/dsa_transparency.md
+	PubRender *int8 `json:"pubrender,omitempty"`
 }
diff --git a/openrtb_ext/request_wrapper.go b/openrtb_ext/request_wrapper.go
index d7ff5acc..4c4eb9a6 100644
--- a/openrtb_ext/request_wrapper.go
+++ b/openrtb_ext/request_wrapper.go
@@ -60,6 +60,7 @@ const (
 	dataKey                             = "data"
 	schainKey                           = "schain"
 	us_privacyKey                       = "us_privacy"
+	cdepKey                             = "cdep"
 )
 
 // LenImp returns the number of impressions without causing the creation of ImpWrapper objects.
@@ -883,6 +884,8 @@ type DeviceExt struct {
 	extDirty    bool
 	prebid      *ExtDevicePrebid
 	prebidDirty bool
+	cdep        string
+	cdepDirty   bool
 }
 
 func (de *DeviceExt) unmarshal(extJson json.RawMessage) error {
@@ -910,6 +913,13 @@ func (de *DeviceExt) unmarshal(extJson json.RawMessage) error {
 		}
 	}
 
+	cdepJson, hasCDep := de.ext[cdepKey]
+	if hasCDep && cdepJson != nil {
+		if err := jsonutil.Unmarshal(cdepJson, &de.cdep); err != nil {
+			return err
+		}
+	}
+
 	return nil
 }
 
@@ -931,6 +941,19 @@ func (de *DeviceExt) marshal() (json.RawMessage, error) {
 		de.prebidDirty = false
 	}
 
+	if de.cdepDirty {
+		if len(de.cdep) > 0 {
+			rawjson, err := jsonutil.Marshal(de.cdep)
+			if err != nil {
+				return nil, err
+			}
+			de.ext[cdepKey] = rawjson
+		} else {
+			delete(de.ext, cdepKey)
+		}
+		de.cdepDirty = false
+	}
+
 	de.extDirty = false
 	if len(de.ext) == 0 {
 		return nil, nil
@@ -939,7 +962,7 @@ func (de *DeviceExt) marshal() (json.RawMessage, error) {
 }
 
 func (de *DeviceExt) Dirty() bool {
-	return de.extDirty || de.prebidDirty
+	return de.extDirty || de.prebidDirty || de.cdepDirty
 }
 
 func (de *DeviceExt) GetExt() map[string]json.RawMessage {
@@ -968,6 +991,15 @@ func (de *DeviceExt) SetPrebid(prebid *ExtDevicePrebid) {
 	de.prebidDirty = true
 }
 
+func (de *DeviceExt) GetCDep() string {
+	return de.cdep
+}
+
+func (de *DeviceExt) SetCDep(cdep string) {
+	de.cdep = cdep
+	de.cdepDirty = true
+}
+
 func (de *DeviceExt) Clone() *DeviceExt {
 	if de == nil {
 		return nil
diff --git a/router/router.go b/router/router.go
index d89d1f59..c285bbda 100644
--- a/router/router.go
+++ b/router/router.go
@@ -93,7 +93,7 @@ func newJsonDirectoryServer(schemaDirectory string, validator openrtb_ext.Bidder
 		data[aliasName] = bidderData
 	}
 
-	response, err := json.Marshal(data)
+	response, err := jsonutil.Marshal(data)
 	if err != nil {
 		glog.Fatalf("Failed to marshal bidder param JSON-schema: %v", err)
 	}
diff --git a/static/bidder-info/adf.yaml b/static/bidder-info/adf.yaml
index 2310f346..d7d9eccf 100644
--- a/static/bidder-info/adf.yaml
+++ b/static/bidder-info/adf.yaml
@@ -1,4 +1,12 @@
+# Please uncomment the appropriate endpoint URL for your datacenter
+# Europe
 endpoint: "https://adx.adform.net/adx/openrtb"
+# North/South America
+# endpoint: "https://adx2.adform.net/adx/openrtb"
+# APAC
+# endpoint: "https://adx3.adform.net/adx/openrtb"
+geoscope:
+  - global
 maintainer:
   email: "scope.sspp@adform.com"
 gvlVendorID: 50
diff --git a/static/bidder-info/bmtm.yaml b/static/bidder-info/bmtm.yaml
index 3d3f2ca6..27452195 100644
--- a/static/bidder-info/bmtm.yaml
+++ b/static/bidder-info/bmtm.yaml
@@ -1,6 +1,6 @@
 endpoint: "https://one.elitebidder.com/api/pbs"
 maintainer:
-  email: dev@brightmountainmedia.com
+  email: product@brightmountainmedia.com
 modifyingVastXmlAllowed: false
 capabilities:
   app:
diff --git a/static/bidder-info/criteo.yaml b/static/bidder-info/criteo.yaml
index 7d58c68d..d6419d46 100644
--- a/static/bidder-info/criteo.yaml
+++ b/static/bidder-info/criteo.yaml
@@ -15,8 +15,8 @@ userSync:
   # criteo supports user syncing, but requires configuration by the host. contact this
   # bidder directly at the email address in this file to ask about enabling user sync.
   redirect:
-    url: "https://ssp-sync.criteo.com/user-sync/redirect?gdprapplies={{.GDPR}}&gdpr={{.GDPRConsent}}&ccpa={{.USPrivacy}}&redir={{.RedirectURL}}&profile=230"
+    url: "https://ssp-sync.criteo.com/user-sync/redirect?gdprapplies={{.GDPR}}&gdpr={{.GDPRConsent}}&ccpa={{.USPrivacy}}&gpp={{.GPP}}&gpp_sid={{.GPPSID}}&redir={{.RedirectURL}}&profile=230"
     userMacro: "${CRITEO_USER_ID}"
   iframe:
-    url: "https://ssp-sync.criteo.com/user-sync/iframe?gdprapplies={{.GDPR}}&gdpr={{.GDPRConsent}}&ccpa={{.USPrivacy}}&redir={{.RedirectURL}}&profile=230"
+    url: "https://ssp-sync.criteo.com/user-sync/iframe?gdprapplies={{.GDPR}}&gdpr={{.GDPRConsent}}&ccpa={{.USPrivacy}}&gpp={{.GPP}}&gpp_sid={{.GPPSID}}&redir={{.RedirectURL}}&profile=230"
     userMacro: "${CRITEO_USER_ID}"
\ No newline at end of file
diff --git a/static/bidder-info/grid.yaml b/static/bidder-info/grid.yaml
index bf1832c9..9d9e7aa5 100644
--- a/static/bidder-info/grid.yaml
+++ b/static/bidder-info/grid.yaml
@@ -15,5 +15,5 @@ capabilities:
       - native
 userSync:
   redirect:
-    url: "https://x.bidswitch.net/check_uuid/{{.RedirectURL}}?gdpr={{.GDPR}}&gdpr_consent={{.GDPRConsent}}&us_privacy={{.USPrivacy}}"
+    url: "https://x.bidswitch.net/check_uuid/{{.RedirectURL}}?gdpr={{.GDPR}}&gdpr_consent={{.GDPRConsent}}&gpp={{.GPP}}&gpp_sid={{.GPPSID}}&us_privacy={{.USPrivacy}}"
     userMacro: "${BSW_UUID}"
diff --git a/static/bidder-info/medianet.yaml b/static/bidder-info/medianet.yaml
index ad741e8f..1b23d5ae 100644
--- a/static/bidder-info/medianet.yaml
+++ b/static/bidder-info/medianet.yaml
@@ -4,6 +4,8 @@ maintainer:
   email: "prebid-support@media.net"
 gvlVendorID: 142
 endpointCompression: gzip
+openrtb:
+  version: 2.6
 modifyingVastXmlAllowed: true
 capabilities:
   app:
diff --git a/static/bidder-info/minutemedia.yaml b/static/bidder-info/minutemedia.yaml
index efe58e57..55f5a2b7 100644
--- a/static/bidder-info/minutemedia.yaml
+++ b/static/bidder-info/minutemedia.yaml
@@ -14,5 +14,5 @@ capabilities:
       - video
 userSync:
   iframe:
-    url: https://pbs-cs.minutemedia-prebid.com/pbs-iframe?gdpr={{.GDPR}}&gdpr_consent={{.GDPRConsent}}&us_privacy={{.USPrivacy}}&redirect={{.RedirectURL}}
+    url: https://pbs-cs.minutemedia-prebid.com/pbs-iframe?gdpr={{.GDPR}}&gdpr_consent={{.GDPRConsent}}&us_privacy={{.USPrivacy}}&gpp={{.GPP}}&gpp_sid={{.GPPSID}}&redirect={{.RedirectURL}}
     userMacro: "[PBS_UID]"
diff --git a/static/bidder-info/rise.yaml b/static/bidder-info/rise.yaml
index 72e56c33..79b64d5a 100644
--- a/static/bidder-info/rise.yaml
+++ b/static/bidder-info/rise.yaml
@@ -14,5 +14,5 @@ capabilities:
       - video
 userSync:
   iframe:
-    url: https://pbs-cs.yellowblue.io/pbs-iframe?gdpr={{.GDPR}}&gdpr_consent={{.GDPRConsent}}&us_privacy={{.USPrivacy}}&redirect={{.RedirectURL}}
+    url: https://pbs-cs.yellowblue.io/pbs-iframe?gdpr={{.GDPR}}&gdpr_consent={{.GDPRConsent}}&us_privacy={{.USPrivacy}}&gpp={{.GPP}}&gpp_sid={{.GPPSID}}&redirect={{.RedirectURL}}
     userMacro: "[PBS_UID]"
diff --git a/static/bidder-info/taboola.yaml b/static/bidder-info/taboola.yaml
index 436f7469..0fccee14 100644
--- a/static/bidder-info/taboola.yaml
+++ b/static/bidder-info/taboola.yaml
@@ -13,8 +13,8 @@ capabilities:
       - native
 userSync:
   redirect:
-    url: https://trc.taboola.com/sg/ps/1/cm?gdpr={{.GDPR}}&gdpr_consent={{.GDPRConsent}}&us_privacy={{.USPrivacy}}&redirect={{.RedirectURL}}
+    url: "https://trc.taboola.com/sg/ps/1/cm?gdpr={{.GDPR}}&gdpr_consent={{.GDPRConsent}}&us_privacy={{.USPrivacy}}&gpp={{.GPP}}&gpp_sid={{.GPPSID}}&redirect={{.RedirectURL}}"
     userMacro: "<TUID>"
   iframe:
-    url: https://cdn.taboola.com/scripts/ps-sync.html?gdpr={{.GDPR}}&gdpr_consent={{.GDPRConsent}}&us_privacy={{.USPrivacy}}&redirect={{.RedirectURL}}
+    url: "https://cdn.taboola.com/scripts/ps-sync.html?gdpr={{.GDPR}}&gdpr_consent={{.GDPRConsent}}&us_privacy={{.USPrivacy}}&gpp={{.GPP}}&gpp_sid={{.GPPSID}}&redirect={{.RedirectURL}}"
     userMacro: "<TUID>"
diff --git a/static/bidder-params/seedingAlliance.json b/static/bidder-params/seedingAlliance.json
index 5fe463b6..d7208623 100644
--- a/static/bidder-params/seedingAlliance.json
+++ b/static/bidder-params/seedingAlliance.json
@@ -11,7 +11,11 @@
         },
         "seatId": {
             "type": "string",
-            "description": "Seat ID"
+            "description": "Deprecated, please use accountId"
+        },
+        "accountId": {
+            "type": "string",
+            "description": "Account ID of partner"
         }
     },
     "required": [
diff --git a/static/index.html b/static/index.html
index a3518ce2..ac79079f 100644
--- a/static/index.html
+++ b/static/index.html
@@ -3,7 +3,7 @@
         <title>Prebid Server</title>
     </head>
     <body>
-        Prebid Server is a server-to-server proxy for <a href="http://prebid.org">Prebid.js</a> users.
+        Prebid Server is a server-to-server proxy for <a href="https://prebid.org">Prebid.js</a> users.
         The host is not responsible for the content or advertising delivered through this proxy. For more information, 
         please contact prebid-server - at - prebid.org.
     </body>
diff --git a/stored_requests/backends/db_fetcher/fetcher.go b/stored_requests/backends/db_fetcher/fetcher.go
index 3a9b83f7..d6fade0e 100644
--- a/stored_requests/backends/db_fetcher/fetcher.go
+++ b/stored_requests/backends/db_fetcher/fetcher.go
@@ -152,7 +152,7 @@ func (fetcher *dbFetcher) FetchResponses(ctx context.Context, ids []string) (dat
 }
 
 func (fetcher *dbFetcher) FetchAccount(ctx context.Context, accountDefaultsJSON json.RawMessage, accountID string) (json.RawMessage, []error) {
-	return nil, []error{stored_requests.NotFoundError{accountID, "Account"}}
+	return nil, []error{stored_requests.NotFoundError{ID: accountID, DataType: "Account"}}
 }
 
 func (fetcher *dbFetcher) FetchCategories(ctx context.Context, primaryAdServer, publisherId, iabCategory string) (string, error) {
diff --git a/stored_requests/backends/empty_fetcher/fetcher.go b/stored_requests/backends/empty_fetcher/fetcher.go
index c851a997..65e5de90 100644
--- a/stored_requests/backends/empty_fetcher/fetcher.go
+++ b/stored_requests/backends/empty_fetcher/fetcher.go
@@ -33,7 +33,7 @@ func (fetcher EmptyFetcher) FetchResponses(ctx context.Context, ids []string) (d
 }
 
 func (fetcher EmptyFetcher) FetchAccount(ctx context.Context, accountDefaultJSON json.RawMessage, accountID string) (json.RawMessage, []error) {
-	return nil, []error{stored_requests.NotFoundError{accountID, "Account"}}
+	return nil, []error{stored_requests.NotFoundError{ID: accountID, DataType: "Account"}}
 }
 
 func (fetcher EmptyFetcher) FetchCategories(ctx context.Context, primaryAdServer, publisherId, iabCategory string) (string, error) {
diff --git a/util/jsonutil/jsonutil.go b/util/jsonutil/jsonutil.go
index b5bb47cc..695ccd8a 100644
--- a/util/jsonutil/jsonutil.go
+++ b/util/jsonutil/jsonutil.go
@@ -5,8 +5,10 @@ import (
 	"encoding/json"
 	"io"
 	"strings"
+	"unsafe"
 
 	jsoniter "github.com/json-iterator/go"
+	"github.com/modern-go/reflect2"
 	"github.com/prebid/prebid-server/v2/errortypes"
 )
 
@@ -211,3 +213,38 @@ func tryExtractErrorMessage(err error) string {
 func isLikelyDetailedErrorMessage(msg string) bool {
 	return !strings.HasPrefix(msg, "request.")
 }
+
+// RawMessageExtension will call json.Compact() on every json.RawMessage field when getting marshalled.
+type RawMessageExtension struct {
+	jsoniter.DummyExtension
+}
+
+// CreateEncoder substitutes the default jsoniter encoder of the json.RawMessage type with ours, that
+// calls json.Compact() before writting to the stream
+func (e *RawMessageExtension) CreateEncoder(typ reflect2.Type) jsoniter.ValEncoder {
+	if typ == jsonRawMessageType {
+		return &rawMessageCodec{}
+	}
+	return nil
+}
+
+var jsonRawMessageType = reflect2.TypeOfPtr(&json.RawMessage{}).Elem()
+
+// rawMessageCodec implements jsoniter.ValEncoder interface so we can override the default json.RawMessage Encode()
+// function with our implementation
+type rawMessageCodec struct{}
+
+func (codec *rawMessageCodec) Encode(ptr unsafe.Pointer, stream *jsoniter.Stream) {
+	if ptr != nil {
+		jsonRawMsg := *(*[]byte)(ptr)
+
+		dst := bytes.NewBuffer(make([]byte, 0, len(jsonRawMsg)))
+		if err := json.Compact(dst, jsonRawMsg); err == nil {
+			stream.Write(dst.Bytes())
+		}
+	}
+}
+
+func (codec *rawMessageCodec) IsEmpty(ptr unsafe.Pointer) bool {
+	return ptr == nil || len(*((*json.RawMessage)(ptr))) == 0
+}
diff --git a/validate.sh b/validate.sh
index d263df0a..ca4fbc1d 100755
--- a/validate.sh
+++ b/validate.sh
@@ -36,5 +36,5 @@ fi
 
 if $VET; then
   echo "Running go vet check"
-  go vet -composites=false ./...
+  go vet ./...
 fi


Test patch:
diff --git a/account/account_test.go b/account/account_test.go
index 369c2d2c..7788f7c4 100644
--- a/account/account_test.go
+++ b/account/account_test.go
@@ -122,7 +122,7 @@ func TestGetAccount(t *testing.T) {
 func TestSetDerivedConfig(t *testing.T) {
 	tests := []struct {
 		description              string
-		purpose1VendorExceptions []openrtb_ext.BidderName
+		purpose1VendorExceptions []string
 		feature1VendorExceptions []openrtb_ext.BidderName
 		basicEnforcementVendors  []string
 		enforceAlgo              string
@@ -134,11 +134,11 @@ func TestSetDerivedConfig(t *testing.T) {
 		},
 		{
 			description:              "One purpose 1 vendor exception",
-			purpose1VendorExceptions: []openrtb_ext.BidderName{"appnexus"},
+			purpose1VendorExceptions: []string{"appnexus"},
 		},
 		{
 			description:              "Multiple purpose 1 vendor exceptions",
-			purpose1VendorExceptions: []openrtb_ext.BidderName{"appnexus", "rubicon"},
+			purpose1VendorExceptions: []string{"appnexus", "rubicon"},
 		},
 		{
 			description:              "Nil feature 1 vendor exceptions",
@@ -192,7 +192,7 @@ func TestSetDerivedConfig(t *testing.T) {
 
 		setDerivedConfig(&account)
 
-		purpose1ExceptionMapKeys := make([]openrtb_ext.BidderName, 0)
+		purpose1ExceptionMapKeys := make([]string, 0)
 		for k := range account.GDPR.Purpose1.VendorExceptionMap {
 			purpose1ExceptionMapKeys = append(purpose1ExceptionMapKeys, k)
 		}
diff --git a/adapters/adnuntius/adnuntiustest/supplemental/site-ext.json b/adapters/adnuntius/adnuntiustest/supplemental/site-ext.json
new file mode 100644
index 00000000..1d1ef3d3
--- /dev/null
+++ b/adapters/adnuntius/adnuntiustest/supplemental/site-ext.json
@@ -0,0 +1,113 @@
+{
+	"mockBidRequest": {
+		"id": "test-request-id",
+		"user": {
+			"id": "1kjh3429kjh295jkl"
+		},
+		"site": {
+			"ext":{
+				"data" : {
+					"key": ["value"]
+				}
+			}
+		},
+		"imp": [
+			{
+				"id": "test-imp-id",
+				"banner": {
+					"format": [
+						{
+							"w": 300,
+							"h": 250
+						},
+						{
+							"w": 300,
+							"h": 600
+						}
+					]
+				},
+				"ext": {
+					"bidder": {
+						"auId": "123"
+					}
+				}
+			}
+		]
+	},
+	"httpCalls": [
+		{
+			"expectedRequest": {
+				"uri": "http://whatever.url?format=json&tzo=0",
+				"body": {
+					"adUnits": [
+						{
+							"auId": "123",
+							"targetId": "123-test-imp-id",
+							"dimensions": [[300,250],[300,600]]
+						}
+					],
+					"kv": {
+						"key": ["value"]
+					},
+					"metaData": {
+						"usi": "1kjh3429kjh295jkl"
+					},
+					"context": "unknown"
+				}
+			},
+			"mockResponse": {
+				"status": 200,
+				"body": {
+					"adUnits": [
+						{
+							"auId": "0000000000000123",
+							"targetId": "123-test-imp-id",
+							"html": "<ADCODE>",
+							"responseId": "adn-rsp-900646517",
+							"ads": [
+								{
+									"destinationUrls": {
+										"url": "http://www.google.com"
+									},
+									"bid": {
+										"amount": 20.0,
+										"currency": "NOK"
+									},
+									"adId": "adn-id-1559784094",
+									"creativeWidth": "980",
+									"creativeHeight": "240",
+									"creativeId": "jn9hpzvlsf8cpdmm",
+									"lineItemId": "q7y9qm5b0xt9htrv"
+								}
+							]
+						}
+					]
+				}
+			}
+		}
+	],
+	"expectedBidResponses": [
+		{
+			"bids": [
+				{
+					"bid": {
+						"id": "adn-id-1559784094",
+						"impid": "test-imp-id",
+						"price": 20000,
+						"adm": "<ADCODE>",
+						"adid": "adn-id-1559784094",
+						"adomain": [
+							"google.com"
+						],
+						"cid": "q7y9qm5b0xt9htrv",
+						"crid": "jn9hpzvlsf8cpdmm",
+						"w": 980,
+						"h": 240
+					},
+					"type": "banner"
+				}
+			],
+			"currency": "NOK"
+		}
+	]
+}
\ No newline at end of file
diff --git a/adapters/adnuntius/adnuntiustest/supplemental/user-ext.json b/adapters/adnuntius/adnuntiustest/supplemental/user-ext.json
new file mode 100644
index 00000000..7d5f8937
--- /dev/null
+++ b/adapters/adnuntius/adnuntiustest/supplemental/user-ext.json
@@ -0,0 +1,112 @@
+{
+	"mockBidRequest": {
+		"id": "test-request-id",
+		"user": {
+			"ext":{
+				"eids" : [
+					{ 
+						"source": "idProvider", 
+						"uids": [
+							{ "id": "userId", "atype": 1, "ext": { "stype": "ppuid" } }
+						] 
+					}
+				]
+			}
+		},
+		"imp": [
+			{
+				"id": "test-imp-id",
+				"banner": {
+					"format": [
+						{
+							"w": 300,
+							"h": 250
+						},
+						{
+							"w": 300,
+							"h": 600
+						}
+					]
+				},
+				"ext": {
+					"bidder": {
+						"auId": "123"
+					}
+				}
+			}
+		]
+	},
+	"httpCalls": [
+		{
+			"expectedRequest": {
+				"uri": "http://whatever.url?format=json&tzo=0",
+				"body": {
+					"adUnits": [
+						{
+							"auId": "123",
+							"targetId": "123-test-imp-id",
+							"dimensions": [[300,250],[300,600]]
+						}
+					],
+					"metaData": {
+						"usi": "userId"
+					},
+					"context": "unknown"
+				}
+			},
+			"mockResponse": {
+				"status": 200,
+				"body": {
+					"adUnits": [
+						{
+							"auId": "0000000000000123",
+							"targetId": "123-test-imp-id",
+							"html": "<ADCODE>",
+							"responseId": "adn-rsp-900646517",
+							"ads": [
+								{
+									"destinationUrls": {
+										"url": "http://www.google.com"
+									},
+									"bid": {
+										"amount": 20.0,
+										"currency": "NOK"
+									},
+									"adId": "adn-id-1559784094",
+									"creativeWidth": "980",
+									"creativeHeight": "240",
+									"creativeId": "jn9hpzvlsf8cpdmm",
+									"lineItemId": "q7y9qm5b0xt9htrv"
+								}
+							]
+						}
+					]
+				}
+			}
+		}
+	],
+	"expectedBidResponses": [
+		{
+			"bids": [
+				{
+					"bid": {
+						"id": "adn-id-1559784094",
+						"impid": "test-imp-id",
+						"price": 20000,
+						"adm": "<ADCODE>",
+						"adid": "adn-id-1559784094",
+						"adomain": [
+							"google.com"
+						],
+						"cid": "q7y9qm5b0xt9htrv",
+						"crid": "jn9hpzvlsf8cpdmm",
+						"w": 980,
+						"h": 240
+					},
+					"type": "banner"
+				}
+			],
+			"currency": "NOK"
+		}
+	]
+}
\ No newline at end of file
diff --git a/adapters/medianet/medianettest/supplemental/valid-req-200-bid-response-from-mnet.json b/adapters/medianet/medianettest/supplemental/valid-req-200-bid-response-from-mnet.json
index e6a9b654..7925ab3f 100644
--- a/adapters/medianet/medianettest/supplemental/valid-req-200-bid-response-from-mnet.json
+++ b/adapters/medianet/medianettest/supplemental/valid-req-200-bid-response-from-mnet.json
@@ -36,9 +36,7 @@
       "USD"
     ],
     "regs": {
-      "ext": {
-        "gdpr": 0
-      }
+      "gdpr": 0
     }
   },
   "httpCalls": [{
@@ -81,9 +79,7 @@
           "USD"
         ],
         "regs": {
-          "ext": {
-            "gdpr": 0
-          }
+          "gdpr": 0
         }
       }
     },
diff --git a/adapters/seedingAlliance/seedingAlliance_test.go b/adapters/seedingAlliance/seedingAlliance_test.go
index 6d9460b8..ae9b888e 100644
--- a/adapters/seedingAlliance/seedingAlliance_test.go
+++ b/adapters/seedingAlliance/seedingAlliance_test.go
@@ -136,24 +136,27 @@ func TestGetMediaTypeForBid(t *testing.T) {
 
 func TestGetExtInfo(t *testing.T) {
 	type args struct {
-		adUnitId string
-		seatId   string
+		adUnitId  string
+		seatId    string
+		accountId string
 	}
 	tests := []struct {
-		name             string
-		expectedAdUnitID string
-		expectedSeatID   string
-		data             args
-		wantErr          bool
+		name              string
+		expectedAdUnitID  string
+		expectedAccountId string
+		data              args
+		wantErr           bool
 	}{
 		{"regular case", "abc123", "pbs", args{adUnitId: "abc123"}, false},
 		{"nil case", "", "pbs", args{adUnitId: ""}, false},
 		{"unmarshal err case", "", "pbs", args{adUnitId: ""}, true},
 		{"seatId case", "abc123", "seat1", args{adUnitId: "abc123", seatId: "seat1"}, false},
+		{"accountId case", "abc123", "account1", args{adUnitId: "abc123", accountId: "account1"}, false},
+		{"accountId and seatId case", "abc123", "account1", args{adUnitId: "abc123", accountId: "account1", seatId: "seat1"}, false},
 	}
 
 	for _, test := range tests {
-		extSA, err := json.Marshal(openrtb_ext.ImpExtSeedingAlliance{AdUnitID: test.data.adUnitId, SeatID: test.data.seatId})
+		extSA, err := json.Marshal(openrtb_ext.ImpExtSeedingAlliance{AdUnitID: test.data.adUnitId, SeatID: test.data.seatId, AccountID: test.data.accountId})
 		if err != nil {
 			t.Fatalf("unexpected error %v", err)
 		}
@@ -168,7 +171,7 @@ func TestGetExtInfo(t *testing.T) {
 		}
 
 		ortbImp := openrtb2.Imp{Ext: extBidder}
-		seatID, err := getExtInfo(&ortbImp)
+		accountId, err := getExtInfo(&ortbImp)
 		if err != nil {
 			if test.wantErr {
 				continue
@@ -180,8 +183,8 @@ func TestGetExtInfo(t *testing.T) {
 			t.Fatalf("want: %v, got: %v", test.expectedAdUnitID, ortbImp.TagID)
 		}
 
-		if test.expectedSeatID != seatID {
-			t.Fatalf("want: %v, got: %v", test.expectedSeatID, seatID)
+		if test.expectedAccountId != accountId {
+			t.Fatalf("want: %v, got: %v", test.expectedAccountId, accountId)
 		}
 	}
 }
diff --git a/adapters/seedingAlliance/seedingAlliancetest/exemplary/banner_with_account.json b/adapters/seedingAlliance/seedingAlliancetest/exemplary/banner_with_account.json
new file mode 100644
index 00000000..fc3678ea
--- /dev/null
+++ b/adapters/seedingAlliance/seedingAlliancetest/exemplary/banner_with_account.json
@@ -0,0 +1,142 @@
+{
+    "mockBidRequest": {
+        "id": "test-request-id",
+        "site": {
+            "publisher": {
+                "id": "foo",
+                "name": "foo"
+            }
+        },
+        "imp": [
+            {
+                "id": "test-imp-id",
+                "banner": {
+                    "format": [
+                        {
+                            "w": 300,
+                            "h": 250
+                        },
+                        {
+                            "w": 300,
+                            "h": 600
+                        }
+                    ]
+                },
+                "ext": {
+                    "bidder": {
+                        "adUnitId": "example-tag-id",
+                        "accountId": "123"
+                    }
+                }
+            }
+        ]
+    },
+    "httpCalls": [
+        {
+            "expectedRequest": {
+                "uri": "https://mockup.seeding-alliance.de/?ssp=123",
+                "body": {
+                    "cur": [
+                        "EUR"
+                    ],
+                    "id": "test-request-id",
+                    "imp": [
+                        {
+                            "id": "test-imp-id",
+                            "banner": {
+                                "format": [
+                                    {
+                                        "w": 300,
+                                        "h": 250
+                                    },
+                                    {
+                                        "w": 300,
+                                        "h": 600
+                                    }
+                                ]
+                            },
+                            "tagid": "example-tag-id",
+                            "ext": {
+                                "bidder": {
+                                    "adUnitId": "example-tag-id",
+                                    "accountId": "123"
+                                }
+                            }
+                        }
+                    ],
+                    "site": {
+                        "publisher": {
+                            "id": "foo",
+                            "name": "foo"
+                        }
+                    }
+                }
+            },
+            "mockResponse": {
+                "status": 200,
+                "body": {
+                    "id": "test-request-id",
+                    "seatbid": [
+                        {
+                            "seat": "123",
+                            "bid": [
+                                {
+                                    "id": "7706636740145184841",
+                                    "impid": "test-imp-id",
+                                    "price": 0.5,
+                                    "adid": "12341234",
+                                    "adm": "some-test-ad",
+                                    "adomain": [
+                                        "domain.com"
+                                    ],
+                                    "iurl": "http://abc.com/cr?id=12341234",
+                                    "cid": "123",
+                                    "crid": "12341234",
+                                    "h": 250,
+                                    "w": 300,
+                                    "ext": {
+                                        "prebid": {
+                                            "type": "banner"
+                                        }
+                                    }
+                                }
+                            ]
+                        }
+                    ],
+                    "bidid": "5778926625248726496",
+                    "cur": "EUR"
+                }
+            }
+        }
+    ],
+    "expectedBidResponses": [
+        {
+            "currency": "EUR",
+            "bids": [
+                {
+                    "bid": {
+                        "id": "7706636740145184841",
+                        "impid": "test-imp-id",
+                        "price": 0.5,
+                        "adm": "some-test-ad",
+                        "adid": "12341234",
+                        "adomain": [
+                            "domain.com"
+                        ],
+                        "iurl": "http://abc.com/cr?id=12341234",
+                        "cid": "123",
+                        "crid": "12341234",
+                        "w": 300,
+                        "h": 250,
+                        "ext": {
+                            "prebid": {
+                                "type": "banner"
+                            }
+                        }
+                    },
+                    "type": "banner"
+                }
+            ]
+        }
+    ]
+}
\ No newline at end of file
diff --git a/adapters/seedingAlliance/seedingAlliancetest/exemplary/banner_with_account_and_seat.json b/adapters/seedingAlliance/seedingAlliancetest/exemplary/banner_with_account_and_seat.json
new file mode 100644
index 00000000..3f010e60
--- /dev/null
+++ b/adapters/seedingAlliance/seedingAlliancetest/exemplary/banner_with_account_and_seat.json
@@ -0,0 +1,144 @@
+{
+    "mockBidRequest": {
+        "id": "test-request-id",
+        "site": {
+            "publisher": {
+                "id": "foo",
+                "name": "foo"
+            }
+        },
+        "imp": [
+            {
+                "id": "test-imp-id",
+                "banner": {
+                    "format": [
+                        {
+                            "w": 300,
+                            "h": 250
+                        },
+                        {
+                            "w": 300,
+                            "h": 600
+                        }
+                    ]
+                },
+                "ext": {
+                    "bidder": {
+                        "adUnitId": "example-tag-id",
+                        "seatId": "ignored",
+                        "accountId": "123"
+                    }
+                }
+            }
+        ]
+    },
+    "httpCalls": [
+        {
+            "expectedRequest": {
+                "uri": "https://mockup.seeding-alliance.de/?ssp=123",
+                "body": {
+                    "cur": [
+                        "EUR"
+                    ],
+                    "id": "test-request-id",
+                    "imp": [
+                        {
+                            "id": "test-imp-id",
+                            "banner": {
+                                "format": [
+                                    {
+                                        "w": 300,
+                                        "h": 250
+                                    },
+                                    {
+                                        "w": 300,
+                                        "h": 600
+                                    }
+                                ]
+                            },
+                            "tagid": "example-tag-id",
+                            "ext": {
+                                "bidder": {
+                                    "adUnitId": "example-tag-id",
+                                    "seatId": "ignored",
+                                    "accountId": "123"
+                                }
+                            }
+                        }
+                    ],
+                    "site": {
+                        "publisher": {
+                            "id": "foo",
+                            "name": "foo"
+                        }
+                    }
+                }
+            },
+            "mockResponse": {
+                "status": 200,
+                "body": {
+                    "id": "test-request-id",
+                    "seatbid": [
+                        {
+                            "seat": "123",
+                            "bid": [
+                                {
+                                    "id": "7706636740145184841",
+                                    "impid": "test-imp-id",
+                                    "price": 0.5,
+                                    "adid": "12341234",
+                                    "adm": "some-test-ad",
+                                    "adomain": [
+                                        "domain.com"
+                                    ],
+                                    "iurl": "http://abc.com/cr?id=12341234",
+                                    "cid": "123",
+                                    "crid": "12341234",
+                                    "h": 250,
+                                    "w": 300,
+                                    "ext": {
+                                        "prebid": {
+                                            "type": "banner"
+                                        }
+                                    }
+                                }
+                            ]
+                        }
+                    ],
+                    "bidid": "5778926625248726496",
+                    "cur": "EUR"
+                }
+            }
+        }
+    ],
+    "expectedBidResponses": [
+        {
+            "currency": "EUR",
+            "bids": [
+                {
+                    "bid": {
+                        "id": "7706636740145184841",
+                        "impid": "test-imp-id",
+                        "price": 0.5,
+                        "adm": "some-test-ad",
+                        "adid": "12341234",
+                        "adomain": [
+                            "domain.com"
+                        ],
+                        "iurl": "http://abc.com/cr?id=12341234",
+                        "cid": "123",
+                        "crid": "12341234",
+                        "w": 300,
+                        "h": 250,
+                        "ext": {
+                            "prebid": {
+                                "type": "banner"
+                            }
+                        }
+                    },
+                    "type": "banner"
+                }
+            ]
+        }
+    ]
+}
\ No newline at end of file
diff --git a/adapters/stroeerCore/stroeercoretest/exemplary/dsa.json b/adapters/stroeerCore/stroeercoretest/exemplary/dsa.json
new file mode 100644
index 00000000..70baef5f
--- /dev/null
+++ b/adapters/stroeerCore/stroeercoretest/exemplary/dsa.json
@@ -0,0 +1,202 @@
+{
+  "mockBidRequest": {
+    "id": "auction-id",
+    "cur": [
+      "EUR"
+    ],
+    "imp": [
+      {
+        "id": "1",
+        "banner": {
+          "format": [
+            {
+              "w": 300,
+              "h": 600
+            }
+          ]
+        },
+        "ext": {
+          "bidder": {
+            "sid": "123456"
+          }
+        }
+      }
+    ],
+    "device": {
+      "ip": "123.123.123.123"
+    },
+    "site": {
+      "domain": "www.publisher.com",
+      "page": "http://www.publisher.com/some/path"
+    },
+    "user": {
+      "buyeruid": "test-buyer-user-id"
+    },
+    "regs": {
+      "ext": {
+        "dsa": {
+          "dsarequired": 3,
+          "pubrender": 0,
+          "datatopub": 2,
+          "transparency": [
+            {
+              "domain": "example-platform.com",
+              "dsaparams": [
+                1
+              ]
+            },
+            {
+              "domain": "example-ssp.com",
+              "dsaparams": [
+                1,
+                2
+              ]
+            }
+          ]
+        }
+      }
+    }
+  },
+  "httpCalls": [
+    {
+      "expectedRequest": {
+        "headers": {
+          "Accept": [
+            "application/json"
+          ],
+          "Content-Type": [
+            "application/json;charset=utf-8"
+          ]
+        },
+        "uri": "http://localhost/s2sdsh",
+        "body": {
+          "id": "auction-id",
+          "cur": [
+            "EUR"
+          ],
+          "imp": [
+            {
+              "id": "1",
+              "tagid": "123456",
+              "banner": {
+                "format": [
+                  {
+                    "w": 300,
+                    "h": 600
+                  }
+                ]
+              },
+              "ext": {
+                "bidder": {
+                  "sid": "123456"
+                }
+              }
+            }
+          ],
+          "device": {
+            "ip": "123.123.123.123"
+          },
+          "site": {
+            "domain": "www.publisher.com",
+            "page": "http://www.publisher.com/some/path"
+          },
+          "user": {
+            "buyeruid": "test-buyer-user-id"
+          },
+          "regs": {
+            "ext": {
+              "dsa": {
+                "dsarequired": 3,
+                "pubrender": 0,
+                "datatopub": 2,
+                "transparency": [
+                  {
+                    "domain": "example-platform.com",
+                    "dsaparams": [
+                      1
+                    ]
+                  },
+                  {
+                    "domain": "example-ssp.com",
+                    "dsaparams": [
+                      1,
+                      2
+                    ]
+                  }
+                ]
+              }
+            }
+          }
+        }
+      },
+      "mockResponse": {
+        "status": 200,
+        "body": {
+          "id": "test-request-id",
+          "bids": [
+            {
+              "id": "1829273982920-01",
+              "bidId": "1",
+              "cpm": 2,
+              "width": 300,
+              "height": 600,
+              "ad": "advert",
+              "crid": "wasd",
+              "mtype": "banner",
+              "dsa": {
+                "behalf": "Advertiser A",
+                "paid": "Advertiser B",
+                "transparency": [
+                  {
+                    "domain": "example-domain.com",
+                    "dsaparams": [
+                      1,
+                      2
+                    ]
+                  }
+                ],
+                "adrender": 1
+              }
+            }
+          ]
+        }
+      }
+    }
+  ],
+  "expectedBidResponses": [
+    {
+      "currency": "EUR",
+      "bids": [
+        {
+          "bid": {
+            "id": "1829273982920-01",
+            "impid": "1",
+            "price": 2,
+            "adm": "advert",
+            "w": 300,
+            "h": 600,
+            "crid": "wasd",
+            "mtype": 1,
+            "ext": {
+              "dsa": {
+                "behalf": "Advertiser A",
+                "paid": "Advertiser B",
+                "transparency": [
+                  {
+                    "domain": "example-domain.com",
+                    "dsaparams": [
+                      1,
+                      2
+                    ]
+                  }
+                ],
+                "adrender": 1
+              }
+            }
+          },
+          "type": "banner"
+        }
+      ]
+    }
+  ]
+}
diff --git a/config/account_test.go b/config/account_test.go
index 65e7f3e8..c529af09 100644
--- a/config/account_test.go
+++ b/config/account_test.go
@@ -476,48 +476,40 @@ func TestPurposeVendorExceptions(t *testing.T) {
 	tests := []struct {
 		description              string
 		givePurposeConfigNil     bool
-		givePurpose1ExceptionMap map[openrtb_ext.BidderName]struct{}
-		givePurpose2ExceptionMap map[openrtb_ext.BidderName]struct{}
+		givePurpose1ExceptionMap map[string]struct{}
+		givePurpose2ExceptionMap map[string]struct{}
 		givePurpose              consentconstants.Purpose
-		wantExceptionMap         map[openrtb_ext.BidderName]struct{}
-		wantExceptionMapSet      bool
+		wantExceptionMap         map[string]struct{}
 	}{
 		{
 			description:          "Purpose config is nil",
 			givePurposeConfigNil: true,
 			givePurpose:          1,
-			// wantExceptionMap:     map[openrtb_ext.BidderName]struct{}{},
-			wantExceptionMap:    nil,
-			wantExceptionMapSet: false,
+			wantExceptionMap:     nil,
 		},
 		{
-			description: "Nil - exception map not defined for purpose",
-			givePurpose: 1,
-			// wantExceptionMap:    map[openrtb_ext.BidderName]struct{}{},
-			wantExceptionMap:    nil,
-			wantExceptionMapSet: false,
+			description:      "Nil - exception map not defined for purpose",
+			givePurpose:      1,
+			wantExceptionMap: nil,
 		},
 		{
 			description:              "Empty - exception map empty for purpose",
 			givePurpose:              1,
-			givePurpose1ExceptionMap: map[openrtb_ext.BidderName]struct{}{},
-			wantExceptionMap:         map[openrtb_ext.BidderName]struct{}{},
-			wantExceptionMapSet:      true,
+			givePurpose1ExceptionMap: map[string]struct{}{},
+			wantExceptionMap:         map[string]struct{}{},
 		},
 		{
 			description:              "Nonempty - exception map with multiple entries for purpose",
 			givePurpose:              1,
-			givePurpose1ExceptionMap: map[openrtb_ext.BidderName]struct{}{"rubicon": {}, "appnexus": {}, "index": {}},
-			wantExceptionMap:         map[openrtb_ext.BidderName]struct{}{"rubicon": {}, "appnexus": {}, "index": {}},
-			wantExceptionMapSet:      true,
+			givePurpose1ExceptionMap: map[string]struct{}{"rubicon": {}, "appnexus": {}, "index": {}},
+			wantExceptionMap:         map[string]struct{}{"rubicon": {}, "appnexus": {}, "index": {}},
 		},
 		{
 			description:              "Nonempty - exception map with multiple entries for different purpose",
 			givePurpose:              2,
-			givePurpose1ExceptionMap: map[openrtb_ext.BidderName]struct{}{"rubicon": {}, "appnexus": {}, "index": {}},
-			givePurpose2ExceptionMap: map[openrtb_ext.BidderName]struct{}{"rubicon": {}, "appnexus": {}, "openx": {}},
-			wantExceptionMap:         map[openrtb_ext.BidderName]struct{}{"rubicon": {}, "appnexus": {}, "openx": {}},
-			wantExceptionMapSet:      true,
+			givePurpose1ExceptionMap: map[string]struct{}{"rubicon": {}, "appnexus": {}, "index": {}},
+			givePurpose2ExceptionMap: map[string]struct{}{"rubicon": {}, "appnexus": {}, "openx": {}},
+			wantExceptionMap:         map[string]struct{}{"rubicon": {}, "appnexus": {}, "openx": {}},
 		},
 	}
 
@@ -538,7 +530,11 @@ func TestPurposeVendorExceptions(t *testing.T) {
 		value, present := accountGDPR.PurposeVendorExceptions(tt.givePurpose)
 
 		assert.Equal(t, tt.wantExceptionMap, value, tt.description)
-		assert.Equal(t, tt.wantExceptionMapSet, present, tt.description)
+		if tt.wantExceptionMap == nil {
+			assert.Equal(t, false, present)
+		} else {
+			assert.Equal(t, true, present)
+		}
 	}
 }
 
diff --git a/config/config_test.go b/config/config_test.go
index a551c1be..c58e93de 100644
--- a/config/config_test.go
+++ b/config/config_test.go
@@ -203,6 +203,8 @@ func TestDefaults(t *testing.T) {
 	cmpInts(t, "account_defaults.price_floors.fetch.period_sec", 3600, cfg.AccountDefaults.PriceFloors.Fetcher.Period)
 	cmpInts(t, "account_defaults.price_floors.fetch.max_age_sec", 86400, cfg.AccountDefaults.PriceFloors.Fetcher.MaxAge)
 	cmpInts(t, "account_defaults.price_floors.fetch.max_schema_dims", 0, cfg.AccountDefaults.PriceFloors.Fetcher.MaxSchemaDims)
+	cmpBools(t, "account_defaults.privacy.privacysandbox.cookiedeprecation.enabled", false, cfg.AccountDefaults.Privacy.PrivacySandbox.CookieDeprecation.Enabled)
+	cmpInts(t, "account_defaults.privacy.privacysandbox.cookiedeprecation.ttl_sec", 604800, cfg.AccountDefaults.Privacy.PrivacySandbox.CookieDeprecation.TTLSec)
 
 	cmpBools(t, "account_defaults.events.enabled", false, cfg.AccountDefaults.Events.Enabled)
 
@@ -229,80 +231,80 @@ func TestDefaults(t *testing.T) {
 			EnforceAlgoID:      TCF2FullEnforcement,
 			EnforcePurpose:     true,
 			EnforceVendors:     true,
-			VendorExceptions:   []openrtb_ext.BidderName{},
-			VendorExceptionMap: map[openrtb_ext.BidderName]struct{}{},
+			VendorExceptions:   []string{},
+			VendorExceptionMap: map[string]struct{}{},
 		},
 		Purpose2: TCF2Purpose{
 			EnforceAlgo:        TCF2EnforceAlgoFull,
 			EnforceAlgoID:      TCF2FullEnforcement,
 			EnforcePurpose:     true,
 			EnforceVendors:     true,
-			VendorExceptions:   []openrtb_ext.BidderName{},
-			VendorExceptionMap: map[openrtb_ext.BidderName]struct{}{},
+			VendorExceptions:   []string{},
+			VendorExceptionMap: map[string]struct{}{},
 		},
 		Purpose3: TCF2Purpose{
 			EnforceAlgo:        TCF2EnforceAlgoFull,
 			EnforceAlgoID:      TCF2FullEnforcement,
 			EnforcePurpose:     true,
 			EnforceVendors:     true,
-			VendorExceptions:   []openrtb_ext.BidderName{},
-			VendorExceptionMap: map[openrtb_ext.BidderName]struct{}{},
+			VendorExceptions:   []string{},
+			VendorExceptionMap: map[string]struct{}{},
 		},
 		Purpose4: TCF2Purpose{
 			EnforceAlgo:        TCF2EnforceAlgoFull,
 			EnforceAlgoID:      TCF2FullEnforcement,
 			EnforcePurpose:     true,
 			EnforceVendors:     true,
-			VendorExceptions:   []openrtb_ext.BidderName{},
-			VendorExceptionMap: map[openrtb_ext.BidderName]struct{}{},
+			VendorExceptions:   []string{},
+			VendorExceptionMap: map[string]struct{}{},
 		},
 		Purpose5: TCF2Purpose{
 			EnforceAlgo:        TCF2EnforceAlgoFull,
 			EnforceAlgoID:      TCF2FullEnforcement,
 			EnforcePurpose:     true,
 			EnforceVendors:     true,
-			VendorExceptions:   []openrtb_ext.BidderName{},
-			VendorExceptionMap: map[openrtb_ext.BidderName]struct{}{},
+			VendorExceptions:   []string{},
+			VendorExceptionMap: map[string]struct{}{},
 		},
 		Purpose6: TCF2Purpose{
 			EnforceAlgo:        TCF2EnforceAlgoFull,
 			EnforceAlgoID:      TCF2FullEnforcement,
 			EnforcePurpose:     true,
 			EnforceVendors:     true,
-			VendorExceptions:   []openrtb_ext.BidderName{},
-			VendorExceptionMap: map[openrtb_ext.BidderName]struct{}{},
+			VendorExceptions:   []string{},
+			VendorExceptionMap: map[string]struct{}{},
 		},
 		Purpose7: TCF2Purpose{
 			EnforceAlgo:        TCF2EnforceAlgoFull,
 			EnforceAlgoID:      TCF2FullEnforcement,
 			EnforcePurpose:     true,
 			EnforceVendors:     true,
-			VendorExceptions:   []openrtb_ext.BidderName{},
-			VendorExceptionMap: map[openrtb_ext.BidderName]struct{}{},
+			VendorExceptions:   []string{},
+			VendorExceptionMap: map[string]struct{}{},
 		},
 		Purpose8: TCF2Purpose{
 			EnforceAlgo:        TCF2EnforceAlgoFull,
 			EnforceAlgoID:      TCF2FullEnforcement,
 			EnforcePurpose:     true,
 			EnforceVendors:     true,
-			VendorExceptions:   []openrtb_ext.BidderName{},
-			VendorExceptionMap: map[openrtb_ext.BidderName]struct{}{},
+			VendorExceptions:   []string{},
+			VendorExceptionMap: map[string]struct{}{},
 		},
 		Purpose9: TCF2Purpose{
 			EnforceAlgo:        TCF2EnforceAlgoFull,
 			EnforceAlgoID:      TCF2FullEnforcement,
 			EnforcePurpose:     true,
 			EnforceVendors:     true,
-			VendorExceptions:   []openrtb_ext.BidderName{},
-			VendorExceptionMap: map[openrtb_ext.BidderName]struct{}{},
+			VendorExceptions:   []string{},
+			VendorExceptionMap: map[string]struct{}{},
 		},
 		Purpose10: TCF2Purpose{
 			EnforceAlgo:        TCF2EnforceAlgoFull,
 			EnforceAlgoID:      TCF2FullEnforcement,
 			EnforcePurpose:     true,
 			EnforceVendors:     true,
-			VendorExceptions:   []openrtb_ext.BidderName{},
-			VendorExceptionMap: map[openrtb_ext.BidderName]struct{}{},
+			VendorExceptions:   []string{},
+			VendorExceptionMap: map[string]struct{}{},
 		},
 		SpecialFeature1: TCF2SpecialFeature{
 			Enforce:            true,
@@ -503,6 +505,10 @@ account_defaults:
             anon_keep_bits: 50
         ipv4:
             anon_keep_bits: 20
+        privacysandbox:
+            cookiedeprecation:
+                enabled: true
+                ttl_sec: 86400
 tmax_adjustments:
   enabled: true
   bidder_response_duration_min_ms: 700
@@ -622,6 +628,9 @@ func TestFullConfig(t *testing.T) {
 	cmpInts(t, "account_defaults.privacy.ipv6.anon_keep_bits", 50, cfg.AccountDefaults.Privacy.IPv6Config.AnonKeepBits)
 	cmpInts(t, "account_defaults.privacy.ipv4.anon_keep_bits", 20, cfg.AccountDefaults.Privacy.IPv4Config.AnonKeepBits)
 
+	cmpBools(t, "account_defaults.privacy.cookiedeprecation.enabled", true, cfg.AccountDefaults.Privacy.PrivacySandbox.CookieDeprecation.Enabled)
+	cmpInts(t, "account_defaults.privacy.cookiedeprecation.ttl_sec", 86400, cfg.AccountDefaults.Privacy.PrivacySandbox.CookieDeprecation.TTLSec)
+
 	// Assert compression related defaults
 	cmpBools(t, "compression.request.enable_gzip", true, cfg.Compression.Request.GZIP)
 	cmpBools(t, "compression.response.enable_gzip", false, cfg.Compression.Response.GZIP)
@@ -654,80 +663,80 @@ func TestFullConfig(t *testing.T) {
 			EnforceAlgoID:      TCF2FullEnforcement,
 			EnforcePurpose:     true,
 			EnforceVendors:     false,
-			VendorExceptions:   []openrtb_ext.BidderName{openrtb_ext.BidderName("foo1a"), openrtb_ext.BidderName("foo1b")},
-			VendorExceptionMap: map[openrtb_ext.BidderName]struct{}{openrtb_ext.BidderName("foo1a"): {}, openrtb_ext.BidderName("foo1b"): {}},
+			VendorExceptions:   []string{"foo1a", "foo1b"},
+			VendorExceptionMap: map[string]struct{}{"foo1a": {}, "foo1b": {}},
 		},
 		Purpose2: TCF2Purpose{
 			EnforceAlgo:        TCF2EnforceAlgoFull,
 			EnforceAlgoID:      TCF2FullEnforcement,
 			EnforcePurpose:     false,
 			EnforceVendors:     false,
-			VendorExceptions:   []openrtb_ext.BidderName{openrtb_ext.BidderName("foo2")},
-			VendorExceptionMap: map[openrtb_ext.BidderName]struct{}{openrtb_ext.BidderName("foo2"): {}},
+			VendorExceptions:   []string{"foo2"},
+			VendorExceptionMap: map[string]struct{}{"foo2": {}},
 		},
 		Purpose3: TCF2Purpose{
 			EnforceAlgo:        TCF2EnforceAlgoBasic,
 			EnforceAlgoID:      TCF2BasicEnforcement,
 			EnforcePurpose:     true,
 			EnforceVendors:     false,
-			VendorExceptions:   []openrtb_ext.BidderName{openrtb_ext.BidderName("foo3")},
-			VendorExceptionMap: map[openrtb_ext.BidderName]struct{}{openrtb_ext.BidderName("foo3"): {}},
+			VendorExceptions:   []string{"foo3"},
+			VendorExceptionMap: map[string]struct{}{"foo3": {}},
 		},
 		Purpose4: TCF2Purpose{
 			EnforceAlgo:        TCF2EnforceAlgoFull,
 			EnforceAlgoID:      TCF2FullEnforcement,
 			EnforcePurpose:     true,
 			EnforceVendors:     false,
-			VendorExceptions:   []openrtb_ext.BidderName{openrtb_ext.BidderName("foo4")},
-			VendorExceptionMap: map[openrtb_ext.BidderName]struct{}{openrtb_ext.BidderName("foo4"): {}},
+			VendorExceptions:   []string{"foo4"},
+			VendorExceptionMap: map[string]struct{}{"foo4": {}},
 		},
 		Purpose5: TCF2Purpose{
 			EnforceAlgo:        TCF2EnforceAlgoFull,
 			EnforceAlgoID:      TCF2FullEnforcement,
 			EnforcePurpose:     true,
 			EnforceVendors:     false,
-			VendorExceptions:   []openrtb_ext.BidderName{openrtb_ext.BidderName("foo5")},
-			VendorExceptionMap: map[openrtb_ext.BidderName]struct{}{openrtb_ext.BidderName("foo5"): {}},
+			VendorExceptions:   []string{"foo5"},
+			VendorExceptionMap: map[string]struct{}{"foo5": {}},
 		},
 		Purpose6: TCF2Purpose{
 			EnforceAlgo:        TCF2EnforceAlgoFull,
 			EnforceAlgoID:      TCF2FullEnforcement,
 			EnforcePurpose:     true,
 			EnforceVendors:     false,
-			VendorExceptions:   []openrtb_ext.BidderName{openrtb_ext.BidderName("foo6")},
-			VendorExceptionMap: map[openrtb_ext.BidderName]struct{}{openrtb_ext.BidderName("foo6"): {}},
+			VendorExceptions:   []string{"foo6"},
+			VendorExceptionMap: map[string]struct{}{"foo6": {}},
 		},
 		Purpose7: TCF2Purpose{
 			EnforceAlgo:        TCF2EnforceAlgoFull,
 			EnforceAlgoID:      TCF2FullEnforcement,
 			EnforcePurpose:     true,
 			EnforceVendors:     false,
-			VendorExceptions:   []openrtb_ext.BidderName{openrtb_ext.BidderName("foo7")},
-			VendorExceptionMap: map[openrtb_ext.BidderName]struct{}{openrtb_ext.BidderName("foo7"): {}},
+			VendorExceptions:   []string{"foo7"},
+			VendorExceptionMap: map[string]struct{}{"foo7": {}},
 		},
 		Purpose8: TCF2Purpose{
 			EnforceAlgo:        TCF2EnforceAlgoFull,
 			EnforceAlgoID:      TCF2FullEnforcement,
 			EnforcePurpose:     true,
 			EnforceVendors:     false,
-			VendorExceptions:   []openrtb_ext.BidderName{openrtb_ext.BidderName("foo8")},
-			VendorExceptionMap: map[openrtb_ext.BidderName]struct{}{openrtb_ext.BidderName("foo8"): {}},
+			VendorExceptions:   []string{"foo8"},
+			VendorExceptionMap: map[string]struct{}{"foo8": {}},
 		},
 		Purpose9: TCF2Purpose{
 			EnforceAlgo:        TCF2EnforceAlgoFull,
 			EnforceAlgoID:      TCF2FullEnforcement,
 			EnforcePurpose:     true,
 			EnforceVendors:     false,
-			VendorExceptions:   []openrtb_ext.BidderName{openrtb_ext.BidderName("foo9")},
-			VendorExceptionMap: map[openrtb_ext.BidderName]struct{}{openrtb_ext.BidderName("foo9"): {}},
+			VendorExceptions:   []string{"foo9"},
+			VendorExceptionMap: map[string]struct{}{"foo9": {}},
 		},
 		Purpose10: TCF2Purpose{
 			EnforceAlgo:        TCF2EnforceAlgoFull,
 			EnforceAlgoID:      TCF2FullEnforcement,
 			EnforcePurpose:     true,
 			EnforceVendors:     false,
-			VendorExceptions:   []openrtb_ext.BidderName{openrtb_ext.BidderName("foo10")},
-			VendorExceptionMap: map[openrtb_ext.BidderName]struct{}{openrtb_ext.BidderName("foo10"): {}},
+			VendorExceptions:   []string{"foo10"},
+			VendorExceptionMap: map[string]struct{}{"foo10": {}},
 		},
 		SpecialFeature1: TCF2SpecialFeature{
 			Enforce:            true, // true by default
@@ -1675,40 +1684,40 @@ func TestTCF2PurposeVendorExceptions(t *testing.T) {
 	tests := []struct {
 		description              string
 		givePurposeConfigNil     bool
-		givePurpose1ExceptionMap map[openrtb_ext.BidderName]struct{}
-		givePurpose2ExceptionMap map[openrtb_ext.BidderName]struct{}
+		givePurpose1ExceptionMap map[string]struct{}
+		givePurpose2ExceptionMap map[string]struct{}
 		givePurpose              consentconstants.Purpose
-		wantExceptionMap         map[openrtb_ext.BidderName]struct{}
+		wantExceptionMap         map[string]struct{}
 	}{
 		{
 			description:          "Purpose config is nil",
 			givePurposeConfigNil: true,
 			givePurpose:          1,
-			wantExceptionMap:     map[openrtb_ext.BidderName]struct{}{},
+			wantExceptionMap:     map[string]struct{}{},
 		},
 		{
 			description:      "Nil - exception map not defined for purpose",
 			givePurpose:      1,
-			wantExceptionMap: map[openrtb_ext.BidderName]struct{}{},
+			wantExceptionMap: map[string]struct{}{},
 		},
 		{
 			description:              "Empty - exception map empty for purpose",
 			givePurpose:              1,
-			givePurpose1ExceptionMap: map[openrtb_ext.BidderName]struct{}{},
-			wantExceptionMap:         map[openrtb_ext.BidderName]struct{}{},
+			givePurpose1ExceptionMap: map[string]struct{}{},
+			wantExceptionMap:         map[string]struct{}{},
 		},
 		{
 			description:              "Nonempty - exception map with multiple entries for purpose",
 			givePurpose:              1,
-			givePurpose1ExceptionMap: map[openrtb_ext.BidderName]struct{}{"rubicon": {}, "appnexus": {}, "index": {}},
-			wantExceptionMap:         map[openrtb_ext.BidderName]struct{}{"rubicon": {}, "appnexus": {}, "index": {}},
+			givePurpose1ExceptionMap: map[string]struct{}{"rubicon": {}, "appnexus": {}, "index": {}},
+			wantExceptionMap:         map[string]struct{}{"rubicon": {}, "appnexus": {}, "index": {}},
 		},
 		{
 			description:              "Nonempty - exception map with multiple entries for different purpose",
 			givePurpose:              2,
-			givePurpose1ExceptionMap: map[openrtb_ext.BidderName]struct{}{"rubicon": {}, "appnexus": {}, "index": {}},
-			givePurpose2ExceptionMap: map[openrtb_ext.BidderName]struct{}{"rubicon": {}, "appnexus": {}, "openx": {}},
-			wantExceptionMap:         map[openrtb_ext.BidderName]struct{}{"rubicon": {}, "appnexus": {}, "openx": {}},
+			givePurpose1ExceptionMap: map[string]struct{}{"rubicon": {}, "appnexus": {}, "index": {}},
+			givePurpose2ExceptionMap: map[string]struct{}{"rubicon": {}, "appnexus": {}, "openx": {}},
+			wantExceptionMap:         map[string]struct{}{"rubicon": {}, "appnexus": {}, "openx": {}},
 		},
 	}
 
diff --git a/dsa/validate_test.go b/dsa/validate_test.go
index 74d428c3..9dfa32c6 100644
--- a/dsa/validate_test.go
+++ b/dsa/validate_test.go
@@ -2,23 +2,48 @@ package dsa
 
 import (
 	"encoding/json"
+	"strings"
 	"testing"
 
 	"github.com/prebid/openrtb/v20/openrtb2"
 	"github.com/prebid/prebid-server/v2/exchange/entities"
 	"github.com/prebid/prebid-server/v2/openrtb_ext"
+	"github.com/prebid/prebid-server/v2/util/ptrutil"
 	"github.com/stretchr/testify/assert"
 )
 
 func TestValidate(t *testing.T) {
+	var (
+		validBehalf   = strings.Repeat("a", 100)
+		invalidBehalf = strings.Repeat("a", 101)
+		validPaid     = strings.Repeat("a", 100)
+		invalidPaid   = strings.Repeat("a", 101)
+	)
+
 	tests := []struct {
 		name        string
 		giveRequest *openrtb_ext.RequestWrapper
 		giveBid     *entities.PbsOrtbBid
-		wantValid   bool
+		wantError   error
 	}{
 		{
-			name: "not_required",
+			name:        "nil",
+			giveRequest: nil,
+			giveBid:     nil,
+			wantError:   nil,
+		},
+		{
+			name:        "request_nil",
+			giveRequest: nil,
+			giveBid: &entities.PbsOrtbBid{
+				Bid: &openrtb2.Bid{
+					Ext: json.RawMessage(`{"dsa":{"behalf":"` + validBehalf + `","paid":"` + validPaid + `","adrender":1}}`),
+				},
+			},
+			wantError: nil,
+		},
+		{
+			name: "not_required_and_bid_is_nil",
 			giveRequest: &openrtb_ext.RequestWrapper{
 				BidRequest: &openrtb2.BidRequest{
 					Regs: &openrtb2.Regs{
@@ -27,7 +52,23 @@ func TestValidate(t *testing.T) {
 				},
 			},
 			giveBid:   nil,
-			wantValid: true,
+			wantError: nil,
+		},
+		{
+			name: "not_required_and_bid_dsa_is_valid",
+			giveRequest: &openrtb_ext.RequestWrapper{
+				BidRequest: &openrtb2.BidRequest{
+					Regs: &openrtb2.Regs{
+						Ext: json.RawMessage(`{"dsa": {"dsarequired": 0,"pubrender":0}}`),
+					},
+				},
+			},
+			giveBid: &entities.PbsOrtbBid{
+				Bid: &openrtb2.Bid{
+					Ext: json.RawMessage(`{"dsa":{"behalf":"` + validBehalf + `","paid":"` + validPaid + `","adrender":1}}`),
+				},
+			},
+			wantError: nil,
 		},
 		{
 			name: "required_and_bid_is_nil",
@@ -39,10 +80,10 @@ func TestValidate(t *testing.T) {
 				},
 			},
 			giveBid:   nil,
-			wantValid: false,
+			wantError: ErrDsaMissing,
 		},
 		{
-			name: "required_and_bid.bid_is_nil",
+			name: "required_and_bid_dsa_has_invalid_behalf",
 			giveRequest: &openrtb_ext.RequestWrapper{
 				BidRequest: &openrtb2.BidRequest{
 					Regs: &openrtb2.Regs{
@@ -50,11 +91,15 @@ func TestValidate(t *testing.T) {
 					},
 				},
 			},
-			giveBid:   &entities.PbsOrtbBid{},
-			wantValid: false,
+			giveBid: &entities.PbsOrtbBid{
+				Bid: &openrtb2.Bid{
+					Ext: json.RawMessage(`{"dsa":{"behalf":"` + invalidBehalf + `"}}`),
+				},
+			},
+			wantError: ErrBehalfTooLong,
 		},
 		{
-			name: "required_and_bid.ext.dsa_not_present",
+			name: "required_and_bid_dsa_has_invalid_paid",
 			giveRequest: &openrtb_ext.RequestWrapper{
 				BidRequest: &openrtb2.BidRequest{
 					Regs: &openrtb2.Regs{
@@ -64,13 +109,61 @@ func TestValidate(t *testing.T) {
 			},
 			giveBid: &entities.PbsOrtbBid{
 				Bid: &openrtb2.Bid{
-					Ext: json.RawMessage(`{}`),
+					Ext: json.RawMessage(`{"dsa":{"paid":"` + invalidPaid + `"}}`),
+				},
+			},
+			wantError: ErrPaidTooLong,
+		},
+		{
+			name: "required_and_neither_will_render",
+			giveRequest: &openrtb_ext.RequestWrapper{
+				BidRequest: &openrtb2.BidRequest{
+					Regs: &openrtb2.Regs{
+						Ext: json.RawMessage(`{"dsa": {"dsarequired": 2,"pubrender": 0}}`),
+					},
+				},
+			},
+			giveBid: &entities.PbsOrtbBid{
+				Bid: &openrtb2.Bid{
+					Ext: json.RawMessage(`{"dsa":{"adrender": 0}}`),
+				},
+			},
+			wantError: ErrNeitherWillRender,
+		},
+		{
+			name: "required_and_both_will_render",
+			giveRequest: &openrtb_ext.RequestWrapper{
+				BidRequest: &openrtb2.BidRequest{
+					Regs: &openrtb2.Regs{
+						Ext: json.RawMessage(`{"dsa": {"dsarequired": 2,"pubrender": 2}}`),
+					},
 				},
 			},
-			wantValid: false,
+			giveBid: &entities.PbsOrtbBid{
+				Bid: &openrtb2.Bid{
+					Ext: json.RawMessage(`{"dsa":{"adrender": 1}}`),
+				},
+			},
+			wantError: ErrBothWillRender,
 		},
 		{
-			name: "required_and_bid.ext.dsa_present",
+			name: "required_and_bid_dsa_is_valid",
+			giveRequest: &openrtb_ext.RequestWrapper{
+				BidRequest: &openrtb2.BidRequest{
+					Regs: &openrtb2.Regs{
+						Ext: json.RawMessage(`{"dsa": {"dsarequired": 2,"pubrender": 0}}`),
+					},
+				},
+			},
+			giveBid: &entities.PbsOrtbBid{
+				Bid: &openrtb2.Bid{
+					Ext: json.RawMessage(`{"dsa":{"behalf":"` + validBehalf + `","paid":"` + validPaid + `","adrender":1}}`),
+				},
+			},
+			wantError: nil,
+		},
+		{
+			name: "required_and_bid_dsa_is_valid_no_pubrender",
 			giveRequest: &openrtb_ext.RequestWrapper{
 				BidRequest: &openrtb2.BidRequest{
 					Regs: &openrtb2.Regs{
@@ -80,17 +173,37 @@ func TestValidate(t *testing.T) {
 			},
 			giveBid: &entities.PbsOrtbBid{
 				Bid: &openrtb2.Bid{
-					Ext: json.RawMessage(`{"dsa": {}}`),
+					Ext: json.RawMessage(`{"dsa":{"behalf":"` + validBehalf + `","paid":"` + validPaid + `","adrender":2}}`),
+				},
+			},
+			wantError: nil,
+		},
+		{
+			name: "required_and_bid_dsa_is_valid_no_adrender",
+			giveRequest: &openrtb_ext.RequestWrapper{
+				BidRequest: &openrtb2.BidRequest{
+					Regs: &openrtb2.Regs{
+						Ext: json.RawMessage(`{"dsa": {"dsarequired": 2, "pubrender": 0}}`),
+					},
+				},
+			},
+			giveBid: &entities.PbsOrtbBid{
+				Bid: &openrtb2.Bid{
+					Ext: json.RawMessage(`{"dsa":{"behalf":"` + validBehalf + `","paid":"` + validPaid + `"}}`),
 				},
 			},
-			wantValid: true,
+			wantError: nil,
 		},
 	}
 
 	for _, tt := range tests {
 		t.Run(tt.name, func(t *testing.T) {
-			valid := Validate(tt.giveRequest, tt.giveBid)
-			assert.Equal(t, tt.wantValid, valid)
+			err := Validate(tt.giveRequest, tt.giveBid)
+			if tt.wantError != nil {
+				assert.Equal(t, err, tt.wantError)
+			} else {
+				assert.NoError(t, err)
+			}
 		})
 	}
 }
@@ -98,55 +211,110 @@ func TestValidate(t *testing.T) {
 func TestDSARequired(t *testing.T) {
 	tests := []struct {
 		name         string
-		giveRequest  *openrtb_ext.RequestWrapper
+		giveReqDSA   *openrtb_ext.ExtRegsDSA
 		wantRequired bool
 	}{
 		{
-			name: "not_required_and_reg.ext.dsa_is_nil",
-			giveRequest: &openrtb_ext.RequestWrapper{
-				BidRequest: &openrtb2.BidRequest{
-					Regs: &openrtb2.Regs{
-						Ext: json.RawMessage(`{}`),
-					},
-				},
+			name:         "nil",
+			giveReqDSA:   nil,
+			wantRequired: false,
+		},
+		{
+			name: "nil_required",
+			giveReqDSA: &openrtb_ext.ExtRegsDSA{
+				Required: nil,
+			},
+			wantRequired: false,
+		},
+		{
+			name: "not_required",
+			giveReqDSA: &openrtb_ext.ExtRegsDSA{
+				Required: ptrutil.ToPtr[int8](0),
+			},
+			wantRequired: false,
+		},
+		{
+			name: "not_required_supported",
+			giveReqDSA: &openrtb_ext.ExtRegsDSA{
+				Required: ptrutil.ToPtr[int8](1),
 			},
 			wantRequired: false,
 		},
 		{
-			name: "not_required_and_reg.ext.dsa_is_empty",
+			name: "required",
+			giveReqDSA: &openrtb_ext.ExtRegsDSA{
+				Required: ptrutil.ToPtr[int8](2),
+			},
+			wantRequired: true,
+		},
+		{
+			name: "required_online_platform",
+			giveReqDSA: &openrtb_ext.ExtRegsDSA{
+				Required: ptrutil.ToPtr[int8](3),
+			},
+			wantRequired: true,
+		},
+	}
+
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			required := dsaRequired(tt.giveReqDSA)
+			assert.Equal(t, tt.wantRequired, required)
+		})
+	}
+}
+
+func TestGetReqDSA(t *testing.T) {
+	tests := []struct {
+		name        string
+		giveRequest *openrtb_ext.RequestWrapper
+		expectedDSA *openrtb_ext.ExtRegsDSA
+	}{
+		{
+			name:        "req_is_nil",
+			giveRequest: nil,
+			expectedDSA: nil,
+		},
+		{
+			name: "bidrequest_is_nil",
+			giveRequest: &openrtb_ext.RequestWrapper{
+				BidRequest: nil,
+			},
+			expectedDSA: nil,
+		},
+		{
+			name: "req.regs_is_nil",
 			giveRequest: &openrtb_ext.RequestWrapper{
 				BidRequest: &openrtb2.BidRequest{
-					Regs: &openrtb2.Regs{
-						Ext: json.RawMessage(`{"dsa": {}}`),
-					},
+					Regs: nil,
 				},
 			},
-			wantRequired: false,
+			expectedDSA: nil,
 		},
 		{
-			name: "required_and_reg.ext.dsa_is_0",
+			name: "req.regs.ext_is_nil",
 			giveRequest: &openrtb_ext.RequestWrapper{
 				BidRequest: &openrtb2.BidRequest{
 					Regs: &openrtb2.Regs{
-						Ext: json.RawMessage(`{"dsa": {"dsarequired": 0}}`),
+						Ext: nil,
 					},
 				},
 			},
-			wantRequired: false,
+			expectedDSA: nil,
 		},
 		{
-			name: "required_and_reg.ext.dsa_is_1",
+			name: "req.regs.ext_is_empty",
 			giveRequest: &openrtb_ext.RequestWrapper{
 				BidRequest: &openrtb2.BidRequest{
 					Regs: &openrtb2.Regs{
-						Ext: json.RawMessage(`{"dsa": {"dsarequired": 1}}`),
+						Ext: json.RawMessage(`{}`),
 					},
 				},
 			},
-			wantRequired: false,
+			expectedDSA: nil,
 		},
 		{
-			name: "required_and_reg.ext.dsa_is_2",
+			name: "req.regs.ext_dsa_is_populated",
 			giveRequest: &openrtb_ext.RequestWrapper{
 				BidRequest: &openrtb2.BidRequest{
 					Regs: &openrtb2.Regs{
@@ -154,25 +322,75 @@ func TestDSARequired(t *testing.T) {
 					},
 				},
 			},
-			wantRequired: true,
+			expectedDSA: &openrtb_ext.ExtRegsDSA{
+				Required: ptrutil.ToPtr[int8](2),
+			},
 		},
+	}
+
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			dsa := getReqDSA(tt.giveRequest)
+			assert.Equal(t, tt.expectedDSA, dsa)
+		})
+	}
+}
+
+func TestGetBidDSA(t *testing.T) {
+	tests := []struct {
+		name        string
+		bid         *entities.PbsOrtbBid
+		expectedDSA *openrtb_ext.ExtBidDSA
+	}{
 		{
-			name: "required_and_reg.ext.dsa_is_3",
-			giveRequest: &openrtb_ext.RequestWrapper{
-				BidRequest: &openrtb2.BidRequest{
-					Regs: &openrtb2.Regs{
-						Ext: json.RawMessage(`{"dsa": {"dsarequired": 3}}`),
-					},
+			name:        "bid_is_nil",
+			bid:         nil,
+			expectedDSA: nil,
+		},
+		{
+			name: "bid.bid_is_nil",
+			bid: &entities.PbsOrtbBid{
+				Bid: nil,
+			},
+			expectedDSA: nil,
+		},
+		{
+			name: "bid.bid.ext_is_nil",
+			bid: &entities.PbsOrtbBid{
+				Bid: &openrtb2.Bid{
+					Ext: nil,
 				},
 			},
-			wantRequired: true,
+			expectedDSA: nil,
+		},
+		{
+			name: "bid.bid.ext_is_empty",
+			bid: &entities.PbsOrtbBid{
+				Bid: &openrtb2.Bid{
+					Ext: json.RawMessage(`{}`),
+				},
+			},
+			expectedDSA: nil,
+		},
+		{
+			name: "bid.bid.ext.dsa_is_populated",
+			bid: &entities.PbsOrtbBid{
+				Bid: &openrtb2.Bid{
+					Ext: json.RawMessage(`{"dsa": {"behalf":"test1","paid":"test2","adrender":1}}`),
+				},
+			},
+			expectedDSA: &openrtb_ext.ExtBidDSA{
+				Behalf:   "test1",
+				Paid:     "test2",
+				AdRender: ptrutil.ToPtr[int8](1),
+			},
 		},
 	}
 
 	for _, tt := range tests {
 		t.Run(tt.name, func(t *testing.T) {
-			required := dsaRequired(tt.giveRequest)
-			assert.Equal(t, tt.wantRequired, required)
+			dsa := getBidDSA(tt.bid)
+			assert.Equal(t, tt.expectedDSA, dsa)
 		})
 	}
 }
diff --git a/endpoints/cookie_sync_test.go b/endpoints/cookie_sync_test.go
index adfdcb22..35d7dd4b 100644
--- a/endpoints/cookie_sync_test.go
+++ b/endpoints/cookie_sync_test.go
@@ -4,6 +4,7 @@ import (
 	"context"
 	"encoding/json"
 	"errors"
+	"fmt"
 	"io"
 	"net/http"
 	"net/http/httptest"
@@ -11,6 +12,7 @@ import (
 	"strings"
 	"testing"
 	"testing/iotest"
+	"time"
 
 	"github.com/prebid/prebid-server/v2/analytics"
 	"github.com/prebid/prebid-server/v2/config"
@@ -23,11 +25,19 @@ import (
 	"github.com/prebid/prebid-server/v2/privacy/ccpa"
 	"github.com/prebid/prebid-server/v2/usersync"
 	"github.com/prebid/prebid-server/v2/util/ptrutil"
-
 	"github.com/stretchr/testify/assert"
 	"github.com/stretchr/testify/mock"
 )
 
+// fakeTime implements the Time interface
+type fakeTime struct {
+	time time.Time
+}
+
+func (ft *fakeTime) Now() time.Time {
+	return ft.time
+}
+
 func TestNewCookieSyncEndpoint(t *testing.T) {
 	var (
 		syncersByBidder  = map[string]usersync.Syncer{"a": &MockSyncer{}}
@@ -111,14 +121,16 @@ func TestCookieSyncHandle(t *testing.T) {
 	cookieWithSyncs.Sync("foo", "anyID")
 
 	testCases := []struct {
-		description              string
-		givenCookie              *usersync.Cookie
-		givenBody                io.Reader
-		givenChooserResult       usersync.Result
-		expectedStatusCode       int
-		expectedBody             string
-		setMetricsExpectations   func(*metrics.MetricsEngineMock)
-		setAnalyticsExpectations func(*MockAnalyticsRunner)
+		description                     string
+		givenCookie                     *usersync.Cookie
+		givenBody                       io.Reader
+		givenChooserResult              usersync.Result
+		givenAccountData                map[string]json.RawMessage
+		expectedStatusCode              int
+		expectedBody                    string
+		setMetricsExpectations          func(*metrics.MetricsEngineMock)
+		setAnalyticsExpectations        func(*MockAnalyticsRunner)
+		expectedCookieDeprecationHeader bool
 	}{
 		{
 			description: "Request With Cookie",
@@ -285,6 +297,42 @@ func TestCookieSyncHandle(t *testing.T) {
 				a.On("LogCookieSyncObject", &expected).Once()
 			},
 		},
+		{
+			description: "CookieDeprecation-Set",
+			givenCookie: cookieWithSyncs,
+			givenBody:   strings.NewReader(`{"account": "testAccount"}`),
+			givenChooserResult: usersync.Result{
+				Status:           usersync.StatusOK,
+				BiddersEvaluated: []usersync.BidderEvaluation{{Bidder: "a", SyncerKey: "aSyncer", Status: usersync.StatusAlreadySynced}},
+				SyncersChosen:    []usersync.SyncerChoice{{Bidder: "a", Syncer: &syncer}},
+			},
+			givenAccountData: map[string]json.RawMessage{
+				"testAccount": json.RawMessage(`{"id":"1","privacy":{"privacysandbox":{"cookiedeprecation":{"enabled":true,"ttlsec":86400}}}}`),
+			},
+			expectedStatusCode:              200,
+			expectedCookieDeprecationHeader: true,
+			expectedBody: `{"status":"ok","bidder_status":[` +
+				`{"bidder":"a","no_cookie":true,"usersync":{"url":"aURL","type":"redirect","supportCORS":true}}` +
+				`]}` + "\n",
+			setMetricsExpectations: func(m *metrics.MetricsEngineMock) {
+				m.On("RecordCookieSync", metrics.CookieSyncOK).Once()
+				m.On("RecordSyncerRequest", "aSyncer", metrics.SyncerCookieSyncAlreadySynced).Once()
+			},
+			setAnalyticsExpectations: func(a *MockAnalyticsRunner) {
+				expected := analytics.CookieSyncObject{
+					Status: 200,
+					Errors: nil,
+					BidderStatus: []*analytics.CookieSyncBidder{
+						{
+							BidderCode:   "a",
+							NoCookie:     true,
+							UsersyncInfo: &analytics.UsersyncInfo{URL: "aURL", Type: "redirect", SupportCORS: true},
+						},
+					},
+				}
+				a.On("LogCookieSyncObject", &expected).Once()
+			},
+		},
 	}
 
 	for _, test := range testCases {
@@ -294,7 +342,9 @@ func TestCookieSyncHandle(t *testing.T) {
 		mockAnalytics := MockAnalyticsRunner{}
 		test.setAnalyticsExpectations(&mockAnalytics)
 
-		fakeAccountFetcher := FakeAccountsFetcher{}
+		fakeAccountFetcher := FakeAccountsFetcher{
+			AccountData: test.givenAccountData,
+		}
 
 		gdprPermsBuilder := fakePermissionsBuilder{
 			permissions: &fakePermissions{},
@@ -329,6 +379,7 @@ func TestCookieSyncHandle(t *testing.T) {
 			metrics:         &mockMetrics,
 			pbsAnalytics:    &mockAnalytics,
 			accountsFetcher: &fakeAccountFetcher,
+			time:            &fakeTime{time: time.Date(2024, 2, 22, 9, 42, 4, 13, time.UTC)},
 		}
 		assert.NoError(t, endpoint.config.MarshalAccountDefaults())
 
@@ -336,6 +387,16 @@ func TestCookieSyncHandle(t *testing.T) {
 
 		assert.Equal(t, test.expectedStatusCode, writer.Code, test.description+":status_code")
 		assert.Equal(t, test.expectedBody, writer.Body.String(), test.description+":body")
+
+		gotCookie := writer.Header().Get("Set-Cookie")
+		if test.expectedCookieDeprecationHeader {
+			wantCookieTTL := endpoint.time.Now().Add(time.Second * time.Duration(86400)).UTC().Format(http.TimeFormat)
+			wantCookie := fmt.Sprintf("receive-cookie-deprecation=1; Path=/; Expires=%v; HttpOnly; Secure; SameSite=None; Partitioned;", wantCookieTTL)
+			assert.Equal(t, wantCookie, gotCookie, test.description)
+		} else {
+			assert.Empty(t, gotCookie, test.description)
+		}
+
 		mockMetrics.AssertExpectations(t)
 		mockAnalytics.AssertExpectations(t)
 	}
@@ -1060,7 +1121,7 @@ func TestCookieSyncParseRequest(t *testing.T) {
 			}},
 		}
 		assert.NoError(t, endpoint.config.MarshalAccountDefaults())
-		request, privacyPolicies, err := endpoint.parseRequest(httpRequest)
+		request, privacyPolicies, _, err := endpoint.parseRequest(httpRequest)
 
 		if test.expectedError == "" {
 			assert.NoError(t, err, test.description+":err")
@@ -2207,3 +2268,131 @@ func getDefaultActivityConfig(componentName string, allow bool) *config.AccountP
 		},
 	}
 }
+
+func TestSetCookieDeprecationHeader(t *testing.T) {
+	getTestRequest := func(addCookie bool) *http.Request {
+		r := httptest.NewRequest("POST", "/cookie_sync", nil)
+		if addCookie {
+			r.AddCookie(&http.Cookie{Name: receiveCookieDeprecation, Value: "1"})
+		}
+		return r
+	}
+
+	tests := []struct {
+		name                            string
+		responseWriter                  http.ResponseWriter
+		request                         *http.Request
+		account                         *config.Account
+		expectedCookieDeprecationHeader bool
+	}{
+		{
+			name:                            "not-present-account-nil",
+			request:                         getTestRequest(false),
+			responseWriter:                  httptest.NewRecorder(),
+			account:                         nil,
+			expectedCookieDeprecationHeader: false,
+		},
+		{
+			name:           "not-present-cookiedeprecation-disabled",
+			request:        getTestRequest(false),
+			responseWriter: httptest.NewRecorder(),
+			account: &config.Account{
+				Privacy: config.AccountPrivacy{
+					PrivacySandbox: config.PrivacySandbox{
+						CookieDeprecation: config.CookieDeprecation{
+							Enabled: false,
+						},
+					},
+				},
+			},
+			expectedCookieDeprecationHeader: false,
+		},
+		{
+			name:           "present-cookiedeprecation-disabled",
+			request:        getTestRequest(true),
+			responseWriter: httptest.NewRecorder(),
+			account: &config.Account{
+				Privacy: config.AccountPrivacy{
+					PrivacySandbox: config.PrivacySandbox{
+						CookieDeprecation: config.CookieDeprecation{
+							Enabled: false,
+						},
+					},
+				},
+			},
+			expectedCookieDeprecationHeader: false,
+		},
+		{
+			name:           "present-cookiedeprecation-enabled",
+			request:        getTestRequest(true),
+			responseWriter: httptest.NewRecorder(),
+			account: &config.Account{
+				Privacy: config.AccountPrivacy{
+					PrivacySandbox: config.PrivacySandbox{
+						CookieDeprecation: config.CookieDeprecation{
+							Enabled: true,
+							TTLSec:  86400,
+						},
+					},
+				},
+			},
+
+			expectedCookieDeprecationHeader: false,
+		},
+		{
+			name:                            "present-account-nil",
+			request:                         getTestRequest(true),
+			responseWriter:                  httptest.NewRecorder(),
+			account:                         nil,
+			expectedCookieDeprecationHeader: false,
+		},
+		{
+			name:           "not-present-cookiedeprecation-enabled",
+			request:        getTestRequest(false),
+			responseWriter: httptest.NewRecorder(),
+			account: &config.Account{
+				Privacy: config.AccountPrivacy{
+					PrivacySandbox: config.PrivacySandbox{
+						CookieDeprecation: config.CookieDeprecation{
+							Enabled: true,
+							TTLSec:  86400,
+						},
+					},
+				},
+			},
+			expectedCookieDeprecationHeader: true,
+		},
+		{
+			name:           "failed-to-read-cookiedeprecation-enabled",
+			request:        &http.Request{}, // nil cookie. error: http: named cookie not present
+			responseWriter: httptest.NewRecorder(),
+			account: &config.Account{
+				Privacy: config.AccountPrivacy{
+					PrivacySandbox: config.PrivacySandbox{
+						CookieDeprecation: config.CookieDeprecation{
+							Enabled: true,
+							TTLSec:  86400,
+						},
+					},
+				},
+			},
+			expectedCookieDeprecationHeader: true,
+		},
+	}
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			c := &cookieSyncEndpoint{
+				time: &fakeTime{time: time.Date(2024, 2, 22, 9, 42, 4, 13, time.UTC)},
+			}
+			c.setCookieDeprecationHeader(tt.responseWriter, tt.request, tt.account)
+			gotCookie := tt.responseWriter.Header().Get("Set-Cookie")
+			if tt.expectedCookieDeprecationHeader {
+				wantCookieTTL := c.time.Now().Add(time.Second * time.Duration(86400)).UTC().Format(http.TimeFormat)
+				wantCookie := fmt.Sprintf("receive-cookie-deprecation=1; Path=/; Expires=%v; HttpOnly; Secure; SameSite=None; Partitioned;", wantCookieTTL)
+				assert.Equal(t, wantCookie, gotCookie, ":set_cookie_deprecation_header")
+			} else {
+				assert.Empty(t, gotCookie, ":set_cookie_deprecation_header")
+			}
+		})
+	}
+}
diff --git a/endpoints/info/bidders_detail_test.go b/endpoints/info/bidders_detail_test.go
index 2911aa8e..4965dece 100644
--- a/endpoints/info/bidders_detail_test.go
+++ b/endpoints/info/bidders_detail_test.go
@@ -453,7 +453,10 @@ func TestBiddersDetailHandler(t *testing.T) {
 	for _, test := range testCases {
 		t.Run(test.description, func(t *testing.T) {
 			responseRecorder := httptest.NewRecorder()
-			handler(responseRecorder, nil, httprouter.Params{{"bidderName", test.givenBidder}})
+			handler(responseRecorder, nil, httprouter.Params{{
+				Key:   "bidderName",
+				Value: test.givenBidder,
+			}})
 
 			result := responseRecorder.Result()
 			assert.Equal(t, result.StatusCode, test.expectedStatus, test.description+":statuscode")
diff --git a/endpoints/openrtb2/auction_test.go b/endpoints/openrtb2/auction_test.go
index a9e16c94..f51979db 100644
--- a/endpoints/openrtb2/auction_test.go
+++ b/endpoints/openrtb2/auction_test.go
@@ -18,6 +18,7 @@ import (
 	"time"
 
 	"github.com/buger/jsonparser"
+	jsoniter "github.com/json-iterator/go"
 	"github.com/julienschmidt/httprouter"
 	"github.com/prebid/openrtb/v20/native1"
 	nativeRequests "github.com/prebid/openrtb/v20/native1/request"
@@ -43,6 +44,11 @@ import (
 
 const jsonFileExtension string = ".json"
 
+func TestMain(m *testing.M) {
+	jsoniter.RegisterExtension(&jsonutil.RawMessageExtension{})
+	os.Exit(m.Run())
+}
+
 func TestJsonSampleRequests(t *testing.T) {
 	testSuites := []struct {
 		description          string
@@ -1644,6 +1650,8 @@ func TestValidateRequest(t *testing.T) {
 		description           string
 		givenIsAmp            bool
 		givenRequestWrapper   *openrtb_ext.RequestWrapper
+		givenHttpRequest      *http.Request
+		givenAccount          *config.Account
 		expectedErrorList     []error
 		expectedChannelObject *openrtb_ext.ExtRequestPrebidChannel
 	}{
@@ -1864,7 +1872,7 @@ func TestValidateRequest(t *testing.T) {
 	}
 
 	for _, test := range testCases {
-		errorList := deps.validateRequest(test.givenRequestWrapper, test.givenIsAmp, false, nil, false)
+		errorList := deps.validateRequest(test.givenAccount, test.givenHttpRequest, test.givenRequestWrapper, test.givenIsAmp, false, nil, false)
 		assert.Equalf(t, test.expectedErrorList, errorList, "Error doesn't match: %s\n", test.description)
 
 		if len(errorList) == 0 {
@@ -3047,7 +3055,7 @@ func TestCurrencyTrunc(t *testing.T) {
 		Cur: []string{"USD", "EUR"},
 	}
 
-	errL := deps.validateRequest(&openrtb_ext.RequestWrapper{BidRequest: &req}, false, false, nil, false)
+	errL := deps.validateRequest(nil, nil, &openrtb_ext.RequestWrapper{BidRequest: &req}, false, false, nil, false)
 
 	expectedError := errortypes.Warning{Message: "A prebid request can only process one currency. Taking the first currency in the list, USD, as the active currency"}
 	assert.ElementsMatch(t, errL, []error{&expectedError})
@@ -3098,7 +3106,7 @@ func TestCCPAInvalid(t *testing.T) {
 		},
 	}
 
-	errL := deps.validateRequest(&openrtb_ext.RequestWrapper{BidRequest: &req}, false, false, nil, false)
+	errL := deps.validateRequest(nil, nil, &openrtb_ext.RequestWrapper{BidRequest: &req}, false, false, nil, false)
 
 	expectedWarning := errortypes.Warning{
 		Message:     "CCPA consent is invalid and will be ignored. (request.regs.ext.us_privacy must contain 4 characters)",
@@ -3152,7 +3160,7 @@ func TestNoSaleInvalid(t *testing.T) {
 		Ext: json.RawMessage(`{"prebid": {"nosale": ["*", "appnexus"]} }`),
 	}
 
-	errL := deps.validateRequest(&openrtb_ext.RequestWrapper{BidRequest: &req}, false, false, nil, false)
+	errL := deps.validateRequest(nil, nil, &openrtb_ext.RequestWrapper{BidRequest: &req}, false, false, nil, false)
 
 	expectedError := errors.New("request.ext.prebid.nosale is invalid: can only specify all bidders if no other bidders are provided")
 	assert.ElementsMatch(t, errL, []error{expectedError})
@@ -3204,7 +3212,7 @@ func TestValidateSourceTID(t *testing.T) {
 		},
 	}
 
-	deps.validateRequest(&openrtb_ext.RequestWrapper{BidRequest: &req}, false, false, nil, false)
+	deps.validateRequest(nil, nil, &openrtb_ext.RequestWrapper{BidRequest: &req}, false, false, nil, false)
 	assert.NotEmpty(t, req.Source.TID, "Expected req.Source.TID to be filled with a randomly generated UID")
 }
 
@@ -3251,7 +3259,7 @@ func TestSChainInvalid(t *testing.T) {
 		Ext: json.RawMessage(`{"prebid":{"schains":[{"bidders":["appnexus"],"schain":{"complete":1,"nodes":[{"asi":"directseller1.com","sid":"00001","rid":"BidRequest1","hp":1}],"ver":"1.0"}}, {"bidders":["appnexus"],"schain":{"complete":1,"nodes":[{"asi":"directseller2.com","sid":"00002","rid":"BidRequest2","hp":1}],"ver":"1.0"}}]}}`),
 	}
 
-	errL := deps.validateRequest(&openrtb_ext.RequestWrapper{BidRequest: &req}, false, false, nil, false)
+	errL := deps.validateRequest(nil, nil, &openrtb_ext.RequestWrapper{BidRequest: &req}, false, false, nil, false)
 
 	expectedError := errors.New("request.ext.prebid.schains contains multiple schains for bidder appnexus; it must contain no more than one per bidder.")
 	assert.ElementsMatch(t, errL, []error{expectedError})
@@ -3820,7 +3828,7 @@ func TestEidPermissionsInvalid(t *testing.T) {
 		Ext: json.RawMessage(`{"prebid": {"data": {"eidpermissions": [{"source":"a", "bidders":[]}]} } }`),
 	}
 
-	errL := deps.validateRequest(&openrtb_ext.RequestWrapper{BidRequest: &req}, false, false, nil, false)
+	errL := deps.validateRequest(nil, nil, &openrtb_ext.RequestWrapper{BidRequest: &req}, false, false, nil, false)
 
 	expectedError := errors.New(`request.ext.prebid.data.eidpermissions[0] missing or empty required field: "bidders"`)
 	assert.ElementsMatch(t, errL, []error{expectedError})
@@ -5128,6 +5136,8 @@ func TestValidateStoredResp(t *testing.T) {
 	testCases := []struct {
 		description               string
 		givenRequestWrapper       *openrtb_ext.RequestWrapper
+		givenHttpRequest          *http.Request
+		givenAccount              *config.Account
 		expectedErrorList         []error
 		hasStoredAuctionResponses bool
 		storedBidResponses        stored_responses.ImpBidderStoredResp
@@ -5669,7 +5679,7 @@ func TestValidateStoredResp(t *testing.T) {
 
 	for _, test := range testCases {
 		t.Run(test.description, func(t *testing.T) {
-			errorList := deps.validateRequest(test.givenRequestWrapper, false, test.hasStoredAuctionResponses, test.storedBidResponses, false)
+			errorList := deps.validateRequest(test.givenAccount, test.givenHttpRequest, test.givenRequestWrapper, false, test.hasStoredAuctionResponses, test.storedBidResponses, false)
 			assert.Equalf(t, test.expectedErrorList, errorList, "Error doesn't match: %s\n", test.description)
 		})
 	}
@@ -6108,3 +6118,392 @@ func TestValidateAliases(t *testing.T) {
 func fakeNormalizeBidderName(name string) (openrtb_ext.BidderName, bool) {
 	return openrtb_ext.BidderName(strings.ToLower(name)), true
 }
+
+func TestValidateOrFillCDep(t *testing.T) {
+	type args struct {
+		httpReq *http.Request
+		req     *openrtb_ext.RequestWrapper
+		account config.Account
+	}
+	tests := []struct {
+		name          string
+		args          args
+		wantDeviceExt json.RawMessage
+		wantErr       error
+	}{
+		{
+			name:          "account-nil",
+			wantDeviceExt: nil,
+			wantErr:       nil,
+		},
+		{
+			name: "cookie-deprecation-not-enabled",
+			args: args{
+				httpReq: &http.Request{
+					Header: http.Header{secCookieDeprecation: []string{"example_label_1"}},
+				},
+				req: &openrtb_ext.RequestWrapper{
+					BidRequest: &openrtb2.BidRequest{},
+				},
+			},
+			wantDeviceExt: nil,
+			wantErr:       nil,
+		},
+		{
+			name: "cookie-deprecation-disabled-explicitly",
+			args: args{
+				httpReq: &http.Request{
+					Header: http.Header{secCookieDeprecation: []string{"example_label_1"}},
+				},
+				req: &openrtb_ext.RequestWrapper{
+					BidRequest: &openrtb2.BidRequest{},
+				},
+				account: config.Account{
+					Privacy: config.AccountPrivacy{
+						PrivacySandbox: config.PrivacySandbox{
+							CookieDeprecation: config.CookieDeprecation{
+								Enabled: false,
+							},
+						},
+					},
+				},
+			},
+			wantDeviceExt: nil,
+			wantErr:       nil,
+		},
+		{
+			name: "cookie-deprecation-enabled-header-not-present-in-request",
+			args: args{
+				httpReq: &http.Request{},
+				req: &openrtb_ext.RequestWrapper{
+					BidRequest: &openrtb2.BidRequest{},
+				},
+				account: config.Account{
+					Privacy: config.AccountPrivacy{
+						PrivacySandbox: config.PrivacySandbox{
+							CookieDeprecation: config.CookieDeprecation{
+								Enabled: true,
+							},
+						},
+					},
+				},
+			},
+			wantDeviceExt: nil,
+			wantErr:       nil,
+		},
+		{
+			name: "header-present-request-device-nil",
+			args: args{
+				httpReq: &http.Request{
+					Header: http.Header{secCookieDeprecation: []string{"example_label_1"}},
+				},
+				req: &openrtb_ext.RequestWrapper{
+					BidRequest: &openrtb2.BidRequest{},
+				},
+				account: config.Account{
+					Privacy: config.AccountPrivacy{
+						PrivacySandbox: config.PrivacySandbox{
+							CookieDeprecation: config.CookieDeprecation{
+								Enabled: true,
+							},
+						},
+					},
+				},
+			},
+			wantDeviceExt: json.RawMessage(`{"cdep":"example_label_1"}`),
+			wantErr:       nil,
+		},
+		{
+			name: "header-present-request-device-ext-nil",
+			args: args{
+				httpReq: &http.Request{
+					Header: http.Header{secCookieDeprecation: []string{"example_label_1"}},
+				},
+				req: &openrtb_ext.RequestWrapper{
+					BidRequest: &openrtb2.BidRequest{
+						Device: &openrtb2.Device{
+							Ext: nil,
+						},
+					},
+				},
+				account: config.Account{
+					Privacy: config.AccountPrivacy{
+						PrivacySandbox: config.PrivacySandbox{
+							CookieDeprecation: config.CookieDeprecation{
+								Enabled: true,
+							},
+						},
+					},
+				},
+			},
+			wantDeviceExt: json.RawMessage(`{"cdep":"example_label_1"}`),
+			wantErr:       nil,
+		},
+		{
+			name: "header-present-request-device-ext-not-nil",
+			args: args{
+				httpReq: &http.Request{
+					Header: http.Header{secCookieDeprecation: []string{"example_label_1"}},
+				},
+				req: &openrtb_ext.RequestWrapper{
+					BidRequest: &openrtb2.BidRequest{
+						Device: &openrtb2.Device{
+							Ext: json.RawMessage(`{"foo":"bar"}`),
+						},
+					},
+				},
+				account: config.Account{
+					Privacy: config.AccountPrivacy{
+						PrivacySandbox: config.PrivacySandbox{
+							CookieDeprecation: config.CookieDeprecation{
+								Enabled: true,
+							},
+						},
+					},
+				},
+			},
+			wantDeviceExt: json.RawMessage(`{"cdep":"example_label_1","foo":"bar"}`),
+			wantErr:       nil,
+		},
+		{
+			name: "header-present-with-length-more-than-100",
+			args: args{
+				httpReq: &http.Request{
+					Header: http.Header{secCookieDeprecation: []string{"zjfXqGxXFI8yura8AhQl1DK2EMMmryrC8haEpAlwjoerrFfEo2MQTXUq6cSmLohI8gjsnkGU4oAzvXd4TTAESzEKsoYjRJ2zKxmEa"}},
+				},
+				req: &openrtb_ext.RequestWrapper{
+					BidRequest: &openrtb2.BidRequest{
+						Device: &openrtb2.Device{
+							Ext: json.RawMessage(`{"foo":"bar"}`),
+						},
+					},
+				},
+				account: config.Account{
+					Privacy: config.AccountPrivacy{
+						PrivacySandbox: config.PrivacySandbox{
+							CookieDeprecation: config.CookieDeprecation{
+								Enabled: true,
+							},
+						},
+					},
+				},
+			},
+			wantDeviceExt: json.RawMessage(`{"foo":"bar"}`),
+			wantErr: &errortypes.Warning{
+				Message:     "request.device.ext.cdep must not exceed 100 characters",
+				WarningCode: errortypes.SecCookieDeprecationLenWarningCode,
+			},
+		},
+		{
+			name: "header-present-request-device-ext-cdep-present",
+			args: args{
+				httpReq: &http.Request{
+					Header: http.Header{secCookieDeprecation: []string{"example_label_1"}},
+				},
+				req: &openrtb_ext.RequestWrapper{
+					BidRequest: &openrtb2.BidRequest{
+						Device: &openrtb2.Device{
+							Ext: json.RawMessage(`{"foo":"bar","cdep":"example_label_2"}`),
+						},
+					},
+				},
+				account: config.Account{
+					Privacy: config.AccountPrivacy{
+						PrivacySandbox: config.PrivacySandbox{
+							CookieDeprecation: config.CookieDeprecation{
+								Enabled: true,
+							},
+						},
+					},
+				},
+			},
+			wantDeviceExt: json.RawMessage(`{"foo":"bar","cdep":"example_label_2"}`),
+			wantErr:       nil,
+		},
+		{
+			name: "header-present-request-device-ext-invalid",
+			args: args{
+				httpReq: &http.Request{
+					Header: http.Header{secCookieDeprecation: []string{"example_label_1"}},
+				},
+				req: &openrtb_ext.RequestWrapper{
+					BidRequest: &openrtb2.BidRequest{
+						Device: &openrtb2.Device{
+							Ext: json.RawMessage(`{`),
+						},
+					},
+				},
+				account: config.Account{
+					Privacy: config.AccountPrivacy{
+						PrivacySandbox: config.PrivacySandbox{
+							CookieDeprecation: config.CookieDeprecation{
+								Enabled: true,
+							},
+						},
+					},
+				},
+			},
+			wantDeviceExt: json.RawMessage(`{`),
+			wantErr: &errortypes.FailedToUnmarshal{
+				Message: "expects \" or n, but found \x00",
+			},
+		},
+	}
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			err := validateOrFillCDep(tt.args.httpReq, tt.args.req, &tt.args.account)
+			assert.Equal(t, tt.wantErr, err)
+			if tt.args.req != nil {
+				err := tt.args.req.RebuildRequest()
+				assert.NoError(t, err)
+			}
+			if tt.wantDeviceExt == nil {
+				if tt.args.req != nil && tt.args.req.Device != nil {
+					assert.Nil(t, tt.args.req.Device.Ext)
+				}
+			} else {
+				assert.Equal(t, string(tt.wantDeviceExt), string(tt.args.req.Device.Ext))
+			}
+		})
+	}
+}
+
+func TestValidateRequestCookieDeprecation(t *testing.T) {
+	testCases :=
+		[]struct {
+			name         string
+			givenAccount *config.Account
+			httpReq      *http.Request
+			reqWrapper   *openrtb_ext.RequestWrapper
+			wantErrs     []error
+			wantCDep     string
+		}{
+			{
+				name: "header-with-length-less-than-100",
+				httpReq: func() *http.Request {
+					req := httptest.NewRequest("POST", "/openrtb2/auction", nil)
+					req.Header.Set(secCookieDeprecation, "sample-value")
+					return req
+				}(),
+				givenAccount: &config.Account{
+					ID: "1",
+					Privacy: config.AccountPrivacy{
+						PrivacySandbox: config.PrivacySandbox{
+							CookieDeprecation: config.CookieDeprecation{
+								Enabled: true,
+								TTLSec:  86400,
+							},
+						},
+					},
+				},
+				reqWrapper: &openrtb_ext.RequestWrapper{
+					BidRequest: &openrtb2.BidRequest{
+						ID:  "Some-ID",
+						App: &openrtb2.App{},
+						Imp: []openrtb2.Imp{
+							{
+								ID: "Some-Imp-ID",
+								Banner: &openrtb2.Banner{
+									Format: []openrtb2.Format{
+										{
+											W: 600,
+											H: 500,
+										},
+										{
+											W: 300,
+											H: 600,
+										},
+									},
+								},
+								Ext: []byte(`{"pubmatic":{"publisherId": 12345678}}`),
+							},
+						},
+					},
+				},
+				wantErrs: []error{},
+				wantCDep: "sample-value",
+			},
+			{
+				name: "header-with-length-more-than-100",
+				httpReq: func() *http.Request {
+					req := httptest.NewRequest("POST", "/openrtb2/auction", nil)
+					req.Header.Set(secCookieDeprecation, "zjfXqGxXFI8yura8AhQl1DK2EMMmryrC8haEpAlwjoerrFfEo2MQTXUq6cSmLohI8gjsnkGU4oAzvXd4TTAESzEKsoYjRJ2zKxmEa")
+					return req
+				}(),
+				givenAccount: &config.Account{
+					ID: "1",
+					Privacy: config.AccountPrivacy{
+						PrivacySandbox: config.PrivacySandbox{
+							CookieDeprecation: config.CookieDeprecation{
+								Enabled: true,
+								TTLSec:  86400,
+							},
+						},
+					},
+				},
+				reqWrapper: &openrtb_ext.RequestWrapper{
+					BidRequest: &openrtb2.BidRequest{
+						ID:  "Some-ID",
+						App: &openrtb2.App{},
+						Imp: []openrtb2.Imp{
+							{
+								ID: "Some-Imp-ID",
+								Banner: &openrtb2.Banner{
+									Format: []openrtb2.Format{
+										{
+											W: 600,
+											H: 500,
+										},
+										{
+											W: 300,
+											H: 600,
+										},
+									},
+								},
+								Ext: []byte(`{"pubmatic":{"publisherId": 12345678}}`),
+							},
+						},
+					},
+				},
+				wantErrs: []error{
+					&errortypes.Warning{
+						Message:     "request.device.ext.cdep must not exceed 100 characters",
+						WarningCode: errortypes.SecCookieDeprecationLenWarningCode,
+					},
+				},
+				wantCDep: "",
+			},
+		}
+
+	deps := &endpointDeps{
+		fakeUUIDGenerator{},
+		&warningsCheckExchange{},
+		mockBidderParamValidator{},
+		&mockStoredReqFetcher{},
+		empty_fetcher.EmptyFetcher{},
+		&mockAccountFetcher{},
+		&config.Configuration{},
+		&metricsConfig.NilMetricsEngine{},
+		analyticsBuild.New(&config.Analytics{}),
+		map[string]string{},
+		false,
+		[]byte{},
+		openrtb_ext.BuildBidderMap(),
+		nil,
+		nil,
+		hardcodedResponseIPValidator{response: true},
+		empty_fetcher.EmptyFetcher{},
+		hooks.EmptyPlanBuilder{},
+		nil,
+		openrtb_ext.NormalizeBidderName,
+	}
+
+	for _, test := range testCases {
+		errs := deps.validateRequest(test.givenAccount, test.httpReq, test.reqWrapper, false, false, stored_responses.ImpBidderStoredResp{}, false)
+		assert.Equal(t, test.wantErrs, errs)
+		test.reqWrapper.RebuildRequest()
+		deviceExt, err := test.reqWrapper.GetDeviceExt()
+		assert.NoError(t, err)
+		assert.Equal(t, test.wantCDep, deviceExt.GetCDep())
+	}
+}
diff --git a/exchange/exchange_test.go b/exchange/exchange_test.go
index e484e21a..78bc167b 100644
--- a/exchange/exchange_test.go
+++ b/exchange/exchange_test.go
@@ -663,7 +663,7 @@ func TestOverrideWithCustomCurrency(t *testing.T) {
 	}.Builder
 	e.currencyConverter = mockCurrencyConverter
 	e.categoriesFetcher = categoriesFetcher
-	e.bidIDGenerator = &mockBidIDGenerator{false, false}
+	e.bidIDGenerator = &fakeBidIDGenerator{GenerateBidID: false, ReturnError: false}
 	e.requestSplitter = requestSplitter{
 		me:               e.me,
 		gdprPermsBuilder: e.gdprPermsBuilder,
@@ -768,7 +768,7 @@ func TestAdapterCurrency(t *testing.T) {
 		}.Builder,
 		currencyConverter: currencyConverter,
 		categoriesFetcher: nilCategoryFetcher{},
-		bidIDGenerator:    &mockBidIDGenerator{false, false},
+		bidIDGenerator:    &fakeBidIDGenerator{GenerateBidID: false, ReturnError: false},
 		adapterMap: map[openrtb_ext.BidderName]AdaptedBidder{
 			openrtb_ext.BidderName("appnexus"): AdaptBidder(mockBidder, nil, &config.Configuration{}, &metricsConfig.NilMetricsEngine{}, openrtb_ext.BidderName("appnexus"), nil, ""),
 		},
@@ -846,7 +846,7 @@ func TestFloorsSignalling(t *testing.T) {
 		}.Builder,
 		currencyConverter: currencyConverter,
 		categoriesFetcher: nilCategoryFetcher{},
-		bidIDGenerator:    &mockBidIDGenerator{false, false},
+		bidIDGenerator:    &fakeBidIDGenerator{GenerateBidID: false, ReturnError: false},
 		priceFloorEnabled: true,
 		priceFloorFetcher: &mockPriceFloorFetcher{},
 	}
@@ -1129,7 +1129,7 @@ func TestReturnCreativeEndToEnd(t *testing.T) {
 	}.Builder
 	e.currencyConverter = currency.NewRateConverter(&http.Client{}, "", time.Duration(0))
 	e.categoriesFetcher = categoriesFetcher
-	e.bidIDGenerator = &mockBidIDGenerator{false, false}
+	e.bidIDGenerator = &fakeBidIDGenerator{GenerateBidID: false, ReturnError: false}
 	e.requestSplitter = requestSplitter{
 		me:               e.me,
 		gdprPermsBuilder: e.gdprPermsBuilder,
@@ -2139,9 +2139,9 @@ func runSpec(t *testing.T, filename string, spec *exchangeSpec) {
 			},
 		},
 	}
-	bidIdGenerator := &mockBidIDGenerator{}
+	bidIdGenerator := &fakeBidIDGenerator{}
 	if spec.BidIDGenerator != nil {
-		*bidIdGenerator = *spec.BidIDGenerator
+		bidIdGenerator = spec.BidIDGenerator
 	}
 	ex := newExchangeForTests(t, filename, spec.OutgoingRequests, aliases, privacyConfig, bidIdGenerator, spec.HostSChainFlag, spec.FloorsEnabled, spec.HostConfigBidValidation, spec.Server)
 	biddersInAuction := findBiddersInAuction(t, filename, &spec.IncomingRequest.OrtbRequest)
@@ -2457,31 +2457,35 @@ func newExchangeForTests(t *testing.T, filename string, expectations map[string]
 	}
 }
 
-type mockBidIDGenerator struct {
+type fakeBidIDGenerator struct {
 	GenerateBidID bool `json:"generateBidID"`
 	ReturnError   bool `json:"returnError"`
+	bidCount      map[string]int
 }
 
-func (big *mockBidIDGenerator) Enabled() bool {
-	return big.GenerateBidID
+func (f *fakeBidIDGenerator) Enabled() bool {
+	return f.GenerateBidID
 }
 
-func (big *mockBidIDGenerator) New() (string, error) {
+func (f *fakeBidIDGenerator) New(bidder string) (string, error) {
+	if f.ReturnError {
+		return "", errors.New("Test error generating bid.ext.prebid.bidid")
+	}
 
-	if big.ReturnError {
-		err := errors.New("Test error generating bid.ext.prebid.bidid")
-		return "", err
+	if f.bidCount == nil {
+		f.bidCount = make(map[string]int)
 	}
-	return "mock_uuid", nil
 
+	f.bidCount[bidder] += 1
+	return fmt.Sprintf("bid-%v-%v", bidder, f.bidCount[bidder]), nil
 }
 
-type fakeRandomDeduplicateBidBooleanGenerator struct {
-	returnValue bool
+type fakeBooleanGenerator struct {
+	value bool
 }
 
-func (m *fakeRandomDeduplicateBidBooleanGenerator) Generate() bool {
-	return m.returnValue
+func (f *fakeBooleanGenerator) Generate() bool {
+	return f.value
 }
 
 func newExtRequest() openrtb_ext.ExtRequest {
@@ -2566,10 +2570,10 @@ func TestCategoryMapping(t *testing.T) {
 	bid3 := openrtb2.Bid{ID: "bid_id3", ImpID: "imp_id3", Price: 30.0000, Cat: cats3, W: 1, H: 1}
 	bid4 := openrtb2.Bid{ID: "bid_id4", ImpID: "imp_id4", Price: 40.0000, Cat: cats4, W: 1, H: 1}
 
-	bid1_1 := entities.PbsOrtbBid{&bid1, nil, "video", nil, &openrtb_ext.ExtBidPrebidVideo{Duration: 30}, nil, nil, 0, false, "", 10.0000, "USD", ""}
-	bid1_2 := entities.PbsOrtbBid{&bid2, nil, "video", nil, &openrtb_ext.ExtBidPrebidVideo{Duration: 40}, nil, nil, 0, false, "", 20.0000, "USD", ""}
-	bid1_3 := entities.PbsOrtbBid{&bid3, nil, "video", nil, &openrtb_ext.ExtBidPrebidVideo{Duration: 30, PrimaryCategory: "AdapterOverride"}, nil, nil, 0, false, "", 30.0000, "USD", ""}
-	bid1_4 := entities.PbsOrtbBid{&bid4, nil, "video", nil, &openrtb_ext.ExtBidPrebidVideo{Duration: 30}, nil, nil, 0, false, "", 40.0000, "USD", ""}
+	bid1_1 := entities.PbsOrtbBid{Bid: &bid1, BidMeta: nil, BidType: "video", BidTargets: nil, BidVideo: &openrtb_ext.ExtBidPrebidVideo{Duration: 30}, BidEvents: nil, BidFloors: nil, DealPriority: 0, DealTierSatisfied: false, GeneratedBidID: "", OriginalBidCPM: 10.0000, OriginalBidCur: "USD", TargetBidderCode: ""}
+	bid1_2 := entities.PbsOrtbBid{Bid: &bid2, BidMeta: nil, BidType: "video", BidTargets: nil, BidVideo: &openrtb_ext.ExtBidPrebidVideo{Duration: 40}, BidEvents: nil, BidFloors: nil, DealPriority: 0, DealTierSatisfied: false, GeneratedBidID: "", OriginalBidCPM: 20.0000, OriginalBidCur: "USD", TargetBidderCode: ""}
+	bid1_3 := entities.PbsOrtbBid{Bid: &bid3, BidMeta: nil, BidType: "video", BidTargets: nil, BidVideo: &openrtb_ext.ExtBidPrebidVideo{Duration: 30, PrimaryCategory: "AdapterOverride"}, BidEvents: nil, BidFloors: nil, DealPriority: 0, DealTierSatisfied: false, GeneratedBidID: "", OriginalBidCPM: 30.0000, OriginalBidCur: "USD", TargetBidderCode: ""}
+	bid1_4 := entities.PbsOrtbBid{Bid: &bid4, BidMeta: nil, BidType: "video", BidTargets: nil, BidVideo: &openrtb_ext.ExtBidPrebidVideo{Duration: 30}, BidEvents: nil, BidFloors: nil, DealPriority: 0, DealTierSatisfied: false, GeneratedBidID: "", OriginalBidCPM: 40.0000, OriginalBidCur: "USD", TargetBidderCode: ""}
 
 	innerBids := []*entities.PbsOrtbBid{
 		&bid1_1,
@@ -2621,10 +2625,10 @@ func TestCategoryMappingNoIncludeBrandCategory(t *testing.T) {
 	bid3 := openrtb2.Bid{ID: "bid_id3", ImpID: "imp_id3", Price: 30.0000, Cat: cats3, W: 1, H: 1}
 	bid4 := openrtb2.Bid{ID: "bid_id4", ImpID: "imp_id4", Price: 40.0000, Cat: cats4, W: 1, H: 1}
 
-	bid1_1 := entities.PbsOrtbBid{&bid1, nil, "video", nil, &openrtb_ext.ExtBidPrebidVideo{Duration: 30}, nil, nil, 0, false, "", 10.0000, "USD", ""}
-	bid1_2 := entities.PbsOrtbBid{&bid2, nil, "video", nil, &openrtb_ext.ExtBidPrebidVideo{Duration: 40}, nil, nil, 0, false, "", 20.0000, "USD", ""}
-	bid1_3 := entities.PbsOrtbBid{&bid3, nil, "video", nil, &openrtb_ext.ExtBidPrebidVideo{Duration: 30, PrimaryCategory: "AdapterOverride"}, nil, nil, 0, false, "", 30.0000, "USD", ""}
-	bid1_4 := entities.PbsOrtbBid{&bid4, nil, "video", nil, &openrtb_ext.ExtBidPrebidVideo{Duration: 50}, nil, nil, 0, false, "", 40.0000, "USD", ""}
+	bid1_1 := entities.PbsOrtbBid{Bid: &bid1, BidMeta: nil, BidType: "video", BidTargets: nil, BidVideo: &openrtb_ext.ExtBidPrebidVideo{Duration: 30}, BidEvents: nil, BidFloors: nil, DealPriority: 0, DealTierSatisfied: false, GeneratedBidID: "", OriginalBidCPM: 10.0000, OriginalBidCur: "USD", TargetBidderCode: ""}
+	bid1_2 := entities.PbsOrtbBid{Bid: &bid2, BidMeta: nil, BidType: "video", BidTargets: nil, BidVideo: &openrtb_ext.ExtBidPrebidVideo{Duration: 40}, BidEvents: nil, BidFloors: nil, DealPriority: 0, DealTierSatisfied: false, GeneratedBidID: "", OriginalBidCPM: 20.0000, OriginalBidCur: "USD", TargetBidderCode: ""}
+	bid1_3 := entities.PbsOrtbBid{Bid: &bid3, BidMeta: nil, BidType: "video", BidTargets: nil, BidVideo: &openrtb_ext.ExtBidPrebidVideo{Duration: 30, PrimaryCategory: "AdapterOverride"}, BidEvents: nil, BidFloors: nil, DealPriority: 0, DealTierSatisfied: false, GeneratedBidID: "", OriginalBidCPM: 30.0000, OriginalBidCur: "USD", TargetBidderCode: ""}
+	bid1_4 := entities.PbsOrtbBid{Bid: &bid4, BidMeta: nil, BidType: "video", BidTargets: nil, BidVideo: &openrtb_ext.ExtBidPrebidVideo{Duration: 50}, BidEvents: nil, BidFloors: nil, DealPriority: 0, DealTierSatisfied: false, GeneratedBidID: "", OriginalBidCPM: 40.0000, OriginalBidCur: "USD", TargetBidderCode: ""}
 
 	innerBids := []*entities.PbsOrtbBid{
 		&bid1_1,
@@ -2675,9 +2679,9 @@ func TestCategoryMappingTranslateCategoriesNil(t *testing.T) {
 	bid2 := openrtb2.Bid{ID: "bid_id2", ImpID: "imp_id2", Price: 20.0000, Cat: cats2, W: 1, H: 1}
 	bid3 := openrtb2.Bid{ID: "bid_id3", ImpID: "imp_id3", Price: 30.0000, Cat: cats3, W: 1, H: 1}
 
-	bid1_1 := entities.PbsOrtbBid{&bid1, nil, "video", nil, &openrtb_ext.ExtBidPrebidVideo{Duration: 30}, nil, nil, 0, false, "", 10.0000, "USD", ""}
-	bid1_2 := entities.PbsOrtbBid{&bid2, nil, "video", nil, &openrtb_ext.ExtBidPrebidVideo{Duration: 40}, nil, nil, 0, false, "", 20.0000, "USD", ""}
-	bid1_3 := entities.PbsOrtbBid{&bid3, nil, "video", nil, &openrtb_ext.ExtBidPrebidVideo{Duration: 30}, nil, nil, 0, false, "", 30.0000, "USD", ""}
+	bid1_1 := entities.PbsOrtbBid{Bid: &bid1, BidMeta: nil, BidType: "video", BidTargets: nil, BidVideo: &openrtb_ext.ExtBidPrebidVideo{Duration: 30}, BidEvents: nil, BidFloors: nil, DealPriority: 0, DealTierSatisfied: false, GeneratedBidID: "", OriginalBidCPM: 10.0000, OriginalBidCur: "USD", TargetBidderCode: ""}
+	bid1_2 := entities.PbsOrtbBid{Bid: &bid2, BidMeta: nil, BidType: "video", BidTargets: nil, BidVideo: &openrtb_ext.ExtBidPrebidVideo{Duration: 40}, BidEvents: nil, BidFloors: nil, DealPriority: 0, DealTierSatisfied: false, GeneratedBidID: "", OriginalBidCPM: 20.0000, OriginalBidCur: "USD", TargetBidderCode: ""}
+	bid1_3 := entities.PbsOrtbBid{Bid: &bid3, BidMeta: nil, BidType: "video", BidTargets: nil, BidVideo: &openrtb_ext.ExtBidPrebidVideo{Duration: 30}, BidEvents: nil, BidFloors: nil, DealPriority: 0, DealTierSatisfied: false, GeneratedBidID: "", OriginalBidCPM: 30.0000, OriginalBidCur: "USD", TargetBidderCode: ""}
 
 	innerBids := []*entities.PbsOrtbBid{
 		&bid1_1,
@@ -2757,9 +2761,9 @@ func TestCategoryMappingTranslateCategoriesFalse(t *testing.T) {
 	bid2 := openrtb2.Bid{ID: "bid_id2", ImpID: "imp_id2", Price: 20.0000, Cat: cats2, W: 1, H: 1}
 	bid3 := openrtb2.Bid{ID: "bid_id3", ImpID: "imp_id3", Price: 30.0000, Cat: cats3, W: 1, H: 1}
 
-	bid1_1 := entities.PbsOrtbBid{&bid1, nil, "video", nil, &openrtb_ext.ExtBidPrebidVideo{Duration: 30}, nil, nil, 0, false, "", 10.0000, "USD", ""}
-	bid1_2 := entities.PbsOrtbBid{&bid2, nil, "video", nil, &openrtb_ext.ExtBidPrebidVideo{Duration: 40}, nil, nil, 0, false, "", 20.0000, "USD", ""}
-	bid1_3 := entities.PbsOrtbBid{&bid3, nil, "video", nil, &openrtb_ext.ExtBidPrebidVideo{Duration: 30}, nil, nil, 0, false, "", 30.0000, "USD", ""}
+	bid1_1 := entities.PbsOrtbBid{Bid: &bid1, BidMeta: nil, BidType: "video", BidTargets: nil, BidVideo: &openrtb_ext.ExtBidPrebidVideo{Duration: 30}, BidEvents: nil, BidFloors: nil, DealPriority: 0, DealTierSatisfied: false, GeneratedBidID: "", OriginalBidCPM: 10.0000, OriginalBidCur: "USD", TargetBidderCode: ""}
+	bid1_2 := entities.PbsOrtbBid{Bid: &bid2, BidMeta: nil, BidType: "video", BidTargets: nil, BidVideo: &openrtb_ext.ExtBidPrebidVideo{Duration: 40}, BidEvents: nil, BidFloors: nil, DealPriority: 0, DealTierSatisfied: false, GeneratedBidID: "", OriginalBidCPM: 20.0000, OriginalBidCur: "USD", TargetBidderCode: ""}
+	bid1_3 := entities.PbsOrtbBid{Bid: &bid3, BidMeta: nil, BidType: "video", BidTargets: nil, BidVideo: &openrtb_ext.ExtBidPrebidVideo{Duration: 30}, BidEvents: nil, BidFloors: nil, DealPriority: 0, DealTierSatisfied: false, GeneratedBidID: "", OriginalBidCPM: 30.0000, OriginalBidCur: "USD", TargetBidderCode: ""}
 
 	innerBids := []*entities.PbsOrtbBid{
 		&bid1_1,
@@ -2849,7 +2853,7 @@ func TestCategoryDedupe(t *testing.T) {
 					Currency: "USD",
 				},
 			}
-			deduplicateGenerator := fakeRandomDeduplicateBidBooleanGenerator{returnValue: tt.dedupeGeneratorValue}
+			deduplicateGenerator := fakeBooleanGenerator{value: tt.dedupeGeneratorValue}
 			bidCategory, adapterBids, rejections, err := applyCategoryMapping(nil, *requestExt.Prebid.Targeting, adapterBids, categoriesFetcher, targData, &deduplicateGenerator, &nonBids{})
 
 			assert.Nil(t, err)
@@ -2885,11 +2889,11 @@ func TestNoCategoryDedupe(t *testing.T) {
 	bid4 := openrtb2.Bid{ID: "bid_id4", ImpID: "imp_id4", Price: 20.0000, Cat: cats4, W: 1, H: 1}
 	bid5 := openrtb2.Bid{ID: "bid_id5", ImpID: "imp_id5", Price: 10.0000, Cat: cats1, W: 1, H: 1}
 
-	bid1_1 := entities.PbsOrtbBid{&bid1, nil, "video", nil, &openrtb_ext.ExtBidPrebidVideo{Duration: 30}, nil, nil, 0, false, "", 14.0000, "USD", ""}
-	bid1_2 := entities.PbsOrtbBid{&bid2, nil, "video", nil, &openrtb_ext.ExtBidPrebidVideo{Duration: 30}, nil, nil, 0, false, "", 14.0000, "USD", ""}
-	bid1_3 := entities.PbsOrtbBid{&bid3, nil, "video", nil, &openrtb_ext.ExtBidPrebidVideo{Duration: 30}, nil, nil, 0, false, "", 20.0000, "USD", ""}
-	bid1_4 := entities.PbsOrtbBid{&bid4, nil, "video", nil, &openrtb_ext.ExtBidPrebidVideo{Duration: 30}, nil, nil, 0, false, "", 20.0000, "USD", ""}
-	bid1_5 := entities.PbsOrtbBid{&bid5, nil, "video", nil, &openrtb_ext.ExtBidPrebidVideo{Duration: 30}, nil, nil, 0, false, "", 10.0000, "USD", ""}
+	bid1_1 := entities.PbsOrtbBid{Bid: &bid1, BidMeta: nil, BidType: "video", BidTargets: nil, BidVideo: &openrtb_ext.ExtBidPrebidVideo{Duration: 30}, BidEvents: nil, BidFloors: nil, DealPriority: 0, DealTierSatisfied: false, GeneratedBidID: "", OriginalBidCPM: 14.0000, OriginalBidCur: "USD", TargetBidderCode: ""}
+	bid1_2 := entities.PbsOrtbBid{Bid: &bid2, BidMeta: nil, BidType: "video", BidTargets: nil, BidVideo: &openrtb_ext.ExtBidPrebidVideo{Duration: 30}, BidEvents: nil, BidFloors: nil, DealPriority: 0, DealTierSatisfied: false, GeneratedBidID: "", OriginalBidCPM: 14.0000, OriginalBidCur: "USD", TargetBidderCode: ""}
+	bid1_3 := entities.PbsOrtbBid{Bid: &bid3, BidMeta: nil, BidType: "video", BidTargets: nil, BidVideo: &openrtb_ext.ExtBidPrebidVideo{Duration: 30}, BidEvents: nil, BidFloors: nil, DealPriority: 0, DealTierSatisfied: false, GeneratedBidID: "", OriginalBidCPM: 20.0000, OriginalBidCur: "USD", TargetBidderCode: ""}
+	bid1_4 := entities.PbsOrtbBid{Bid: &bid4, BidMeta: nil, BidType: "video", BidTargets: nil, BidVideo: &openrtb_ext.ExtBidPrebidVideo{Duration: 30}, BidEvents: nil, BidFloors: nil, DealPriority: 0, DealTierSatisfied: false, GeneratedBidID: "", OriginalBidCPM: 20.0000, OriginalBidCur: "USD", TargetBidderCode: ""}
+	bid1_5 := entities.PbsOrtbBid{Bid: &bid5, BidMeta: nil, BidType: "video", BidTargets: nil, BidVideo: &openrtb_ext.ExtBidPrebidVideo{Duration: 30}, BidEvents: nil, BidFloors: nil, DealPriority: 0, DealTierSatisfied: false, GeneratedBidID: "", OriginalBidCPM: 10.0000, OriginalBidCur: "USD", TargetBidderCode: ""}
 
 	selectedBids := make(map[string]int)
 	expectedCategories := map[string]string{
@@ -2965,8 +2969,8 @@ func TestCategoryMappingBidderName(t *testing.T) {
 	bid1 := openrtb2.Bid{ID: "bid_id1", ImpID: "imp_id1", Price: 10.0000, Cat: cats1, W: 1, H: 1}
 	bid2 := openrtb2.Bid{ID: "bid_id2", ImpID: "imp_id2", Price: 10.0000, Cat: cats2, W: 1, H: 1}
 
-	bid1_1 := entities.PbsOrtbBid{&bid1, nil, "video", nil, &openrtb_ext.ExtBidPrebidVideo{Duration: 30}, nil, nil, 0, false, "", 10.0000, "USD", ""}
-	bid1_2 := entities.PbsOrtbBid{&bid2, nil, "video", nil, &openrtb_ext.ExtBidPrebidVideo{Duration: 30}, nil, nil, 0, false, "", 10.0000, "USD", ""}
+	bid1_1 := entities.PbsOrtbBid{Bid: &bid1, BidMeta: nil, BidType: "video", BidTargets: nil, BidVideo: &openrtb_ext.ExtBidPrebidVideo{Duration: 30}, BidEvents: nil, BidFloors: nil, DealPriority: 0, DealTierSatisfied: false, GeneratedBidID: "", OriginalBidCPM: 10.0000, OriginalBidCur: "USD", TargetBidderCode: ""}
+	bid1_2 := entities.PbsOrtbBid{Bid: &bid2, BidMeta: nil, BidType: "video", BidTargets: nil, BidVideo: &openrtb_ext.ExtBidPrebidVideo{Duration: 30}, BidEvents: nil, BidFloors: nil, DealPriority: 0, DealTierSatisfied: false, GeneratedBidID: "", OriginalBidCPM: 10.0000, OriginalBidCur: "USD", TargetBidderCode: ""}
 
 	innerBids1 := []*entities.PbsOrtbBid{
 		&bid1_1,
@@ -3019,8 +3023,8 @@ func TestCategoryMappingBidderNameNoCategories(t *testing.T) {
 	bid1 := openrtb2.Bid{ID: "bid_id1", ImpID: "imp_id1", Price: 10.0000, Cat: cats1, W: 1, H: 1}
 	bid2 := openrtb2.Bid{ID: "bid_id2", ImpID: "imp_id2", Price: 12.0000, Cat: cats2, W: 1, H: 1}
 
-	bid1_1 := entities.PbsOrtbBid{&bid1, nil, "video", nil, &openrtb_ext.ExtBidPrebidVideo{Duration: 17}, nil, nil, 0, false, "", 10.0000, "USD", ""}
-	bid1_2 := entities.PbsOrtbBid{&bid2, nil, "video", nil, &openrtb_ext.ExtBidPrebidVideo{Duration: 8}, nil, nil, 0, false, "", 12.0000, "USD", ""}
+	bid1_1 := entities.PbsOrtbBid{Bid: &bid1, BidMeta: nil, BidType: "video", BidTargets: nil, BidVideo: &openrtb_ext.ExtBidPrebidVideo{Duration: 17}, BidEvents: nil, BidFloors: nil, DealPriority: 0, DealTierSatisfied: false, GeneratedBidID: "", OriginalBidCPM: 10.0000, OriginalBidCur: "USD", TargetBidderCode: ""}
+	bid1_2 := entities.PbsOrtbBid{Bid: &bid2, BidMeta: nil, BidType: "video", BidTargets: nil, BidVideo: &openrtb_ext.ExtBidPrebidVideo{Duration: 8}, BidEvents: nil, BidFloors: nil, DealPriority: 0, DealTierSatisfied: false, GeneratedBidID: "", OriginalBidCPM: 12.0000, OriginalBidCur: "USD", TargetBidderCode: ""}
 
 	innerBids1 := []*entities.PbsOrtbBid{
 		&bid1_1,
@@ -3131,7 +3135,7 @@ func TestBidRejectionErrors(t *testing.T) {
 		innerBids := []*entities.PbsOrtbBid{}
 		for _, bid := range test.bids {
 			currentBid := entities.PbsOrtbBid{
-				bid, nil, "video", nil, &openrtb_ext.ExtBidPrebidVideo{Duration: test.duration}, nil, nil, 0, false, "", 10.0000, "USD", ""}
+				Bid: bid, BidMeta: nil, BidType: "video", BidTargets: nil, BidVideo: &openrtb_ext.ExtBidPrebidVideo{Duration: test.duration}, BidEvents: nil, BidFloors: nil, DealPriority: 0, DealTierSatisfied: false, GeneratedBidID: "", OriginalBidCPM: 10.0000, OriginalBidCur: "USD", TargetBidderCode: ""}
 			innerBids = append(innerBids, &currentBid)
 		}
 
@@ -3179,8 +3183,8 @@ func TestCategoryMappingTwoBiddersOneBidEachNoCategorySamePrice(t *testing.T) {
 	bidApn1 := openrtb2.Bid{ID: "bid_idApn1", ImpID: "imp_idApn1", Price: 10.0000, Cat: cats1, W: 1, H: 1}
 	bidApn2 := openrtb2.Bid{ID: "bid_idApn2", ImpID: "imp_idApn2", Price: 10.0000, Cat: cats2, W: 1, H: 1}
 
-	bid1_Apn1 := entities.PbsOrtbBid{&bidApn1, nil, "video", nil, &openrtb_ext.ExtBidPrebidVideo{Duration: 30}, nil, nil, 0, false, "", 10.0000, "USD", ""}
-	bid1_Apn2 := entities.PbsOrtbBid{&bidApn2, nil, "video", nil, &openrtb_ext.ExtBidPrebidVideo{Duration: 30}, nil, nil, 0, false, "", 10.0000, "USD", ""}
+	bid1_Apn1 := entities.PbsOrtbBid{Bid: &bidApn1, BidMeta: nil, BidType: "video", BidTargets: nil, BidVideo: &openrtb_ext.ExtBidPrebidVideo{Duration: 30}, BidEvents: nil, BidFloors: nil, DealPriority: 0, DealTierSatisfied: false, GeneratedBidID: "", OriginalBidCPM: 10.0000, OriginalBidCur: "USD", TargetBidderCode: ""}
+	bid1_Apn2 := entities.PbsOrtbBid{Bid: &bidApn2, BidMeta: nil, BidType: "video", BidTargets: nil, BidVideo: &openrtb_ext.ExtBidPrebidVideo{Duration: 30}, BidEvents: nil, BidFloors: nil, DealPriority: 0, DealTierSatisfied: false, GeneratedBidID: "", OriginalBidCPM: 10.0000, OriginalBidCur: "USD", TargetBidderCode: ""}
 
 	innerBidsApn1 := []*entities.PbsOrtbBid{
 		&bid1_Apn1,
@@ -3259,11 +3263,11 @@ func TestCategoryMappingTwoBiddersManyBidsEachNoCategorySamePrice(t *testing.T)
 	bidApn2_1 := openrtb2.Bid{ID: "bid_idApn2_1", ImpID: "imp_idApn2_1", Price: 10.0000, Cat: cats2, W: 1, H: 1}
 	bidApn2_2 := openrtb2.Bid{ID: "bid_idApn2_2", ImpID: "imp_idApn2_2", Price: 20.0000, Cat: cats2, W: 1, H: 1}
 
-	bid1_Apn1_1 := entities.PbsOrtbBid{&bidApn1_1, nil, "video", nil, &openrtb_ext.ExtBidPrebidVideo{Duration: 30}, nil, nil, 0, false, "", 10.0000, "USD", ""}
-	bid1_Apn1_2 := entities.PbsOrtbBid{&bidApn1_2, nil, "video", nil, &openrtb_ext.ExtBidPrebidVideo{Duration: 30}, nil, nil, 0, false, "", 20.0000, "USD", ""}
+	bid1_Apn1_1 := entities.PbsOrtbBid{Bid: &bidApn1_1, BidMeta: nil, BidType: "video", BidTargets: nil, BidVideo: &openrtb_ext.ExtBidPrebidVideo{Duration: 30}, BidEvents: nil, BidFloors: nil, DealPriority: 0, DealTierSatisfied: false, GeneratedBidID: "", OriginalBidCPM: 10.0000, OriginalBidCur: "USD", TargetBidderCode: ""}
+	bid1_Apn1_2 := entities.PbsOrtbBid{Bid: &bidApn1_2, BidMeta: nil, BidType: "video", BidTargets: nil, BidVideo: &openrtb_ext.ExtBidPrebidVideo{Duration: 30}, BidEvents: nil, BidFloors: nil, DealPriority: 0, DealTierSatisfied: false, GeneratedBidID: "", OriginalBidCPM: 20.0000, OriginalBidCur: "USD", TargetBidderCode: ""}
 
-	bid1_Apn2_1 := entities.PbsOrtbBid{&bidApn2_1, nil, "video", nil, &openrtb_ext.ExtBidPrebidVideo{Duration: 30}, nil, nil, 0, false, "", 10.0000, "USD", ""}
-	bid1_Apn2_2 := entities.PbsOrtbBid{&bidApn2_2, nil, "video", nil, &openrtb_ext.ExtBidPrebidVideo{Duration: 30}, nil, nil, 0, false, "", 20.0000, "USD", ""}
+	bid1_Apn2_1 := entities.PbsOrtbBid{Bid: &bidApn2_1, BidMeta: nil, BidType: "video", BidTargets: nil, BidVideo: &openrtb_ext.ExtBidPrebidVideo{Duration: 30}, BidEvents: nil, BidFloors: nil, DealPriority: 0, DealTierSatisfied: false, GeneratedBidID: "", OriginalBidCPM: 10.0000, OriginalBidCur: "USD", TargetBidderCode: ""}
+	bid1_Apn2_2 := entities.PbsOrtbBid{Bid: &bidApn2_2, BidMeta: nil, BidType: "video", BidTargets: nil, BidVideo: &openrtb_ext.ExtBidPrebidVideo{Duration: 30}, BidEvents: nil, BidFloors: nil, DealPriority: 0, DealTierSatisfied: false, GeneratedBidID: "", OriginalBidCPM: 20.0000, OriginalBidCur: "USD", TargetBidderCode: ""}
 
 	innerBidsApn1 := []*entities.PbsOrtbBid{
 		&bid1_Apn1_1,
@@ -3286,7 +3290,7 @@ func TestCategoryMappingTwoBiddersManyBidsEachNoCategorySamePrice(t *testing.T)
 	adapterBids[bidderNameApn1] = &seatBidApn1
 	adapterBids[bidderNameApn2] = &seatBidApn2
 
-	_, adapterBids, rejections, err := applyCategoryMapping(nil, *requestExt.Prebid.Targeting, adapterBids, categoriesFetcher, targData, &fakeRandomDeduplicateBidBooleanGenerator{true}, &nonBids{})
+	_, adapterBids, rejections, err := applyCategoryMapping(nil, *requestExt.Prebid.Targeting, adapterBids, categoriesFetcher, targData, &fakeBooleanGenerator{value: true}, &nonBids{})
 
 	assert.NoError(t, err, "Category mapping error should be empty")
 
@@ -3341,9 +3345,9 @@ func TestRemoveBidById(t *testing.T) {
 	bidApn1_2 := openrtb2.Bid{ID: "bid_idApn1_2", ImpID: "imp_idApn1_2", Price: 20.0000, Cat: cats1, W: 1, H: 1}
 	bidApn1_3 := openrtb2.Bid{ID: "bid_idApn1_3", ImpID: "imp_idApn1_3", Price: 10.0000, Cat: cats1, W: 1, H: 1}
 
-	bid1_Apn1_1 := entities.PbsOrtbBid{&bidApn1_1, nil, "video", nil, &openrtb_ext.ExtBidPrebidVideo{Duration: 30}, nil, nil, 0, false, "", 10.0000, "USD", ""}
-	bid1_Apn1_2 := entities.PbsOrtbBid{&bidApn1_2, nil, "video", nil, &openrtb_ext.ExtBidPrebidVideo{Duration: 30}, nil, nil, 0, false, "", 20.0000, "USD", ""}
-	bid1_Apn1_3 := entities.PbsOrtbBid{&bidApn1_3, nil, "video", nil, &openrtb_ext.ExtBidPrebidVideo{Duration: 30}, nil, nil, 0, false, "", 10.0000, "USD", ""}
+	bid1_Apn1_1 := entities.PbsOrtbBid{Bid: &bidApn1_1, BidMeta: nil, BidType: "video", BidTargets: nil, BidVideo: &openrtb_ext.ExtBidPrebidVideo{Duration: 30}, BidEvents: nil, BidFloors: nil, DealPriority: 0, DealTierSatisfied: false, GeneratedBidID: "", OriginalBidCPM: 10.0000, OriginalBidCur: "USD", TargetBidderCode: ""}
+	bid1_Apn1_2 := entities.PbsOrtbBid{Bid: &bidApn1_2, BidMeta: nil, BidType: "video", BidTargets: nil, BidVideo: &openrtb_ext.ExtBidPrebidVideo{Duration: 30}, BidEvents: nil, BidFloors: nil, DealPriority: 0, DealTierSatisfied: false, GeneratedBidID: "", OriginalBidCPM: 20.0000, OriginalBidCur: "USD", TargetBidderCode: ""}
+	bid1_Apn1_3 := entities.PbsOrtbBid{Bid: &bidApn1_3, BidMeta: nil, BidType: "video", BidTargets: nil, BidVideo: &openrtb_ext.ExtBidPrebidVideo{Duration: 30}, BidEvents: nil, BidFloors: nil, DealPriority: 0, DealTierSatisfied: false, GeneratedBidID: "", OriginalBidCPM: 10.0000, OriginalBidCur: "USD", TargetBidderCode: ""}
 
 	type aTest struct {
 		desc      string
@@ -3544,7 +3548,7 @@ func TestApplyDealSupport(t *testing.T) {
 			},
 		}
 
-		bid := entities.PbsOrtbBid{&openrtb2.Bid{ID: "123456"}, nil, "video", map[string]string{}, &openrtb_ext.ExtBidPrebidVideo{}, nil, nil, test.in.dealPriority, false, "", 0, "USD", ""}
+		bid := entities.PbsOrtbBid{Bid: &openrtb2.Bid{ID: "123456"}, BidMeta: nil, BidType: "video", BidTargets: map[string]string{}, BidVideo: &openrtb_ext.ExtBidPrebidVideo{}, BidEvents: nil, BidFloors: nil, DealPriority: test.in.dealPriority, DealTierSatisfied: false, GeneratedBidID: "", OriginalBidCPM: 0, OriginalBidCur: "USD", TargetBidderCode: ""}
 		bidCategory := map[string]string{
 			bid.Bid.ID: test.in.targ["hb_pb_cat_dur"],
 		}
@@ -3604,8 +3608,8 @@ func TestApplyDealSupportMultiBid(t *testing.T) {
 					allBidsByBidder: map[string]map[openrtb_ext.BidderName][]*entities.PbsOrtbBid{
 						"imp_id1": {
 							openrtb_ext.BidderName("appnexus"): {
-								&entities.PbsOrtbBid{&openrtb2.Bid{ID: "123456"}, nil, "video", map[string]string{}, &openrtb_ext.ExtBidPrebidVideo{}, nil, nil, 5, false, "", 0, "USD", ""},
-								&entities.PbsOrtbBid{&openrtb2.Bid{ID: "789101"}, nil, "video", map[string]string{}, &openrtb_ext.ExtBidPrebidVideo{}, nil, nil, 5, false, "", 0, "USD", ""},
+								&entities.PbsOrtbBid{Bid: &openrtb2.Bid{ID: "123456"}, BidMeta: nil, BidType: "video", BidTargets: map[string]string{}, BidVideo: &openrtb_ext.ExtBidPrebidVideo{}, BidEvents: nil, BidFloors: nil, DealPriority: 5, DealTierSatisfied: false, GeneratedBidID: "", OriginalBidCPM: 0, OriginalBidCur: "USD", TargetBidderCode: ""},
+								&entities.PbsOrtbBid{Bid: &openrtb2.Bid{ID: "789101"}, BidMeta: nil, BidType: "video", BidTargets: map[string]string{}, BidVideo: &openrtb_ext.ExtBidPrebidVideo{}, BidEvents: nil, BidFloors: nil, DealPriority: 5, DealTierSatisfied: false, GeneratedBidID: "", OriginalBidCPM: 0, OriginalBidCur: "USD", TargetBidderCode: ""},
 							},
 						},
 					},
@@ -3650,8 +3654,8 @@ func TestApplyDealSupportMultiBid(t *testing.T) {
 					allBidsByBidder: map[string]map[openrtb_ext.BidderName][]*entities.PbsOrtbBid{
 						"imp_id1": {
 							openrtb_ext.BidderName("appnexus"): {
-								&entities.PbsOrtbBid{&openrtb2.Bid{ID: "123456"}, nil, "video", map[string]string{}, &openrtb_ext.ExtBidPrebidVideo{}, nil, nil, 5, false, "", 0, "USD", ""},
-								&entities.PbsOrtbBid{&openrtb2.Bid{ID: "789101"}, nil, "video", map[string]string{}, &openrtb_ext.ExtBidPrebidVideo{}, nil, nil, 5, false, "", 0, "USD", ""},
+								&entities.PbsOrtbBid{Bid: &openrtb2.Bid{ID: "123456"}, BidMeta: nil, BidType: "video", BidTargets: map[string]string{}, BidVideo: &openrtb_ext.ExtBidPrebidVideo{}, BidEvents: nil, BidFloors: nil, DealPriority: 5, DealTierSatisfied: false, GeneratedBidID: "", OriginalBidCPM: 0, OriginalBidCur: "USD", TargetBidderCode: ""},
+								&entities.PbsOrtbBid{Bid: &openrtb2.Bid{ID: "789101"}, BidMeta: nil, BidType: "video", BidTargets: map[string]string{}, BidVideo: &openrtb_ext.ExtBidPrebidVideo{}, BidEvents: nil, BidFloors: nil, DealPriority: 5, DealTierSatisfied: false, GeneratedBidID: "", OriginalBidCPM: 0, OriginalBidCur: "USD", TargetBidderCode: ""},
 							},
 						},
 					},
@@ -3701,8 +3705,8 @@ func TestApplyDealSupportMultiBid(t *testing.T) {
 					allBidsByBidder: map[string]map[openrtb_ext.BidderName][]*entities.PbsOrtbBid{
 						"imp_id1": {
 							openrtb_ext.BidderName("appnexus"): {
-								&entities.PbsOrtbBid{&openrtb2.Bid{ID: "123456"}, nil, "video", map[string]string{}, &openrtb_ext.ExtBidPrebidVideo{}, nil, nil, 5, false, "", 0, "USD", ""},
-								&entities.PbsOrtbBid{&openrtb2.Bid{ID: "789101"}, nil, "video", map[string]string{}, &openrtb_ext.ExtBidPrebidVideo{}, nil, nil, 5, false, "", 0, "USD", ""},
+								&entities.PbsOrtbBid{Bid: &openrtb2.Bid{ID: "123456"}, BidMeta: nil, BidType: "video", BidTargets: map[string]string{}, BidVideo: &openrtb_ext.ExtBidPrebidVideo{}, BidEvents: nil, BidFloors: nil, DealPriority: 5, DealTierSatisfied: false, GeneratedBidID: "", OriginalBidCPM: 0, OriginalBidCur: "USD", TargetBidderCode: ""},
+								&entities.PbsOrtbBid{Bid: &openrtb2.Bid{ID: "789101"}, BidMeta: nil, BidType: "video", BidTargets: map[string]string{}, BidVideo: &openrtb_ext.ExtBidPrebidVideo{}, BidEvents: nil, BidFloors: nil, DealPriority: 5, DealTierSatisfied: false, GeneratedBidID: "", OriginalBidCPM: 0, OriginalBidCur: "USD", TargetBidderCode: ""},
 							},
 						},
 					},
@@ -3893,7 +3897,7 @@ func TestUpdateHbPbCatDur(t *testing.T) {
 	}
 
 	for _, test := range testCases {
-		bid := entities.PbsOrtbBid{&openrtb2.Bid{ID: "123456"}, nil, "video", map[string]string{}, &openrtb_ext.ExtBidPrebidVideo{}, nil, nil, test.dealPriority, false, "", 0, "USD", ""}
+		bid := entities.PbsOrtbBid{Bid: &openrtb2.Bid{ID: "123456"}, BidMeta: nil, BidType: "video", BidTargets: map[string]string{}, BidVideo: &openrtb_ext.ExtBidPrebidVideo{}, BidEvents: nil, BidFloors: nil, DealPriority: test.dealPriority, DealTierSatisfied: false, GeneratedBidID: "", OriginalBidCPM: 0, OriginalBidCur: "USD", TargetBidderCode: ""}
 		bidCategory := map[string]string{
 			bid.Bid.ID: test.targ["hb_pb_cat_dur"],
 		}
@@ -4110,7 +4114,7 @@ func TestStoredAuctionResponses(t *testing.T) {
 	e.cache = &wellBehavedCache{}
 	e.me = &metricsConf.NilMetricsEngine{}
 	e.categoriesFetcher = categoriesFetcher
-	e.bidIDGenerator = &mockBidIDGenerator{false, false}
+	e.bidIDGenerator = &fakeBidIDGenerator{GenerateBidID: false, ReturnError: false}
 	e.currencyConverter = currency.NewRateConverter(&http.Client{}, "", time.Duration(0))
 	e.gdprPermsBuilder = fakePermissionsBuilder{
 		permissions: &permissionsMock{
@@ -4476,7 +4480,7 @@ func TestAuctionDebugEnabled(t *testing.T) {
 	e.cache = &wellBehavedCache{}
 	e.me = &metricsConf.NilMetricsEngine{}
 	e.categoriesFetcher = categoriesFetcher
-	e.bidIDGenerator = &mockBidIDGenerator{false, false}
+	e.bidIDGenerator = &fakeBidIDGenerator{GenerateBidID: false, ReturnError: false}
 	e.currencyConverter = currency.NewRateConverter(&http.Client{}, "", time.Duration(0))
 	e.gdprPermsBuilder = fakePermissionsBuilder{
 		permissions: &permissionsMock{
@@ -4758,7 +4762,7 @@ func TestMakeBidWithValidation(t *testing.T) {
 			name:               "One_of_two_bids_is_invalid_based_on_DSA_object_presence",
 			givenBidRequestExt: json.RawMessage(`{"dsa": {"dsarequired": 2}}`),
 			givenValidations:   config.Validations{},
-			givenBids:          []*entities.PbsOrtbBid{{Bid: &openrtb2.Bid{Ext: json.RawMessage(`{"dsa": {}}`)}}, {Bid: &openrtb2.Bid{}}},
+			givenBids:          []*entities.PbsOrtbBid{{Bid: &openrtb2.Bid{Ext: json.RawMessage(`{"dsa": {"adrender":1}}`)}}, {Bid: &openrtb2.Bid{}}},
 			givenSeat:          "pubmatic",
 			expectedNumOfBids:  1,
 			expectedNonBids: &nonBids{
@@ -5077,7 +5081,7 @@ func TestOverrideConfigAlternateBidderCodesWithRequestValues(t *testing.T) {
 	}.Builder
 	e.currencyConverter = currency.NewRateConverter(&http.Client{}, "", time.Duration(0))
 	e.categoriesFetcher = categoriesFetcher
-	e.bidIDGenerator = &mockBidIDGenerator{false, false}
+	e.bidIDGenerator = &fakeBidIDGenerator{GenerateBidID: false, ReturnError: false}
 	e.requestSplitter = requestSplitter{
 		me:               e.me,
 		gdprPermsBuilder: e.gdprPermsBuilder,
@@ -5474,7 +5478,7 @@ type exchangeSpec struct {
 	DebugLog                   *DebugLog              `json:"debuglog,omitempty"`
 	EventsEnabled              bool                   `json:"events_enabled,omitempty"`
 	StartTime                  int64                  `json:"start_time_ms,omitempty"`
-	BidIDGenerator             *mockBidIDGenerator    `json:"bidIDGenerator,omitempty"`
+	BidIDGenerator             *fakeBidIDGenerator    `json:"bidIDGenerator,omitempty"`
 	RequestType                *metrics.RequestType   `json:"requestType,omitempty"`
 	PassthroughFlag            bool                   `json:"passthrough_flag,omitempty"`
 	HostSChainFlag             bool                   `json:"host_schain_flag,omitempty"`
diff --git a/exchange/exchangetest/bid-id-invalid.json b/exchange/exchangetest/bid-id-invalid.json
deleted file mode 100644
index 9b3c5eee..00000000
--- a/exchange/exchangetest/bid-id-invalid.json
+++ /dev/null
@@ -1,200 +0,0 @@
-{
-    "bidIDGenerator": {
-        "generateBidID": true,
-        "returnError": true
-    },
-    "incomingRequest": {
-        "ortbRequest": {
-            "id": "some-request-id",
-            "site": {
-                "page": "test.somepage.com"
-            },
-            "imp": [
-                {
-                    "id": "my-imp-id",
-                    "video": {
-                        "mimes": [
-                            "video/mp4"
-                        ]
-                    },
-                    "ext": {
-                        "prebid": {
-                            "bidder": {
-                                "appnexus": {
-                                    "placementId": 1
-                                }
-                            }
-                        }
-                    }
-                }
-            ],
-            "test": 1,
-            "ext": {
-                "prebid": {
-                    "targeting": {
-                        "includebrandcategory": {
-                            "primaryadserver": 1,
-                            "publisher": "",
-                            "withcategory": true
-                        },
-                        "pricegranularity": {
-                            "precision": 2,
-                            "ranges": [
-                                {
-                                    "min": 0,
-                                    "max": 20,
-                                    "increment": 0.1
-                                }
-                            ]
-                        },
-                        "includewinners": true,
-                        "includebidderkeys": true,
-                        "appendbiddernames": true
-                    }
-                }
-            }
-        },
-        "usersyncs": {
-            "appnexus": "123"
-        }
-    },
-    "outgoingRequests": {
-        "appnexus": {
-            "mockResponse": {
-                "pbsSeatBids": [
-                    {
-                        "pbsBids": [
-                            {
-                                "ortbBid": {
-                                    "id": "apn-bid",
-                                    "impid": "my-imp-id",
-                                    "price": 0.3,
-                                    "w": 200,
-                                    "h": 250,
-                                    "crid": "creative-1",
-                                    "cat": [
-                                        "IAB1-1"
-                                    ]
-                                },
-                                "bidType": "video",
-                                "bidVideo": {
-                                    "duration": 30,
-                                    "PrimaryCategory": ""
-                                }
-                            }
-                        ],
-                        "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",
-                            "cat": [
-                                "IAB1-1"
-                            ],
-                            "ext": {
-                                "origbidcpm": 0.3,
-                                "prebid": {
-                                    "meta": {
-                                        "adaptercode":"appnexus"
-                                    },
-                                    "type": "video",
-                                    "targeting": {
-                                        "hb_bidder": "appnexus",
-                                        "hb_bidder_appnexus": "appnexus",
-                                        "hb_cache_host": "www.pbcserver.com",
-                                        "hb_cache_host_appnex": "www.pbcserver.com",
-                                        "hb_cache_path": "/pbcache/endpoint",
-                                        "hb_cache_path_appnex": "/pbcache/endpoint",
-                                        "hb_pb": "0.20",
-                                        "hb_pb_appnexus": "0.20",
-                                        "hb_pb_cat_dur": "0.20_VideoGames_0s_appnexus",
-                                        "hb_pb_cat_dur_appnex": "0.20_VideoGames_0s_appnexus",
-                                        "hb_size": "200x250",
-                                        "hb_size_appnexus": "200x250"
-                                    }
-                                }
-                            }
-                        }
-                    ]
-                }
-            ]
-        },
-        "ext": {
-            "debug": {
-                "resolvedrequest": {
-                    "id": "some-request-id",
-                    "imp": [
-                        {
-                            "id": "my-imp-id",
-                            "video": {
-                                "mimes": [
-                                    "video/mp4"
-                                ]
-                            },
-                            "ext": {
-                                "prebid": {
-                                    "bidder": {
-                                        "appnexus": {
-                                            "placementId": 1
-                                        }
-                                    }
-                                }
-                            }
-                        }
-                    ],
-                    "site": {
-                        "page": "test.somepage.com"
-                    },
-                    "test": 1,
-                    "ext": {
-                        "prebid": {
-                            "targeting": {
-                                "includebrandcategory": {
-                                    "primaryadserver": 1,
-                                    "publisher": "",
-                                    "withcategory": true
-                                },
-                                "pricegranularity": {
-                                    "precision": 2,
-                                    "ranges": [
-                                        {
-                                            "min": 0,
-                                            "max": 20,
-                                            "increment": 0.1
-                                        }
-                                    ]
-                                },
-                                "includewinners": true,
-                                "includebidderkeys": true,
-                                "appendbiddernames": true
-                            }
-                        }
-                    }
-                }
-            },
-            "errors": {
-                "prebid": [
-                    {
-                        "code": 999,
-                        "message": "Error generating bid.ext.prebid.bidid"
-                    }
-                ]
-            }
-        }
-    }
-}
\ No newline at end of file
diff --git a/exchange/exchangetest/bid-id-valid.json b/exchange/exchangetest/bid-id-valid.json
deleted file mode 100644
index caa02384..00000000
--- a/exchange/exchangetest/bid-id-valid.json
+++ /dev/null
@@ -1,193 +0,0 @@
-{
-    "bidIDGenerator": {
-        "generateBidID": true,
-        "returnError": false
-    },
-    "incomingRequest": {
-        "ortbRequest": {
-            "id": "some-request-id",
-            "site": {
-                "page": "test.somepage.com"
-            },
-            "imp": [
-                {
-                    "id": "my-imp-id",
-                    "video": {
-                        "mimes": [
-                            "video/mp4"
-                        ]
-                    },
-                    "ext": {
-                        "prebid": {
-                            "bidder": {
-                                "appnexus": {
-                                    "placementId": 1
-                                }
-                            }
-                        }
-                    }
-                }
-            ],
-            "test": 1,
-            "ext": {
-                "prebid": {
-                    "targeting": {
-                        "includebrandcategory": {
-                            "primaryadserver": 1,
-                            "publisher": "",
-                            "withcategory": true
-                        },
-                        "pricegranularity": {
-                            "precision": 2,
-                            "ranges": [
-                                {
-                                    "min": 0,
-                                    "max": 20,
-                                    "increment": 0.1
-                                }
-                            ]
-                        },
-                        "includewinners": true,
-                        "includebidderkeys": true,
-                        "appendbiddernames": true
-                    }
-                }
-            }
-        },
-        "usersyncs": {
-            "appnexus": "123"
-        }
-    },
-    "outgoingRequests": {
-        "appnexus": {
-            "mockResponse": {
-                "pbsSeatBids": [
-                    {
-                        "pbsBids": [
-                            {
-                                "ortbBid": {
-                                    "id": "apn-bid",
-                                    "impid": "my-imp-id",
-                                    "price": 0.3,
-                                    "w": 200,
-                                    "h": 250,
-                                    "crid": "creative-1",
-                                    "cat": [
-                                        "IAB1-1"
-                                    ]
-                                },
-                                "bidType": "video",
-                                "bidVideo": {
-                                    "duration": 30,
-                                    "PrimaryCategory": ""
-                                }
-                            }
-                        ],
-                        "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",
-                            "cat": [
-                                "IAB1-1"
-                            ],
-                            "ext": {
-                                "origbidcpm": 0.3,
-                                "prebid": {
-                                    "meta": {
-                                        "adaptercode":"appnexus"
-                                    },
-                                    "bidid": "mock_uuid",
-                                    "type": "video",
-                                    "targeting": {
-                                        "hb_bidder": "appnexus",
-                                        "hb_bidder_appnexus": "appnexus",
-                                        "hb_cache_host": "www.pbcserver.com",
-                                        "hb_cache_host_appnex": "www.pbcserver.com",
-                                        "hb_cache_path": "/pbcache/endpoint",
-                                        "hb_cache_path_appnex": "/pbcache/endpoint",
-                                        "hb_pb": "0.20",
-                                        "hb_pb_appnexus": "0.20",
-                                        "hb_pb_cat_dur": "0.20_VideoGames_0s_appnexus",
-                                        "hb_pb_cat_dur_appnex": "0.20_VideoGames_0s_appnexus",
-                                        "hb_size": "200x250",
-                                        "hb_size_appnexus": "200x250"
-                                    }
-                                }
-                            }
-                        }
-                    ]
-                }
-            ]
-        },
-        "ext": {
-            "debug": {
-                "resolvedrequest": {
-                    "id": "some-request-id",
-                    "imp": [
-                        {
-                            "id": "my-imp-id",
-                            "video": {
-                                "mimes": [
-                                    "video/mp4"
-                                ]
-                            },
-                            "ext": {
-                                "prebid": {
-                                    "bidder": {
-                                        "appnexus": {
-                                            "placementId": 1
-                                        }
-                                    }
-                                }
-                            }
-                        }
-                    ],
-                    "site": {
-                        "page": "test.somepage.com"
-                    },
-                    "test": 1,
-                    "ext": {
-                        "prebid": {
-                            "targeting": {
-                                "pricegranularity": {
-                                    "precision": 2,
-                                    "ranges": [
-                                        {
-                                            "min": 0,
-                                            "max": 20,
-                                            "increment": 0.1
-                                        }
-                                    ]
-                                },
-                                "includewinners": true,
-                                "includebidderkeys": true,
-                                "includebrandcategory": {
-                                    "primaryadserver": 1,
-                                    "publisher": "",
-                                    "withcategory": true
-                                },
-                                "appendbiddernames": true
-                            }
-                        }
-                    }
-                }
-            }
-        }
-    }
-}
\ No newline at end of file
diff --git a/exchange/exchangetest/events-vast-account-off-request-on.json b/exchange/exchangetest/events-vast-account-off-request-on.json
index 1a5aea42..93ec6f35 100644
--- a/exchange/exchangetest/events-vast-account-off-request-on.json
+++ b/exchange/exchangetest/events-vast-account-off-request-on.json
@@ -134,7 +134,7 @@
                   "meta": {
                     "adaptercode": "audienceNetwork"
                   },
-                  "bidid": "mock_uuid",
+                  "bidid": "bid-audienceNetwork-1",
                   "type": "video"
                 }
               }
@@ -146,7 +146,7 @@
           "bid": [
             {
               "id": "winning-bid",
-              "adm": "<VAST version=\"3.0\"><Ad><Wrapper><AdSystem>prebid.org wrapper</AdSystem><VASTAdTagURI><![CDATA[http://domain.com/winning-bid]]></VASTAdTagURI><Impression><![CDATA[http://localhost/event?t=imp&b=mock_uuid&a=testaccount&bidder=appnexus&f=b&int=testIntegrationType&ts=1234567890]]></Impression><Creatives></Creatives></Wrapper></Ad></VAST>",
+              "adm": "<VAST version=\"3.0\"><Ad><Wrapper><AdSystem>prebid.org wrapper</AdSystem><VASTAdTagURI><![CDATA[http://domain.com/winning-bid]]></VASTAdTagURI><Impression><![CDATA[http://localhost/event?t=imp&b=bid-appnexus-1&a=testaccount&bidder=appnexus&f=b&int=testIntegrationType&ts=1234567890]]></Impression><Creatives></Creatives></Wrapper></Ad></VAST>",
               "nurl": "http://domain.com/winning-bid",
               "impid": "my-imp-id",
               "price": 0.71,
@@ -156,10 +156,10 @@
               "ext": {
                 "origbidcpm": 0.71,
                 "prebid": {
-                  "bidid": "mock_uuid",
                   "meta": {
                     "adaptercode": "appnexus"
                   },
+                  "bidid": "bid-appnexus-1",
                   "type": "video",
                   "targeting": {
                     "hb_bidder": "appnexus",
@@ -174,7 +174,7 @@
             },
             {
               "id": "losing-bid",
-              "adm": "<VAST version=\"3.0\"><Ad><Wrapper><AdSystem>prebid.org wrapper</AdSystem><VASTAdTagURI><![CDATA[http://domain.com/losing-bid]]></VASTAdTagURI><Impression><![CDATA[http://localhost/event?t=imp&b=mock_uuid&a=testaccount&bidder=appnexus&f=b&int=testIntegrationType&ts=1234567890]]></Impression><Creatives></Creatives></Wrapper></Ad></VAST>",
+              "adm": "<VAST version=\"3.0\"><Ad><Wrapper><AdSystem>prebid.org wrapper</AdSystem><VASTAdTagURI><![CDATA[http://domain.com/losing-bid]]></VASTAdTagURI><Impression><![CDATA[http://localhost/event?t=imp&b=bid-appnexus-2&a=testaccount&bidder=appnexus&f=b&int=testIntegrationType&ts=1234567890]]></Impression><Creatives></Creatives></Wrapper></Ad></VAST>",
               "nurl": "http://domain.com/losing-bid",
               "impid": "my-imp-id",
               "price": 0.21,
@@ -187,7 +187,7 @@
                   "meta": {
                     "adaptercode": "appnexus"
                   },
-                  "bidid": "mock_uuid",
+                  "bidid": "bid-appnexus-2",
                   "type": "video"
                 }
               }
diff --git a/exchange/exchangetest/generate-bid-id-error.json b/exchange/exchangetest/generate-bid-id-error.json
new file mode 100644
index 00000000..9c13ef38
--- /dev/null
+++ b/exchange/exchangetest/generate-bid-id-error.json
@@ -0,0 +1,200 @@
+{
+  "bidIDGenerator": {
+    "generateBidID": true,
+    "returnError": true
+  },
+  "incomingRequest": {
+    "ortbRequest": {
+      "id": "some-request-id",
+      "site": {
+        "page": "test.somepage.com"
+      },
+      "imp": [
+        {
+          "id": "my-imp-id",
+          "video": {
+            "mimes": [
+              "video/mp4"
+            ]
+          },
+          "ext": {
+            "prebid": {
+              "bidder": {
+                "appnexus": {
+                  "placementId": 1
+                }
+              }
+            }
+          }
+        }
+      ],
+      "test": 1,
+      "ext": {
+        "prebid": {
+          "targeting": {
+            "includebrandcategory": {
+              "primaryadserver": 1,
+              "publisher": "",
+              "withcategory": true
+            },
+            "pricegranularity": {
+              "precision": 2,
+              "ranges": [
+                {
+                  "min": 0,
+                  "max": 20,
+                  "increment": 0.1
+                }
+              ]
+            },
+            "includewinners": true,
+            "includebidderkeys": true,
+            "appendbiddernames": true
+          }
+        }
+      }
+    },
+    "usersyncs": {
+      "appnexus": "123"
+    }
+  },
+  "outgoingRequests": {
+    "appnexus": {
+      "mockResponse": {
+        "pbsSeatBids": [
+          {
+            "pbsBids": [
+              {
+                "ortbBid": {
+                  "id": "apn-bid",
+                  "impid": "my-imp-id",
+                  "price": 0.3,
+                  "w": 200,
+                  "h": 250,
+                  "crid": "creative-1",
+                  "cat": [
+                    "IAB1-1"
+                  ]
+                },
+                "bidType": "video",
+                "bidVideo": {
+                  "duration": 30,
+                  "PrimaryCategory": ""
+                }
+              }
+            ],
+            "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",
+              "cat": [
+                "IAB1-1"
+              ],
+              "ext": {
+                "origbidcpm": 0.3,
+                "prebid": {
+                  "meta": {
+                    "adaptercode": "appnexus"
+                  },
+                  "type": "video",
+                  "targeting": {
+                    "hb_bidder": "appnexus",
+                    "hb_bidder_appnexus": "appnexus",
+                    "hb_cache_host": "www.pbcserver.com",
+                    "hb_cache_host_appnex": "www.pbcserver.com",
+                    "hb_cache_path": "/pbcache/endpoint",
+                    "hb_cache_path_appnex": "/pbcache/endpoint",
+                    "hb_pb": "0.20",
+                    "hb_pb_appnexus": "0.20",
+                    "hb_pb_cat_dur": "0.20_VideoGames_0s_appnexus",
+                    "hb_pb_cat_dur_appnex": "0.20_VideoGames_0s_appnexus",
+                    "hb_size": "200x250",
+                    "hb_size_appnexus": "200x250"
+                  }
+                }
+              }
+            }
+          ]
+        }
+      ]
+    },
+    "ext": {
+      "debug": {
+        "resolvedrequest": {
+          "id": "some-request-id",
+          "imp": [
+            {
+              "id": "my-imp-id",
+              "video": {
+                "mimes": [
+                  "video/mp4"
+                ]
+              },
+              "ext": {
+                "prebid": {
+                  "bidder": {
+                    "appnexus": {
+                      "placementId": 1
+                    }
+                  }
+                }
+              }
+            }
+          ],
+          "site": {
+            "page": "test.somepage.com"
+          },
+          "test": 1,
+          "ext": {
+            "prebid": {
+              "targeting": {
+                "includebrandcategory": {
+                  "primaryadserver": 1,
+                  "publisher": "",
+                  "withcategory": true
+                },
+                "pricegranularity": {
+                  "precision": 2,
+                  "ranges": [
+                    {
+                      "min": 0,
+                      "max": 20,
+                      "increment": 0.1
+                    }
+                  ]
+                },
+                "includewinners": true,
+                "includebidderkeys": true,
+                "appendbiddernames": true
+              }
+            }
+          }
+        }
+      },
+      "errors": {
+        "prebid": [
+          {
+            "code": 999,
+            "message": "Error generating bid.ext.prebid.bidid"
+          }
+        ]
+      }
+    }
+  }
+}
\ No newline at end of file
diff --git a/exchange/exchangetest/generate-bid-id-many.json b/exchange/exchangetest/generate-bid-id-many.json
new file mode 100644
index 00000000..4c6a18ab
--- /dev/null
+++ b/exchange/exchangetest/generate-bid-id-many.json
@@ -0,0 +1,163 @@
+{
+  "bidIDGenerator": {
+    "generateBidID": true,
+    "returnError": false
+  },
+  "incomingRequest": {
+    "ortbRequest": {
+      "id": "some-request-id",
+      "site": {
+        "page": "test.somepage.com"
+      },
+      "imp": [
+        {
+          "id": "imp-id-1",
+          "video": {
+            "mimes": [
+              "video/mp4"
+            ]
+          },
+          "ext": {
+            "prebid": {
+              "bidder": {
+                "appnexus": {
+                  "placementId": 1
+                }
+              }
+            }
+          }
+        }
+      ]
+    }
+  },
+  "outgoingRequests": {
+    "appnexus": {
+      "mockResponse": {
+        "pbsSeatBids": [
+          {
+            "pbsBids": [
+              {
+                "ortbBid": {
+                  "id": "apn-bid-1",
+                  "impid": "imp-id-1",
+                  "price": 0.3,
+                  "w": 200,
+                  "h": 250,
+                  "crid": "creative-1",
+                  "cat": [
+                    "IAB1-1"
+                  ]
+                },
+                "bidType": "video",
+                "bidVideo": {
+                  "duration": 30
+                }
+              },
+              {
+                "ortbBid": {
+                  "id": "apn-bid-2",
+                  "impid": "imp-id-1",
+                  "price": 0.3,
+                  "w": 200,
+                  "h": 250,
+                  "crid": "creative-1",
+                  "cat": [
+                    "IAB1-1"
+                  ]
+                },
+                "bidType": "video",
+                "bidVideo": {
+                  "duration": 30
+                }
+              }
+            ],
+            "seat": "appnexus"
+          }
+        ]
+      }
+    }
+  },
+  "response": {
+    "bids": {
+      "id": "some-request-id",
+      "seatbid": [
+        {
+          "seat": "appnexus",
+          "bid": [
+            {
+              "id": "apn-bid-1",
+              "impid": "imp-id-1",
+              "price": 0.3,
+              "w": 200,
+              "h": 250,
+              "crid": "creative-1",
+              "cat": [
+                "IAB1-1"
+              ],
+              "ext": {
+                "origbidcpm": 0.3,
+                "prebid": {
+                  "meta": {
+                    "adaptercode": "appnexus"
+                  },
+                  "bidid": "bid-appnexus-1",
+                  "type": "video"
+                }
+              }
+            },
+            {
+              "id": "apn-bid-2",
+              "impid": "imp-id-1",
+              "price": 0.3,
+              "w": 200,
+              "h": 250,
+              "crid": "creative-1",
+              "cat": [
+                "IAB1-1"
+              ],
+              "ext": {
+                "origbidcpm": 0.3,
+                "prebid": {
+                  "meta": {
+                    "adaptercode": "appnexus"
+                  },
+                  "bidid": "bid-appnexus-2",
+                  "type": "video"
+                }
+              }
+            }
+          ]
+        }
+      ]
+    },
+    "ext": {
+      "debug": {
+        "resolvedrequest": {
+          "id": "some-request-id",
+          "imp": [
+            {
+              "id": "imp-id-1",
+              "video": {
+                "mimes": [
+                  "video/mp4"
+                ]
+              },
+              "ext": {
+                "prebid": {
+                  "bidder": {
+                    "appnexus": {
+                      "placementId": 1
+                    }
+                  }
+                }
+              }
+            }
+          ],
+          "site": {
+            "page": "test.somepage.com"
+          }
+        }
+      }
+    }
+  }
+}
\ No newline at end of file
diff --git a/exchange/exchangetest/generate-bid-id-one.json b/exchange/exchangetest/generate-bid-id-one.json
new file mode 100644
index 00000000..7d4169d6
--- /dev/null
+++ b/exchange/exchangetest/generate-bid-id-one.json
@@ -0,0 +1,125 @@
+{
+  "bidIDGenerator": {
+    "generateBidID": true,
+    "returnError": false
+  },
+  "incomingRequest": {
+    "ortbRequest": {
+      "id": "some-request-id",
+      "site": {
+        "page": "test.somepage.com"
+      },
+      "imp": [
+        {
+          "id": "imp-id-1",
+          "video": {
+            "mimes": [
+              "video/mp4"
+            ]
+          },
+          "ext": {
+            "prebid": {
+              "bidder": {
+                "appnexus": {
+                  "placementId": 1
+                }
+              }
+            }
+          }
+        }
+      ]
+    }
+  },
+  "outgoingRequests": {
+    "appnexus": {
+      "mockResponse": {
+        "pbsSeatBids": [
+          {
+            "pbsBids": [
+              {
+                "ortbBid": {
+                  "id": "apn-bid",
+                  "impid": "imp-id-1",
+                  "price": 0.3,
+                  "w": 200,
+                  "h": 250,
+                  "crid": "creative-1",
+                  "cat": [
+                    "IAB1-1"
+                  ]
+                },
+                "bidType": "video",
+                "bidVideo": {
+                  "duration": 30
+                }
+              }
+            ],
+            "seat": "appnexus"
+          }
+        ]
+      }
+    }
+  },
+  "response": {
+    "bids": {
+      "id": "some-request-id",
+      "seatbid": [
+        {
+          "seat": "appnexus",
+          "bid": [
+            {
+              "id": "apn-bid",
+              "impid": "imp-id-1",
+              "price": 0.3,
+              "w": 200,
+              "h": 250,
+              "crid": "creative-1",
+              "cat": [
+                "IAB1-1"
+              ],
+              "ext": {
+                "origbidcpm": 0.3,
+                "prebid": {
+                  "meta": {
+                    "adaptercode": "appnexus"
+                  },
+                  "bidid": "bid-appnexus-1",
+                  "type": "video"
+                }
+              }
+            }
+          ]
+        }
+      ]
+    },
+    "ext": {
+      "debug": {
+        "resolvedrequest": {
+          "id": "some-request-id",
+          "imp": [
+            {
+              "id": "imp-id-1",
+              "video": {
+                "mimes": [
+                  "video/mp4"
+                ]
+              },
+              "ext": {
+                "prebid": {
+                  "bidder": {
+                    "appnexus": {
+                      "placementId": 1
+                    }
+                  }
+                }
+              }
+            }
+          ],
+          "site": {
+            "page": "test.somepage.com"
+          }
+        }
+      }
+    }
+  }
+}
\ No newline at end of file
diff --git a/exchange/targeting_test.go b/exchange/targeting_test.go
index e6cc429e..13a7572a 100644
--- a/exchange/targeting_test.go
+++ b/exchange/targeting_test.go
@@ -102,7 +102,7 @@ func runTargetingAuction(t *testing.T, mockBids map[openrtb_ext.BidderName][]*op
 		currencyConverter: currency.NewRateConverter(&http.Client{}, "", time.Duration(0)),
 		gdprDefaultValue:  gdpr.SignalYes,
 		categoriesFetcher: categoriesFetcher,
-		bidIDGenerator:    &mockBidIDGenerator{false, false},
+		bidIDGenerator:    &fakeBidIDGenerator{GenerateBidID: false, ReturnError: false},
 	}
 	ex.requestSplitter = requestSplitter{
 		me:               ex.me,
diff --git a/gdpr/aggregated_config_test.go b/gdpr/aggregated_config_test.go
index 54d1c901..a24e9eff 100644
--- a/gdpr/aggregated_config_test.go
+++ b/gdpr/aggregated_config_test.go
@@ -317,54 +317,54 @@ func TestPurposeEnforcingVendors(t *testing.T) {
 func TestPurposeVendorExceptions(t *testing.T) {
 	tests := []struct {
 		description                     string
-		givePurpose1HostExceptionMap    map[openrtb_ext.BidderName]struct{}
-		givePurpose1AccountExceptionMap map[openrtb_ext.BidderName]struct{}
-		givePurpose2HostExceptionMap    map[openrtb_ext.BidderName]struct{}
-		givePurpose2AccountExceptionMap map[openrtb_ext.BidderName]struct{}
+		givePurpose1HostExceptionMap    map[string]struct{}
+		givePurpose1AccountExceptionMap map[string]struct{}
+		givePurpose2HostExceptionMap    map[string]struct{}
+		givePurpose2AccountExceptionMap map[string]struct{}
 		givePurpose                     consentconstants.Purpose
-		wantExceptionMap                map[openrtb_ext.BidderName]struct{}
+		wantExceptionMap                map[string]struct{}
 	}{
 		{
 			description:                     "Purpose 1 exception list set at account level - use empty account list",
-			givePurpose1HostExceptionMap:    map[openrtb_ext.BidderName]struct{}{},
-			givePurpose1AccountExceptionMap: map[openrtb_ext.BidderName]struct{}{},
+			givePurpose1HostExceptionMap:    map[string]struct{}{},
+			givePurpose1AccountExceptionMap: map[string]struct{}{},
 			givePurpose:                     1,
-			wantExceptionMap:                map[openrtb_ext.BidderName]struct{}{},
+			wantExceptionMap:                map[string]struct{}{},
 		},
 		{
 			description:                     "Purpose 1 exception list set at account level - use nonempty account list",
-			givePurpose1HostExceptionMap:    map[openrtb_ext.BidderName]struct{}{},
-			givePurpose1AccountExceptionMap: map[openrtb_ext.BidderName]struct{}{"appnexus": {}, "rubicon": {}},
+			givePurpose1HostExceptionMap:    map[string]struct{}{},
+			givePurpose1AccountExceptionMap: map[string]struct{}{"appnexus": {}, "rubicon": {}},
 			givePurpose:                     1,
-			wantExceptionMap:                map[openrtb_ext.BidderName]struct{}{"appnexus": {}, "rubicon": {}},
+			wantExceptionMap:                map[string]struct{}{"appnexus": {}, "rubicon": {}},
 		},
 		{
 			description:                     "Purpose 1 exception list not set at account level - use empty host list",
-			givePurpose1HostExceptionMap:    map[openrtb_ext.BidderName]struct{}{},
+			givePurpose1HostExceptionMap:    map[string]struct{}{},
 			givePurpose1AccountExceptionMap: nil,
 			givePurpose:                     1,
-			wantExceptionMap:                map[openrtb_ext.BidderName]struct{}{},
+			wantExceptionMap:                map[string]struct{}{},
 		},
 		{
 			description:                     "Purpose 1 exception list not set at account level - use nonempty host list",
-			givePurpose1HostExceptionMap:    map[openrtb_ext.BidderName]struct{}{"appnexus": {}, "rubicon": {}},
+			givePurpose1HostExceptionMap:    map[string]struct{}{"appnexus": {}, "rubicon": {}},
 			givePurpose1AccountExceptionMap: nil,
 			givePurpose:                     1,
-			wantExceptionMap:                map[openrtb_ext.BidderName]struct{}{"appnexus": {}, "rubicon": {}},
+			wantExceptionMap:                map[string]struct{}{"appnexus": {}, "rubicon": {}},
 		},
 		{
 			description:                     "Purpose 1 exception list not set at account level or host level",
 			givePurpose1HostExceptionMap:    nil,
 			givePurpose1AccountExceptionMap: nil,
 			givePurpose:                     1,
-			wantExceptionMap:                map[openrtb_ext.BidderName]struct{}{},
+			wantExceptionMap:                map[string]struct{}{},
 		},
 		{
 			description:                     "Some other purpose exception list set at account level",
-			givePurpose2HostExceptionMap:    map[openrtb_ext.BidderName]struct{}{},
-			givePurpose2AccountExceptionMap: map[openrtb_ext.BidderName]struct{}{"appnexus": {}, "rubicon": {}},
+			givePurpose2HostExceptionMap:    map[string]struct{}{},
+			givePurpose2AccountExceptionMap: map[string]struct{}{"appnexus": {}, "rubicon": {}},
 			givePurpose:                     2,
-			wantExceptionMap:                map[openrtb_ext.BidderName]struct{}{"appnexus": {}, "rubicon": {}},
+			wantExceptionMap:                map[string]struct{}{"appnexus": {}, "rubicon": {}},
 		},
 	}
 
diff --git a/gdpr/basic_enforcement_test.go b/gdpr/basic_enforcement_test.go
index 06472618..eb22f59b 100644
--- a/gdpr/basic_enforcement_test.go
+++ b/gdpr/basic_enforcement_test.go
@@ -12,7 +12,10 @@ import (
 )
 
 func TestBasicLegalBasis(t *testing.T) {
-	appnexusID := uint16(32)
+	var (
+		appnexus   = string(openrtb_ext.BidderAppnexus)
+		appnexusID = uint16(32)
+	)
 
 	noConsents := "CPerMsAPerMsAAAAAAENCfCAAAAAAAAAAAAAAAAAAAAA"
 	purpose2Consent := "CPerMsAPerMsAAAAAAENCfCAAEAAAAAAAAAAAAAAAAAA"
@@ -149,7 +152,7 @@ func TestBasicLegalBasis(t *testing.T) {
 				PurposeID:                  consentconstants.Purpose(2),
 				EnforcePurpose:             false,
 				EnforceVendors:             true,
-				BasicEnforcementVendorsMap: map[string]struct{}{string(openrtb_ext.BidderAppnexus): {}},
+				BasicEnforcementVendorsMap: map[string]struct{}{appnexus: {}},
 			},
 			wantResult: true,
 		},
@@ -170,7 +173,7 @@ func TestBasicLegalBasis(t *testing.T) {
 				PurposeID:          consentconstants.Purpose(2),
 				EnforcePurpose:     true,
 				EnforceVendors:     true,
-				VendorExceptionMap: map[openrtb_ext.BidderName]struct{}{openrtb_ext.BidderAppnexus: {}},
+				VendorExceptionMap: map[string]struct{}{appnexus: {}},
 			},
 			wantResult: true,
 		},
@@ -181,7 +184,7 @@ func TestBasicLegalBasis(t *testing.T) {
 				PurposeID:          consentconstants.Purpose(2),
 				EnforcePurpose:     true,
 				EnforceVendors:     true,
-				VendorExceptionMap: map[openrtb_ext.BidderName]struct{}{openrtb_ext.BidderAppnexus: {}},
+				VendorExceptionMap: map[string]struct{}{appnexus: {}},
 			},
 			overrides:  Overrides{blockVendorExceptions: true},
 			wantResult: false,
@@ -203,7 +206,7 @@ func TestBasicLegalBasis(t *testing.T) {
 				PurposeID:                  consentconstants.Purpose(2),
 				EnforcePurpose:             true,
 				EnforceVendors:             true,
-				BasicEnforcementVendorsMap: map[string]struct{}{string(openrtb_ext.BidderAppnexus): {}},
+				BasicEnforcementVendorsMap: map[string]struct{}{appnexus: {}},
 			},
 			wantResult: true,
 		},
@@ -233,7 +236,7 @@ func TestBasicLegalBasis(t *testing.T) {
 		enforcer := BasicEnforcement{cfg: tt.config}
 
 		vendorInfo := VendorInfo{vendorID: appnexusID, vendor: nil}
-		result := enforcer.LegalBasis(vendorInfo, openrtb_ext.BidderAppnexus, consentMeta, tt.overrides)
+		result := enforcer.LegalBasis(vendorInfo, appnexus, consentMeta, tt.overrides)
 
 		assert.Equal(t, tt.wantResult, result, tt.description)
 	}
diff --git a/gdpr/full_enforcement_test.go b/gdpr/full_enforcement_test.go
index dac9d7ef..32ba2e50 100644
--- a/gdpr/full_enforcement_test.go
+++ b/gdpr/full_enforcement_test.go
@@ -15,7 +15,10 @@ import (
 )
 
 func TestLegalBasisWithPubRestrictionAllowNone(t *testing.T) {
-	appnexusID := uint16(32)
+	var (
+		appnexus   = string(openrtb_ext.BidderAppnexus)
+		appnexusID = uint16(32)
+	)
 
 	NoConsentsWithP1P2P3V32RestrictionAllowNone := "CPfMKEAPfMKEAAAAAAENCgCAAAAAAAAAAAAAAQAAAAAAAIAAAAAAAGCAAgAgCAAQAQBgAIAIAAAA"
 	P1P2P3PurposeConsentAndV32VendorConsentWithP1P2P3V32RestrictionAllowNone := "CPfMKEAPfMKEAAAAAAENCgCAAOAAAAAAAAAAAQAAAAAEAIAAAAAAAGCAAgAgCAAQAQBgAIAIAAAA"
@@ -44,7 +47,7 @@ func TestLegalBasisWithPubRestrictionAllowNone(t *testing.T) {
 			config: purposeConfig{
 				EnforcePurpose:     true,
 				EnforceVendors:     true,
-				VendorExceptionMap: map[openrtb_ext.BidderName]struct{}{openrtb_ext.BidderAppnexus: {}},
+				VendorExceptionMap: map[string]struct{}{appnexus: {}},
 			},
 			consent:                  NoConsentsWithP1P2P3V32RestrictionAllowNone,
 			wantConsentPurposeResult: false,
@@ -80,21 +83,24 @@ func TestLegalBasisWithPubRestrictionAllowNone(t *testing.T) {
 		enforcer := FullEnforcement{cfg: tt.config}
 
 		enforcer.cfg.PurposeID = consentconstants.Purpose(1)
-		consentPurposeResult := enforcer.LegalBasis(vendorInfo, openrtb_ext.BidderAppnexus, consentMeta, Overrides{})
+		consentPurposeResult := enforcer.LegalBasis(vendorInfo, appnexus, consentMeta, Overrides{})
 		assert.Equal(t, tt.wantConsentPurposeResult, consentPurposeResult, tt.description+" -- GVL consent purpose")
 
 		enforcer.cfg.PurposeID = consentconstants.Purpose(2)
-		LIPurposeresult := enforcer.LegalBasis(vendorInfo, openrtb_ext.BidderAppnexus, consentMeta, Overrides{})
+		LIPurposeresult := enforcer.LegalBasis(vendorInfo, appnexus, consentMeta, Overrides{})
 		assert.Equal(t, tt.wantLIPurposeResult, LIPurposeresult, tt.description+" -- GVL LI purpose")
 
 		enforcer.cfg.PurposeID = consentconstants.Purpose(3)
-		flexPurposeResult := enforcer.LegalBasis(vendorInfo, openrtb_ext.BidderAppnexus, consentMeta, Overrides{})
+		flexPurposeResult := enforcer.LegalBasis(vendorInfo, appnexus, consentMeta, Overrides{})
 		assert.Equal(t, tt.wantFlexPurposeResult, flexPurposeResult, tt.description+" -- GVL flex purpose")
 	}
 }
 
 func TestLegalBasisWithNoPubRestrictionsAndWithPubRestrictionAllowAll(t *testing.T) {
-	appnexusID := uint16(32)
+	var (
+		appnexus   = string(openrtb_ext.BidderAppnexus)
+		appnexusID = uint16(32)
+	)
 
 	NoConsents := "CPfCRQAPfCRQAAAAAAENCgCAAAAAAAAAAAAAAAAAAAAA"
 	P1P2P3PurposeConsent := "CPfCRQAPfCRQAAAAAAENCgCAAOAAAAAAAAAAAAAAAAAA"
@@ -325,7 +331,7 @@ func TestLegalBasisWithNoPubRestrictionsAndWithPubRestrictionAllowAll(t *testing
 			config: purposeConfig{
 				EnforcePurpose:     true,
 				EnforceVendors:     true,
-				VendorExceptionMap: map[openrtb_ext.BidderName]struct{}{openrtb_ext.BidderAppnexus: {}},
+				VendorExceptionMap: map[string]struct{}{appnexus: {}},
 			},
 			consentNoPubRestriction:   NoConsents,
 			consentWithPubRestriction: NoConsentsWithP1P2P3V32RestrictionAllowAll,
@@ -338,7 +344,7 @@ func TestLegalBasisWithNoPubRestrictionsAndWithPubRestrictionAllowAll(t *testing
 			config: purposeConfig{
 				EnforcePurpose:     true,
 				EnforceVendors:     true,
-				VendorExceptionMap: map[openrtb_ext.BidderName]struct{}{openrtb_ext.BidderAppnexus: {}},
+				VendorExceptionMap: map[string]struct{}{appnexus: {}},
 			},
 			consentNoPubRestriction:   NoConsents,
 			consentWithPubRestriction: NoConsentsWithP1P2P3V32RestrictionAllowAll,
@@ -370,22 +376,25 @@ func TestLegalBasisWithNoPubRestrictionsAndWithPubRestrictionAllowAll(t *testing
 			enforcer := FullEnforcement{cfg: tt.config}
 
 			enforcer.cfg.PurposeID = consentconstants.Purpose(1)
-			consentPurposeResult := enforcer.LegalBasis(vendorInfo, openrtb_ext.BidderAppnexus, consentMeta, tt.overrides)
+			consentPurposeResult := enforcer.LegalBasis(vendorInfo, appnexus, consentMeta, tt.overrides)
 			assert.Equal(t, tt.wantConsentPurposeResult, consentPurposeResult, tt.description+" -- GVL consent purpose -- consent string %d of %d", i+1, len(consents))
 
 			enforcer.cfg.PurposeID = consentconstants.Purpose(2)
-			LIPurposeresult := enforcer.LegalBasis(vendorInfo, openrtb_ext.BidderAppnexus, consentMeta, tt.overrides)
+			LIPurposeresult := enforcer.LegalBasis(vendorInfo, appnexus, consentMeta, tt.overrides)
 			assert.Equal(t, tt.wantLIPurposeResult, LIPurposeresult, tt.description+" -- GVL LI purpose -- consent string %d of %d", i+1, len(consents))
 
 			enforcer.cfg.PurposeID = consentconstants.Purpose(3)
-			flexPurposeResult := enforcer.LegalBasis(vendorInfo, openrtb_ext.BidderAppnexus, consentMeta, tt.overrides)
+			flexPurposeResult := enforcer.LegalBasis(vendorInfo, appnexus, consentMeta, tt.overrides)
 			assert.Equal(t, tt.wantFlexPurposeResult, flexPurposeResult, tt.description+" -- GVL flex purpose -- consent string %d of %d", i+1, len(consents))
 		}
 	}
 }
 
 func TestLegalBasisWithPubRestrictionRequireConsent(t *testing.T) {
-	appnexusID := uint16(32)
+	var (
+		appnexus   = string(openrtb_ext.BidderAppnexus)
+		appnexusID = uint16(32)
+	)
 
 	NoConsentsWithP1P2P3V32RestrictionRequireConsent := "CPfFkMAPfFkMAAAAAAENCgCAAAAAAAAAAAAAAQAAAAAAAIAAAAAAAGCgAgAgCQAQAQBoAIAIAAAA"
 	P1P2P3PurposeConsentWithP1P2P3V32RestrictionRequireConsent := "CPfFkMAPfFkMAAAAAAENCgCAAOAAAAAAAAAAAQAAAAAAAIAAAAAAAGCgAgAgCQAQAQBoAIAIAAAA"
@@ -590,7 +599,7 @@ func TestLegalBasisWithPubRestrictionRequireConsent(t *testing.T) {
 			config: purposeConfig{
 				EnforcePurpose:     true,
 				EnforceVendors:     true,
-				VendorExceptionMap: map[openrtb_ext.BidderName]struct{}{openrtb_ext.BidderAppnexus: {}},
+				VendorExceptionMap: map[string]struct{}{appnexus: {}},
 			},
 			consent:                  NoConsentsWithP1P2P3V32RestrictionRequireConsent,
 			wantConsentPurposeResult: true,
@@ -602,7 +611,7 @@ func TestLegalBasisWithPubRestrictionRequireConsent(t *testing.T) {
 			config: purposeConfig{
 				EnforcePurpose:     true,
 				EnforceVendors:     true,
-				VendorExceptionMap: map[openrtb_ext.BidderName]struct{}{openrtb_ext.BidderAppnexus: {}},
+				VendorExceptionMap: map[string]struct{}{appnexus: {}},
 			},
 			consent:                  NoConsentsWithP1P2P3V32RestrictionRequireConsent,
 			overrides:                Overrides{blockVendorExceptions: true},
@@ -628,21 +637,24 @@ func TestLegalBasisWithPubRestrictionRequireConsent(t *testing.T) {
 		enforcer := FullEnforcement{cfg: tt.config}
 
 		enforcer.cfg.PurposeID = consentconstants.Purpose(1)
-		consentPurposeResult := enforcer.LegalBasis(vendorInfo, openrtb_ext.BidderAppnexus, consentMeta, tt.overrides)
+		consentPurposeResult := enforcer.LegalBasis(vendorInfo, appnexus, consentMeta, tt.overrides)
 		assert.Equal(t, tt.wantConsentPurposeResult, consentPurposeResult, tt.description+" -- GVL consent purpose")
 
 		enforcer.cfg.PurposeID = consentconstants.Purpose(2)
-		LIPurposeresult := enforcer.LegalBasis(vendorInfo, openrtb_ext.BidderAppnexus, consentMeta, tt.overrides)
+		LIPurposeresult := enforcer.LegalBasis(vendorInfo, appnexus, consentMeta, tt.overrides)
 		assert.Equal(t, tt.wantLIPurposeResult, LIPurposeresult, tt.description+" -- GVL LI purpose")
 
 		enforcer.cfg.PurposeID = consentconstants.Purpose(3)
-		flexPurposeResult := enforcer.LegalBasis(vendorInfo, openrtb_ext.BidderAppnexus, consentMeta, tt.overrides)
+		flexPurposeResult := enforcer.LegalBasis(vendorInfo, appnexus, consentMeta, tt.overrides)
 		assert.Equal(t, tt.wantFlexPurposeResult, flexPurposeResult, tt.description+" -- GVL flex purpose")
 	}
 }
 
 func TestLegalBasisWithPubRestrictionRequireLI(t *testing.T) {
-	appnexusID := uint16(32)
+	var (
+		appnexus   = string(openrtb_ext.BidderAppnexus)
+		appnexusID = uint16(32)
+	)
 
 	NoConsentsWithP1P2P3V32RestrictionRequireLI := "CPfFkMAPfFkMAAAAAAENCgCAAAAAAAAAAAAAAQAAAAAAAIAAAAAAAGDAAgAgCgAQAQBwAIAIAAAA"
 	P1P2P3PurposeConsentWithP1P2P3V32RestrictionRequireLI := "CPfFkMAPfFkMAAAAAAENCgCAAOAAAAAAAAAAAQAAAAAAAIAAAAAAAGDAAgAgCgAQAQBwAIAIAAAA"
@@ -847,7 +859,7 @@ func TestLegalBasisWithPubRestrictionRequireLI(t *testing.T) {
 			config: purposeConfig{
 				EnforcePurpose:     true,
 				EnforceVendors:     true,
-				VendorExceptionMap: map[openrtb_ext.BidderName]struct{}{openrtb_ext.BidderAppnexus: {}},
+				VendorExceptionMap: map[string]struct{}{appnexus: {}},
 			},
 			consent:                  NoConsentsWithP1P2P3V32RestrictionRequireLI,
 			wantConsentPurposeResult: true,
@@ -859,7 +871,7 @@ func TestLegalBasisWithPubRestrictionRequireLI(t *testing.T) {
 			config: purposeConfig{
 				EnforcePurpose:     true,
 				EnforceVendors:     true,
-				VendorExceptionMap: map[openrtb_ext.BidderName]struct{}{openrtb_ext.BidderAppnexus: {}},
+				VendorExceptionMap: map[string]struct{}{appnexus: {}},
 			},
 			consent:                  NoConsentsWithP1P2P3V32RestrictionRequireLI,
 			overrides:                Overrides{blockVendorExceptions: true},
@@ -885,20 +897,21 @@ func TestLegalBasisWithPubRestrictionRequireLI(t *testing.T) {
 		enforcer := FullEnforcement{cfg: tt.config}
 
 		enforcer.cfg.PurposeID = consentconstants.Purpose(1)
-		consentPurposeResult := enforcer.LegalBasis(vendorInfo, openrtb_ext.BidderAppnexus, consentMeta, tt.overrides)
+		consentPurposeResult := enforcer.LegalBasis(vendorInfo, appnexus, consentMeta, tt.overrides)
 		assert.Equal(t, tt.wantConsentPurposeResult, consentPurposeResult, tt.description+" -- GVL consent purpose")
 
 		enforcer.cfg.PurposeID = consentconstants.Purpose(2)
-		LIPurposeresult := enforcer.LegalBasis(vendorInfo, openrtb_ext.BidderAppnexus, consentMeta, tt.overrides)
+		LIPurposeresult := enforcer.LegalBasis(vendorInfo, appnexus, consentMeta, tt.overrides)
 		assert.Equal(t, tt.wantLIPurposeResult, LIPurposeresult, tt.description+" -- GVL LI purpose")
 
 		enforcer.cfg.PurposeID = consentconstants.Purpose(3)
-		flexPurposeResult := enforcer.LegalBasis(vendorInfo, openrtb_ext.BidderAppnexus, consentMeta, tt.overrides)
+		flexPurposeResult := enforcer.LegalBasis(vendorInfo, appnexus, consentMeta, tt.overrides)
 		assert.Equal(t, tt.wantFlexPurposeResult, flexPurposeResult, tt.description+" -- GVL flex purpose")
 	}
 }
 
 func TestLegalBasisWithoutVendor(t *testing.T) {
+	appnexus := string(openrtb_ext.BidderAppnexus)
 	P1P2P3PurposeConsent := "CPfCRQAPfCRQAAAAAAENCgCAAOAAAAAAAAAAAAAAAAAA"
 	tests := []struct {
 		name       string
@@ -918,7 +931,7 @@ func TestLegalBasisWithoutVendor(t *testing.T) {
 			config: purposeConfig{
 				EnforcePurpose:     true,
 				EnforceVendors:     false,
-				VendorExceptionMap: map[openrtb_ext.BidderName]struct{}{openrtb_ext.BidderAppnexus: {}},
+				VendorExceptionMap: map[string]struct{}{appnexus: {}},
 			},
 			wantResult: true,
 		},
@@ -952,7 +965,7 @@ func TestLegalBasisWithoutVendor(t *testing.T) {
 			enforcer := FullEnforcement{cfg: tt.config}
 			enforcer.cfg.PurposeID = consentconstants.Purpose(3)
 
-			result := enforcer.LegalBasis(vendorInfo, openrtb_ext.BidderAppnexus, consentMeta, Overrides{})
+			result := enforcer.LegalBasis(vendorInfo, appnexus, consentMeta, Overrides{})
 			assert.Equal(t, tt.wantResult, result)
 		})
 	}
diff --git a/gdpr/gdpr_test.go b/gdpr/gdpr_test.go
index 9604e24f..3729eda3 100644
--- a/gdpr/gdpr_test.go
+++ b/gdpr/gdpr_test.go
@@ -60,6 +60,6 @@ type fakePurposeEnforcerBuilder struct {
 	purposeEnforcer PurposeEnforcer
 }
 
-func (fpeb fakePurposeEnforcerBuilder) Builder(consentconstants.Purpose, openrtb_ext.BidderName) PurposeEnforcer {
+func (fpeb fakePurposeEnforcerBuilder) Builder(consentconstants.Purpose, string) PurposeEnforcer {
 	return fpeb.purposeEnforcer
 }
diff --git a/gdpr/impl_test.go b/gdpr/impl_test.go
index fc3d69d9..64fa4434 100644
--- a/gdpr/impl_test.go
+++ b/gdpr/impl_test.go
@@ -350,7 +350,7 @@ func TestAllowActivitiesBidderWithoutGVLID(t *testing.T) {
 	tests := []struct {
 		name                    string
 		enforceAlgoID           config.TCF2EnforcementAlgo
-		vendorExceptions        map[openrtb_ext.BidderName]struct{}
+		vendorExceptions        map[string]struct{}
 		basicEnforcementVendors map[string]struct{}
 		consent                 string
 		allowBidRequest         bool
@@ -364,7 +364,7 @@ func TestAllowActivitiesBidderWithoutGVLID(t *testing.T) {
 		{
 			name:             "full_enforcement_vendor_exception_user_consents_to_purpose_2",
 			enforceAlgoID:    config.TCF2FullEnforcement,
-			vendorExceptions: map[openrtb_ext.BidderName]struct{}{bidderWithoutGVLID: {}},
+			vendorExceptions: map[string]struct{}{string(bidderWithoutGVLID): {}},
 			consent:          purpose2Consent,
 			allowBidRequest:  true,
 			passID:           true,
@@ -375,7 +375,7 @@ func TestAllowActivitiesBidderWithoutGVLID(t *testing.T) {
 		},
 		{
 			name:             "basic_enforcement_vendor_exception_user_consents_to_purpose_2",
-			vendorExceptions: map[openrtb_ext.BidderName]struct{}{bidderWithoutGVLID: {}},
+			vendorExceptions: map[string]struct{}{string(bidderWithoutGVLID): {}},
 			consent:          purpose2Consent,
 			allowBidRequest:  true,
 			passID:           true,
@@ -1110,12 +1110,13 @@ func TestAllowActivitiesBidRequests(t *testing.T) {
 }
 
 func TestAllowActivitiesVendorException(t *testing.T) {
+	appnexus := string(openrtb_ext.BidderAppnexus)
 	noPurposeOrVendorConsentAndPubRestrictsP2 := "CPF_61ePF_61eFxAAAENAiCAAAAAAAAAAAAAACEAAAACEAAgAgAA"
 	noPurposeOrVendorConsentAndPubRestrictsNone := "CPF_61ePF_61eFxAAAENAiCAAAAAAAAAAAAAACEAAAAA"
 
 	testDefs := []struct {
 		description           string
-		p2VendorExceptionMap  map[openrtb_ext.BidderName]struct{}
+		p2VendorExceptionMap  map[string]struct{}
 		sf1VendorExceptionMap map[openrtb_ext.BidderName]struct{}
 		bidder                openrtb_ext.BidderName
 		consent               string
@@ -1126,7 +1127,7 @@ func TestAllowActivitiesVendorException(t *testing.T) {
 	}{
 		{
 			description:          "Bid/ID blocked by publisher - p2 enabled with p2 vendor exception, pub restricts p2 for vendor",
-			p2VendorExceptionMap: map[openrtb_ext.BidderName]struct{}{openrtb_ext.BidderAppnexus: {}},
+			p2VendorExceptionMap: map[string]struct{}{appnexus: {}},
 			bidder:               openrtb_ext.BidderAppnexus,
 			bidderCoreName:       openrtb_ext.BidderAppnexus,
 			consent:              noPurposeOrVendorConsentAndPubRestrictsP2,
@@ -1136,7 +1137,7 @@ func TestAllowActivitiesVendorException(t *testing.T) {
 		},
 		{
 			description:           "Bid/ID allowed by vendor exception - p2 enabled with p2 vendor exception, pub restricts none",
-			p2VendorExceptionMap:  map[openrtb_ext.BidderName]struct{}{openrtb_ext.BidderAppnexus: {}},
+			p2VendorExceptionMap:  map[string]struct{}{appnexus: {}},
 			sf1VendorExceptionMap: map[openrtb_ext.BidderName]struct{}{},
 			bidder:                openrtb_ext.BidderAppnexus,
 			bidderCoreName:        openrtb_ext.BidderAppnexus,
@@ -1147,7 +1148,7 @@ func TestAllowActivitiesVendorException(t *testing.T) {
 		},
 		{
 			description:           "Geo blocked - sf1 enabled but no consent",
-			p2VendorExceptionMap:  map[openrtb_ext.BidderName]struct{}{},
+			p2VendorExceptionMap:  map[string]struct{}{},
 			sf1VendorExceptionMap: map[openrtb_ext.BidderName]struct{}{},
 			bidder:                openrtb_ext.BidderAppnexus,
 			bidderCoreName:        openrtb_ext.BidderAppnexus,
@@ -1158,7 +1159,7 @@ func TestAllowActivitiesVendorException(t *testing.T) {
 		},
 		{
 			description:           "Geo allowed by vendor exception - sf1 enabled with sf1 vendor exception",
-			p2VendorExceptionMap:  map[openrtb_ext.BidderName]struct{}{},
+			p2VendorExceptionMap:  map[string]struct{}{},
 			sf1VendorExceptionMap: map[openrtb_ext.BidderName]struct{}{openrtb_ext.BidderAppnexus: {}},
 			bidder:                openrtb_ext.BidderAppnexus,
 			bidderCoreName:        openrtb_ext.BidderAppnexus,
@@ -1204,33 +1205,34 @@ func TestAllowActivitiesVendorException(t *testing.T) {
 }
 
 func TestBidderSyncAllowedVendorException(t *testing.T) {
+	appnexus := string(openrtb_ext.BidderAppnexus)
 	noPurposeOrVendorConsentAndPubRestrictsP1 := "CPF_61ePF_61eFxAAAENAiCAAAAAAAAAAAAAAQAAAAAAAAAAIIACACA"
 	noPurposeOrVendorConsentAndPubRestrictsNone := "CPF_61ePF_61eFxAAAENAiCAAAAAAAAAAAAAACEAAAAA"
 
 	testDefs := []struct {
 		description          string
-		p1VendorExceptionMap map[openrtb_ext.BidderName]struct{}
+		p1VendorExceptionMap map[string]struct{}
 		bidder               openrtb_ext.BidderName
 		consent              string
 		allowSync            bool
 	}{
 		{
 			description:          "Sync blocked by no consent - p1 enabled, no p1 vendor exception, pub restricts none",
-			p1VendorExceptionMap: map[openrtb_ext.BidderName]struct{}{},
+			p1VendorExceptionMap: map[string]struct{}{},
 			bidder:               openrtb_ext.BidderAppnexus,
 			consent:              noPurposeOrVendorConsentAndPubRestrictsNone,
 			allowSync:            false,
 		},
 		{
 			description:          "Sync blocked by publisher - p1 enabled with p1 vendor exception, pub restricts p1 for vendor",
-			p1VendorExceptionMap: map[openrtb_ext.BidderName]struct{}{openrtb_ext.BidderAppnexus: {}},
+			p1VendorExceptionMap: map[string]struct{}{appnexus: {}},
 			bidder:               openrtb_ext.BidderAppnexus,
 			consent:              noPurposeOrVendorConsentAndPubRestrictsP1,
 			allowSync:            false,
 		},
 		{
 			description:          "Sync allowed by vendor exception - p1 enabled with p1 vendor exception, pub restricts none",
-			p1VendorExceptionMap: map[openrtb_ext.BidderName]struct{}{openrtb_ext.BidderAppnexus: {}},
+			p1VendorExceptionMap: map[string]struct{}{appnexus: {}},
 			bidder:               openrtb_ext.BidderAppnexus,
 			consent:              noPurposeOrVendorConsentAndPubRestrictsNone,
 			allowSync:            true,
diff --git a/gdpr/purpose_config_test.go b/gdpr/purpose_config_test.go
index 4837b62a..c2bd6f56 100644
--- a/gdpr/purpose_config_test.go
+++ b/gdpr/purpose_config_test.go
@@ -9,59 +9,66 @@ import (
 )
 
 func TestPurposeConfigBasicEnforcementVendor(t *testing.T) {
+	var (
+		appnexus = string(openrtb_ext.BidderAppnexus)
+		ix       = string(openrtb_ext.BidderIx)
+		pubmatic = string(openrtb_ext.BidderPubmatic)
+		rubicon  = string(openrtb_ext.BidderRubicon)
+	)
+
 	tests := []struct {
 		description      string
 		giveBasicVendors map[string]struct{}
-		giveBidder       openrtb_ext.BidderName
+		giveBidder       string
 		wantFound        bool
 	}{
 		{
 			description:      "vendor map is nil",
 			giveBasicVendors: nil,
-			giveBidder:       openrtb_ext.BidderAppnexus,
+			giveBidder:       appnexus,
 			wantFound:        false,
 		},
 		{
 			description:      "vendor map is empty",
 			giveBasicVendors: map[string]struct{}{},
-			giveBidder:       openrtb_ext.BidderAppnexus,
+			giveBidder:       appnexus,
 			wantFound:        false,
 		},
 		{
 			description: "vendor map has one bidders - bidder not found",
 			giveBasicVendors: map[string]struct{}{
-				string(openrtb_ext.BidderPubmatic): {},
+				pubmatic: {},
 			},
-			giveBidder: openrtb_ext.BidderAppnexus,
+			giveBidder: appnexus,
 			wantFound:  false,
 		},
 		{
 			description: "vendor map has one bidders - bidder found",
 			giveBasicVendors: map[string]struct{}{
-				string(openrtb_ext.BidderAppnexus): {},
+				appnexus: {},
 			},
-			giveBidder: openrtb_ext.BidderAppnexus,
+			giveBidder: appnexus,
 			wantFound:  true,
 		},
 		{
 			description: "vendor map has many bidderss - bidder not found",
 			giveBasicVendors: map[string]struct{}{
-				string(openrtb_ext.BidderIx):       {},
-				string(openrtb_ext.BidderPubmatic): {},
-				string(openrtb_ext.BidderRubicon):  {},
+				ix:       {},
+				pubmatic: {},
+				rubicon:  {},
 			},
-			giveBidder: openrtb_ext.BidderAppnexus,
+			giveBidder: appnexus,
 			wantFound:  false,
 		},
 		{
 			description: "vendor map has many bidderss - bidder found",
 			giveBasicVendors: map[string]struct{}{
-				string(openrtb_ext.BidderIx):       {},
-				string(openrtb_ext.BidderPubmatic): {},
-				string(openrtb_ext.BidderAppnexus): {},
-				string(openrtb_ext.BidderRubicon):  {},
+				ix:       {},
+				pubmatic: {},
+				appnexus: {},
+				rubicon:  {},
 			},
-			giveBidder: openrtb_ext.BidderAppnexus,
+			giveBidder: appnexus,
 			wantFound:  true,
 		},
 	}
@@ -77,59 +84,66 @@ func TestPurposeConfigBasicEnforcementVendor(t *testing.T) {
 }
 
 func TestPurposeConfigVendorException(t *testing.T) {
+	var (
+		appnexus = string(openrtb_ext.BidderAppnexus)
+		ix       = string(openrtb_ext.BidderIx)
+		pubmatic = string(openrtb_ext.BidderPubmatic)
+		rubicon  = string(openrtb_ext.BidderRubicon)
+	)
+
 	tests := []struct {
 		description    string
-		giveExceptions map[openrtb_ext.BidderName]struct{}
-		giveBidder     openrtb_ext.BidderName
+		giveExceptions map[string]struct{}
+		giveBidder     string
 		wantFound      bool
 	}{
 		{
 			description:    "vendor exception map is nil",
 			giveExceptions: nil,
-			giveBidder:     openrtb_ext.BidderAppnexus,
+			giveBidder:     appnexus,
 			wantFound:      false,
 		},
 		{
 			description:    "vendor exception map is empty",
-			giveExceptions: map[openrtb_ext.BidderName]struct{}{},
-			giveBidder:     openrtb_ext.BidderAppnexus,
+			giveExceptions: map[string]struct{}{},
+			giveBidder:     appnexus,
 			wantFound:      false,
 		},
 		{
 			description: "vendor exception map has one bidders - bidder not found",
-			giveExceptions: map[openrtb_ext.BidderName]struct{}{
-				openrtb_ext.BidderPubmatic: {},
+			giveExceptions: map[string]struct{}{
+				pubmatic: {},
 			},
-			giveBidder: openrtb_ext.BidderAppnexus,
+			giveBidder: appnexus,
 			wantFound:  false,
 		},
 		{
 			description: "vendor exception map has one bidders - bidder found",
-			giveExceptions: map[openrtb_ext.BidderName]struct{}{
-				openrtb_ext.BidderAppnexus: {},
+			giveExceptions: map[string]struct{}{
+				appnexus: {},
 			},
-			giveBidder: openrtb_ext.BidderAppnexus,
+			giveBidder: appnexus,
 			wantFound:  true,
 		},
 		{
 			description: "vendor exception map has many bidderss - bidder not found",
-			giveExceptions: map[openrtb_ext.BidderName]struct{}{
-				openrtb_ext.BidderIx:       {},
-				openrtb_ext.BidderPubmatic: {},
-				openrtb_ext.BidderRubicon:  {},
+			giveExceptions: map[string]struct{}{
+				ix:       {},
+				pubmatic: {},
+				rubicon:  {},
 			},
-			giveBidder: openrtb_ext.BidderAppnexus,
+			giveBidder: appnexus,
 			wantFound:  false,
 		},
 		{
 			description: "vendor exception map has many bidderss - bidder found",
-			giveExceptions: map[openrtb_ext.BidderName]struct{}{
-				openrtb_ext.BidderIx:       {},
-				openrtb_ext.BidderPubmatic: {},
-				openrtb_ext.BidderAppnexus: {},
-				openrtb_ext.BidderRubicon:  {},
+			giveExceptions: map[string]struct{}{
+				ix:       {},
+				pubmatic: {},
+				appnexus: {},
+				rubicon:  {},
 			},
-			giveBidder: openrtb_ext.BidderAppnexus,
+			giveBidder: appnexus,
 			wantFound:  true,
 		},
 	}
diff --git a/gdpr/purpose_enforcer_test.go b/gdpr/purpose_enforcer_test.go
index ed1176b1..fa060570 100644
--- a/gdpr/purpose_enforcer_test.go
+++ b/gdpr/purpose_enforcer_test.go
@@ -11,15 +11,17 @@ import (
 )
 
 func TestNewPurposeEnforcerBuilder(t *testing.T) {
+	appnexus := string(openrtb_ext.BidderAppnexus)
+
 	tests := []struct {
 		description        string
 		enforceAlgo        config.TCF2EnforcementAlgo
 		enforcePurpose     bool
 		enforceVendors     bool
 		basicVendorsMap    map[string]struct{}
-		vendorExceptionMap map[openrtb_ext.BidderName]struct{}
+		vendorExceptionMap map[string]struct{}
 		purpose            consentconstants.Purpose
-		bidder             openrtb_ext.BidderName
+		bidder             string
 		wantType           PurposeEnforcer
 	}{
 		{
@@ -28,9 +30,9 @@ func TestNewPurposeEnforcerBuilder(t *testing.T) {
 			enforcePurpose:     true,
 			enforceVendors:     true,
 			basicVendorsMap:    map[string]struct{}{},
-			vendorExceptionMap: map[openrtb_ext.BidderName]struct{}{},
+			vendorExceptionMap: map[string]struct{}{},
 			purpose:            consentconstants.Purpose(1),
-			bidder:             openrtb_ext.BidderAppnexus,
+			bidder:             appnexus,
 			wantType:           &FullEnforcement{},
 		},
 		{
@@ -38,10 +40,10 @@ func TestNewPurposeEnforcerBuilder(t *testing.T) {
 			enforceAlgo:        config.TCF2FullEnforcement,
 			enforcePurpose:     true,
 			enforceVendors:     true,
-			basicVendorsMap:    map[string]struct{}{string(openrtb_ext.BidderAppnexus): {}},
-			vendorExceptionMap: map[openrtb_ext.BidderName]struct{}{},
+			basicVendorsMap:    map[string]struct{}{appnexus: {}},
+			vendorExceptionMap: map[string]struct{}{},
 			purpose:            consentconstants.Purpose(1),
-			bidder:             openrtb_ext.BidderAppnexus,
+			bidder:             appnexus,
 			wantType:           &FullEnforcement{},
 		},
 		{
@@ -50,9 +52,9 @@ func TestNewPurposeEnforcerBuilder(t *testing.T) {
 			enforcePurpose:     true,
 			enforceVendors:     true,
 			basicVendorsMap:    map[string]struct{}{},
-			vendorExceptionMap: map[openrtb_ext.BidderName]struct{}{},
+			vendorExceptionMap: map[string]struct{}{},
 			purpose:            consentconstants.Purpose(1),
-			bidder:             openrtb_ext.BidderAppnexus,
+			bidder:             appnexus,
 			wantType:           &BasicEnforcement{},
 		},
 		{
@@ -61,9 +63,9 @@ func TestNewPurposeEnforcerBuilder(t *testing.T) {
 			enforcePurpose:     true,
 			enforceVendors:     true,
 			basicVendorsMap:    map[string]struct{}{},
-			vendorExceptionMap: map[openrtb_ext.BidderName]struct{}{},
+			vendorExceptionMap: map[string]struct{}{},
 			purpose:            consentconstants.Purpose(2),
-			bidder:             openrtb_ext.BidderAppnexus,
+			bidder:             appnexus,
 			wantType:           &FullEnforcement{},
 		},
 		{
@@ -71,10 +73,10 @@ func TestNewPurposeEnforcerBuilder(t *testing.T) {
 			enforceAlgo:        config.TCF2FullEnforcement,
 			enforcePurpose:     true,
 			enforceVendors:     true,
-			basicVendorsMap:    map[string]struct{}{string(openrtb_ext.BidderAppnexus): {}},
-			vendorExceptionMap: map[openrtb_ext.BidderName]struct{}{},
+			basicVendorsMap:    map[string]struct{}{appnexus: {}},
+			vendorExceptionMap: map[string]struct{}{},
 			purpose:            consentconstants.Purpose(2),
-			bidder:             openrtb_ext.BidderAppnexus,
+			bidder:             appnexus,
 			wantType:           &BasicEnforcement{},
 		},
 		{
@@ -83,9 +85,9 @@ func TestNewPurposeEnforcerBuilder(t *testing.T) {
 			enforcePurpose:     true,
 			enforceVendors:     true,
 			basicVendorsMap:    map[string]struct{}{},
-			vendorExceptionMap: map[openrtb_ext.BidderName]struct{}{},
+			vendorExceptionMap: map[string]struct{}{},
 			purpose:            consentconstants.Purpose(2),
-			bidder:             openrtb_ext.BidderAppnexus,
+			bidder:             appnexus,
 			wantType:           &BasicEnforcement{},
 		},
 	}
@@ -136,13 +138,13 @@ func TestNewPurposeEnforcerBuilder(t *testing.T) {
 
 func TestNewPurposeEnforcerBuilderCaching(t *testing.T) {
 
-	bidder1 := openrtb_ext.BidderAppnexus
+	bidder1 := string(openrtb_ext.BidderAppnexus)
 	bidder1Enforcers := make([]PurposeEnforcer, 11)
-	bidder2 := openrtb_ext.BidderIx
+	bidder2 := string(openrtb_ext.BidderIx)
 	bidder2Enforcers := make([]PurposeEnforcer, 11)
-	bidder3 := openrtb_ext.BidderPubmatic
+	bidder3 := string(openrtb_ext.BidderPubmatic)
 	bidder3Enforcers := make([]PurposeEnforcer, 11)
-	bidder4 := openrtb_ext.BidderRubicon
+	bidder4 := string(openrtb_ext.BidderRubicon)
 	bidder4Enforcers := make([]PurposeEnforcer, 11)
 
 	cfg := fakeTCF2ConfigReader{
@@ -198,7 +200,7 @@ type fakeTCF2ConfigReader struct {
 	enforceAlgo                config.TCF2EnforcementAlgo
 	enforcePurpose             bool
 	enforceVendors             bool
-	vendorExceptionMap         map[openrtb_ext.BidderName]struct{}
+	vendorExceptionMap         map[string]struct{}
 	basicEnforcementVendorsMap map[string]struct{}
 }
 
@@ -226,7 +228,7 @@ func (fcr *fakeTCF2ConfigReader) PurposeEnforcementAlgo(purpose consentconstants
 func (fcr *fakeTCF2ConfigReader) PurposeEnforcingVendors(purpose consentconstants.Purpose) bool {
 	return fcr.enforceVendors
 }
-func (fcr *fakeTCF2ConfigReader) PurposeVendorExceptions(purpose consentconstants.Purpose) map[openrtb_ext.BidderName]struct{} {
+func (fcr *fakeTCF2ConfigReader) PurposeVendorExceptions(purpose consentconstants.Purpose) map[string]struct{} {
 	return fcr.vendorExceptionMap
 }
 func (fcr *fakeTCF2ConfigReader) PurposeOneTreatmentEnabled() bool {
diff --git a/hooks/hookexecution/execution_test.go b/hooks/hookexecution/execution_test.go
index c7d17a15..33f652fa 100644
--- a/hooks/hookexecution/execution_test.go
+++ b/hooks/hookexecution/execution_test.go
@@ -176,7 +176,7 @@ func TestHandleModuleActivitiesProcessedAuctionRequestPayload(t *testing.T) {
 			//check input payload didn't change
 			origInPayloadData := test.inPayloadData
 			activityControl := privacy.NewActivityControl(test.privacyConfig)
-			account := &config.Account{Privacy: config.AccountPrivacy{IPv6Config: config.IPv6{testIPv6ScrubBytes}}}
+			account := &config.Account{Privacy: config.AccountPrivacy{IPv6Config: config.IPv6{AnonKeepBits: testIPv6ScrubBytes}}}
 			newPayload := handleModuleActivities(test.hookCode, activityControl, test.inPayloadData, account)
 			assert.Equal(t, test.expectedPayloadData.Request.BidRequest, newPayload.Request.BidRequest)
 			assert.Equal(t, origInPayloadData, test.inPayloadData)
diff --git a/openrtb_ext/request_wrapper_test.go b/openrtb_ext/request_wrapper_test.go
index f04a51a4..ffa925e4 100644
--- a/openrtb_ext/request_wrapper_test.go
+++ b/openrtb_ext/request_wrapper_test.go
@@ -767,13 +767,13 @@ func TestRebuildDeviceExt(t *testing.T) {
 		{
 			description:             "Nil - Dirty",
 			request:                 openrtb2.BidRequest{},
-			requestDeviceExtWrapper: DeviceExt{prebid: &prebidContent1, prebidDirty: true},
-			expectedRequest:         openrtb2.BidRequest{Device: &openrtb2.Device{Ext: json.RawMessage(`{"prebid":{"interstitial":{"minwidthperc":1,"minheightperc":0}}}`)}},
+			requestDeviceExtWrapper: DeviceExt{prebid: &prebidContent1, prebidDirty: true, cdep: "1", cdepDirty: true},
+			expectedRequest:         openrtb2.BidRequest{Device: &openrtb2.Device{Ext: json.RawMessage(`{"cdep":"1","prebid":{"interstitial":{"minwidthperc":1,"minheightperc":0}}}`)}},
 		},
 		{
 			description:             "Nil - Dirty - No Change",
 			request:                 openrtb2.BidRequest{},
-			requestDeviceExtWrapper: DeviceExt{prebid: nil, prebidDirty: true},
+			requestDeviceExtWrapper: DeviceExt{prebid: nil, prebidDirty: true, cdep: "", cdepDirty: true},
 			expectedRequest:         openrtb2.BidRequest{},
 		},
 		{
@@ -785,37 +785,37 @@ func TestRebuildDeviceExt(t *testing.T) {
 		{
 			description:             "Empty - Dirty",
 			request:                 openrtb2.BidRequest{Device: &openrtb2.Device{}},
-			requestDeviceExtWrapper: DeviceExt{prebid: &prebidContent1, prebidDirty: true},
-			expectedRequest:         openrtb2.BidRequest{Device: &openrtb2.Device{Ext: json.RawMessage(`{"prebid":{"interstitial":{"minwidthperc":1,"minheightperc":0}}}`)}},
+			requestDeviceExtWrapper: DeviceExt{prebid: &prebidContent1, prebidDirty: true, cdep: "1", cdepDirty: true},
+			expectedRequest:         openrtb2.BidRequest{Device: &openrtb2.Device{Ext: json.RawMessage(`{"cdep":"1","prebid":{"interstitial":{"minwidthperc":1,"minheightperc":0}}}`)}},
 		},
 		{
 			description:             "Empty - Dirty - No Change",
 			request:                 openrtb2.BidRequest{Device: &openrtb2.Device{}},
-			requestDeviceExtWrapper: DeviceExt{prebid: nil, prebidDirty: true},
+			requestDeviceExtWrapper: DeviceExt{prebid: nil, prebidDirty: true, cdep: "", cdepDirty: true},
 			expectedRequest:         openrtb2.BidRequest{Device: &openrtb2.Device{}},
 		},
 		{
 			description:             "Populated - Not Dirty",
-			request:                 openrtb2.BidRequest{Device: &openrtb2.Device{Ext: json.RawMessage(`{"prebid":{"interstitial":{"minwidthperc":1,"minheightperc":0}}}`)}},
+			request:                 openrtb2.BidRequest{Device: &openrtb2.Device{Ext: json.RawMessage(`{"cdep":"1","prebid":{"interstitial":{"minwidthperc":1,"minheightperc":0}}}`)}},
 			requestDeviceExtWrapper: DeviceExt{},
-			expectedRequest:         openrtb2.BidRequest{Device: &openrtb2.Device{Ext: json.RawMessage(`{"prebid":{"interstitial":{"minwidthperc":1,"minheightperc":0}}}`)}},
+			expectedRequest:         openrtb2.BidRequest{Device: &openrtb2.Device{Ext: json.RawMessage(`{"cdep":"1","prebid":{"interstitial":{"minwidthperc":1,"minheightperc":0}}}`)}},
 		},
 		{
 			description:             "Populated - Dirty",
-			request:                 openrtb2.BidRequest{Device: &openrtb2.Device{Ext: json.RawMessage(`{"prebid":{"interstitial":{"minwidthperc":1,"minheightperc":0}}}`)}},
-			requestDeviceExtWrapper: DeviceExt{prebid: &prebidContent2, prebidDirty: true},
-			expectedRequest:         openrtb2.BidRequest{Device: &openrtb2.Device{Ext: json.RawMessage(`{"prebid":{"interstitial":{"minwidthperc":2,"minheightperc":0}}}`)}},
+			request:                 openrtb2.BidRequest{Device: &openrtb2.Device{Ext: json.RawMessage(`{"cdep":"1","prebid":{"interstitial":{"minwidthperc":1,"minheightperc":0}}}`)}},
+			requestDeviceExtWrapper: DeviceExt{prebid: &prebidContent2, prebidDirty: true, cdep: "2", cdepDirty: true},
+			expectedRequest:         openrtb2.BidRequest{Device: &openrtb2.Device{Ext: json.RawMessage(`{"cdep":"2","prebid":{"interstitial":{"minwidthperc":2,"minheightperc":0}}}`)}},
 		},
 		{
 			description:             "Populated - Dirty - No Change",
-			request:                 openrtb2.BidRequest{Device: &openrtb2.Device{Ext: json.RawMessage(`{"prebid":{"interstitial":{"minwidthperc":1,"minheightperc":0}}}`)}},
-			requestDeviceExtWrapper: DeviceExt{prebid: &prebidContent1, prebidDirty: true},
-			expectedRequest:         openrtb2.BidRequest{Device: &openrtb2.Device{Ext: json.RawMessage(`{"prebid":{"interstitial":{"minwidthperc":1,"minheightperc":0}}}`)}},
+			request:                 openrtb2.BidRequest{Device: &openrtb2.Device{Ext: json.RawMessage(`{"cdep":"1","prebid":{"interstitial":{"minwidthperc":1,"minheightperc":0}}}`)}},
+			requestDeviceExtWrapper: DeviceExt{prebid: &prebidContent1, prebidDirty: true, cdep: "1", cdepDirty: true},
+			expectedRequest:         openrtb2.BidRequest{Device: &openrtb2.Device{Ext: json.RawMessage(`{"cdep":"1","prebid":{"interstitial":{"minwidthperc":1,"minheightperc":0}}}`)}},
 		},
 		{
 			description:             "Populated - Dirty - Cleared",
-			request:                 openrtb2.BidRequest{Device: &openrtb2.Device{Ext: json.RawMessage(`{"prebid":{"interstitial":{"minwidthperc":1,"minheightperc":0}}}`)}},
-			requestDeviceExtWrapper: DeviceExt{prebid: nil, prebidDirty: true},
+			request:                 openrtb2.BidRequest{Device: &openrtb2.Device{Ext: json.RawMessage(`{"cdep":"1","prebid":{"interstitial":{"minwidthperc":1,"minheightperc":0}}}`)}},
+			requestDeviceExtWrapper: DeviceExt{prebid: nil, prebidDirty: true, cdep: "", cdepDirty: true},
 			expectedRequest:         openrtb2.BidRequest{Device: &openrtb2.Device{}},
 		},
 	}
@@ -1014,6 +1014,8 @@ func TestCloneDeviceExt(t *testing.T) {
 				prebid: &ExtDevicePrebid{
 					Interstitial: &ExtDeviceInt{MinWidthPerc: 65.0, MinHeightPerc: 75.0},
 				},
+				cdep:      "1",
+				cdepDirty: true,
 			},
 			devExtCopy: &DeviceExt{
 				ext:      map[string]json.RawMessage{"A": json.RawMessage(`{}`), "B": json.RawMessage(`{"foo":"bar"}`)},
@@ -1021,6 +1023,8 @@ func TestCloneDeviceExt(t *testing.T) {
 				prebid: &ExtDevicePrebid{
 					Interstitial: &ExtDeviceInt{MinWidthPerc: 65.0, MinHeightPerc: 75.0},
 				},
+				cdep:      "1",
+				cdepDirty: true,
 			},
 			mutator: func(t *testing.T, devExt *DeviceExt) {},
 		},
@@ -1032,6 +1036,8 @@ func TestCloneDeviceExt(t *testing.T) {
 				prebid: &ExtDevicePrebid{
 					Interstitial: &ExtDeviceInt{MinWidthPerc: 65.0, MinHeightPerc: 75.0},
 				},
+				cdep:      "1",
+				cdepDirty: true,
 			},
 			devExtCopy: &DeviceExt{
 				ext:      map[string]json.RawMessage{"A": json.RawMessage(`{}`), "B": json.RawMessage(`{"foo":"bar"}`)},
@@ -1039,6 +1045,8 @@ func TestCloneDeviceExt(t *testing.T) {
 				prebid: &ExtDevicePrebid{
 					Interstitial: &ExtDeviceInt{MinWidthPerc: 65, MinHeightPerc: 75},
 				},
+				cdep:      "1",
+				cdepDirty: true,
 			},
 			mutator: func(t *testing.T, devExt *DeviceExt) {
 				devExt.ext["A"] = json.RawMessage(`"string"`)
@@ -1047,6 +1055,8 @@ func TestCloneDeviceExt(t *testing.T) {
 				devExt.prebid.Interstitial.MinHeightPerc = 55
 				devExt.prebid.Interstitial = &ExtDeviceInt{MinWidthPerc: 80}
 				devExt.prebid = nil
+				devExt.cdep = ""
+				devExt.cdepDirty = true
 			},
 		},
 	}
@@ -2069,7 +2079,7 @@ func TestRebuildRegExt(t *testing.T) {
 		{
 			name:    "req_regs_nil_-_dirty_and_different_-_change",
 			request: openrtb2.BidRequest{},
-			regExt:  RegExt{dsa: &ExtRegsDSA{Required: 1}, dsaDirty: true, gdpr: ptrutil.ToPtr[int8](1), gdprDirty: true, usPrivacy: strA, usPrivacyDirty: true},
+			regExt:  RegExt{dsa: &ExtRegsDSA{Required: ptrutil.ToPtr[int8](1)}, dsaDirty: true, gdpr: ptrutil.ToPtr[int8](1), gdprDirty: true, usPrivacy: strA, usPrivacyDirty: true},
 			expectedRequest: openrtb2.BidRequest{
 				Regs: &openrtb2.Regs{
 					Ext: json.RawMessage(`{"dsa":{"dsarequired":1},"gdpr":1,"us_privacy":"a"}`),
@@ -2085,7 +2095,7 @@ func TestRebuildRegExt(t *testing.T) {
 		{
 			name:    "req_regs_ext_nil_-_dirty_and_different_-_change",
 			request: openrtb2.BidRequest{Regs: &openrtb2.Regs{}},
-			regExt:  RegExt{dsa: &ExtRegsDSA{Required: 1}, dsaDirty: true, gdpr: ptrutil.ToPtr[int8](1), gdprDirty: true, usPrivacy: strA, usPrivacyDirty: true},
+			regExt:  RegExt{dsa: &ExtRegsDSA{Required: ptrutil.ToPtr[int8](1)}, dsaDirty: true, gdpr: ptrutil.ToPtr[int8](1), gdprDirty: true, usPrivacy: strA, usPrivacyDirty: true},
 			expectedRequest: openrtb2.BidRequest{
 				Regs: &openrtb2.Regs{
 					Ext: json.RawMessage(`{"dsa":{"dsarequired":1},"gdpr":1,"us_privacy":"a"}`),
@@ -2101,13 +2111,13 @@ func TestRebuildRegExt(t *testing.T) {
 		{
 			name:            "req_regs_dsa_populated_-_dirty_and_different-_change",
 			request:         openrtb2.BidRequest{Regs: &openrtb2.Regs{Ext: json.RawMessage(`{"dsa":{"dsarequired":1}}`)}},
-			regExt:          RegExt{dsa: &ExtRegsDSA{Required: 2}, dsaDirty: true},
+			regExt:          RegExt{dsa: &ExtRegsDSA{Required: ptrutil.ToPtr[int8](2)}, dsaDirty: true},
 			expectedRequest: openrtb2.BidRequest{Regs: &openrtb2.Regs{Ext: json.RawMessage(`{"dsa":{"dsarequired":2}}`)}},
 		},
 		{
 			name:            "req_regs_dsa_populated_-_dirty_and_same_-_no_change",
 			request:         openrtb2.BidRequest{Regs: &openrtb2.Regs{Ext: json.RawMessage(`{"dsa":{"dsarequired":1}}`)}},
-			regExt:          RegExt{dsa: &ExtRegsDSA{Required: 1}, dsaDirty: true},
+			regExt:          RegExt{dsa: &ExtRegsDSA{Required: ptrutil.ToPtr[int8](1)}, dsaDirty: true},
 			expectedRequest: openrtb2.BidRequest{Regs: &openrtb2.Regs{Ext: json.RawMessage(`{"dsa":{"dsarequired":1}}`)}},
 		},
 		{
@@ -2215,7 +2225,7 @@ func TestRegExtUnmarshal(t *testing.T) {
 			regExt:  &RegExt{},
 			extJson: json.RawMessage(`{"dsa":{"dsarequired":1}}`),
 			expectDSA: &ExtRegsDSA{
-				Required: 1,
+				Required: ptrutil.ToPtr[int8](1),
 			},
 			expectError: false,
 		},
@@ -2224,7 +2234,7 @@ func TestRegExtUnmarshal(t *testing.T) {
 			regExt:  &RegExt{},
 			extJson: json.RawMessage(`{"dsa":{"dsarequired":""}}`),
 			expectDSA: &ExtRegsDSA{
-				Required: 0,
+				Required: ptrutil.ToPtr[int8](0),
 			},
 			expectError: true,
 		},
@@ -2299,7 +2309,7 @@ func TestRegExtGetDSASetDSA(t *testing.T) {
 	assert.False(t, regExt.Dirty())
 
 	dsa := &ExtRegsDSA{
-		Required: 2,
+		Required: ptrutil.ToPtr[int8](2),
 	}
 	regExt.SetDSA(dsa)
 	assert.True(t, regExt.Dirty())
diff --git a/router/aspects/request_timeout_handler_test.go b/router/aspects/request_timeout_handler_test.go
index 4ece1420..f77d48aa 100644
--- a/router/aspects/request_timeout_handler_test.go
+++ b/router/aspects/request_timeout_handler_test.go
@@ -101,7 +101,7 @@ func ExecuteAspectRequest(t *testing.T, timeInQueue string, reqTimeout string, s
 		req.Header.Set(reqTimeoutHeaderName, reqTimeout)
 	}
 
-	customHeaders := config.RequestTimeoutHeaders{reqTimeInQueueHeaderName, reqTimeoutHeaderName}
+	customHeaders := config.RequestTimeoutHeaders{RequestTimeInQueue: reqTimeInQueueHeaderName, RequestTimeoutInQueue: reqTimeoutHeaderName}
 
 	metrics := &metrics.MetricsEngineMock{}
 
diff --git a/router/bidder_params_tests/appnexus.json b/router/bidder_params_tests/appnexus.json
new file mode 100644
index 00000000..11dedd41
--- /dev/null
+++ b/router/bidder_params_tests/appnexus.json
@@ -0,0 +1,27 @@
+{
+    "$schema": "http://json-schema.org/draft-04/schema#",
+    "title": "Sample schema",
+    "description": "A sample schema to test the bidder/params endpoint",
+    "type": "object",
+    "properties": {
+        "integer_param": {
+            "type": "integer",
+            "minimum": 1,
+            "description": "A customer id"
+        },
+        "string_param_1": {
+            "type": "string",
+            "minLength": 1,
+            "description": "Text with blanks in between"
+        },
+        "string_param_2": {
+            "type": "string",
+            "minLength": 1,
+            "description": "Text_with_no_blanks_in_between"
+        }
+    },
+    "required": [
+        "integer_param",
+        "string_param_2"
+    ]
+}
diff --git a/router/router_test.go b/router/router_test.go
index cc2f077e..866a8440 100644
--- a/router/router_test.go
+++ b/router/router_test.go
@@ -7,6 +7,7 @@ import (
 	"os"
 	"testing"
 
+	jsoniter "github.com/json-iterator/go"
 	"github.com/prebid/prebid-server/v2/config"
 	"github.com/prebid/prebid-server/v2/openrtb_ext"
 	"github.com/prebid/prebid-server/v2/util/jsonutil"
@@ -18,6 +19,11 @@ const adapterDirectory = "../adapters"
 
 type testValidator struct{}
 
+func TestMain(m *testing.M) {
+	jsoniter.RegisterExtension(&jsonutil.RawMessageExtension{})
+	os.Exit(m.Run())
+}
+
 func (validator *testValidator) Validate(name openrtb_ext.BidderName, ext json.RawMessage) error {
 	return nil
 }
@@ -275,3 +281,23 @@ func TestValidateDefaultAliases(t *testing.T) {
 		}
 	}
 }
+
+func TestBidderParamsCompactedOutput(t *testing.T) {
+	expectedFormattedResponse := `{"appnexus":{"$schema":"http://json-schema.org/draft-04/schema#","title":"Sample schema","description":"A sample schema to test the bidder/params endpoint","type":"object","properties":{"integer_param":{"type":"integer","minimum":1,"description":"A customer id"},"string_param_1":{"type":"string","minLength":1,"description":"Text with blanks in between"},"string_param_2":{"type":"string","minLength":1,"description":"Text_with_no_blanks_in_between"}},"required":["integer_param","string_param_2"]}}`
+
+	// Setup
+	inSchemaDirectory := "bidder_params_tests"
+	paramsValidator, err := openrtb_ext.NewBidderParamsValidator(inSchemaDirectory)
+	assert.NoError(t, err, "Error initialing validator")
+
+	handler := newJsonDirectoryServer(inSchemaDirectory, paramsValidator, nil, nil)
+	recorder := httptest.NewRecorder()
+	request, err := http.NewRequest("GET", "/bidder/params", nil)
+	assert.NoError(t, err, "Error creating request")
+
+	// Run
+	handler(recorder, request, nil)
+
+	// Assertions
+	assert.Equal(t, expectedFormattedResponse, recorder.Body.String())
+}
diff --git a/stored_requests/backends/file_fetcher/fetcher_test.go b/stored_requests/backends/file_fetcher/fetcher_test.go
index 0155c1aa..b4a73870 100644
--- a/stored_requests/backends/file_fetcher/fetcher_test.go
+++ b/stored_requests/backends/file_fetcher/fetcher_test.go
@@ -36,7 +36,7 @@ func TestAccountFetcher(t *testing.T) {
 	_, errs = fetcher.FetchAccount(context.Background(), json.RawMessage(`{"events_enabled":true}`), "nonexistent")
 	assertErrorCount(t, 1, errs)
 	assert.Error(t, errs[0])
-	assert.Equal(t, stored_requests.NotFoundError{"nonexistent", "Account"}, errs[0])
+	assert.Equal(t, stored_requests.NotFoundError{ID: "nonexistent", DataType: "Account"}, errs[0])
 
 	_, errs = fetcher.FetchAccount(context.Background(), json.RawMessage(`{"events_enabled"}`), "valid")
 	assertErrorCount(t, 1, errs)
diff --git a/util/jsonutil/jsonutil_test.go b/util/jsonutil/jsonutil_test.go
index 09fb6727..96632d62 100644
--- a/util/jsonutil/jsonutil_test.go
+++ b/util/jsonutil/jsonutil_test.go
@@ -1,10 +1,15 @@
 package jsonutil
 
 import (
+	"bytes"
+	"encoding/json"
 	"errors"
 	"strings"
 	"testing"
+	"unsafe"
 
+	jsoniter "github.com/json-iterator/go"
+	"github.com/modern-go/reflect2"
 	"github.com/stretchr/testify/assert"
 )
 
@@ -240,3 +245,80 @@ func TestTryExtractErrorMessage(t *testing.T) {
 		})
 	}
 }
+
+func TestCreateEncoder(t *testing.T) {
+	testCases := []struct {
+		desc               string
+		inType             reflect2.Type
+		expectedValEncoder jsoniter.ValEncoder
+	}{
+		{
+			desc:               "With_extension",
+			inType:             reflect2.TypeOfPtr((*jsoniter.Any)(nil)).Elem(),
+			expectedValEncoder: nil,
+		},
+		{
+			desc:               "No_extension",
+			inType:             reflect2.TypeOfPtr(&json.RawMessage{}).Elem(),
+			expectedValEncoder: &rawMessageCodec{},
+		},
+	}
+
+	for _, tc := range testCases {
+		t.Run(tc.desc, func(t *testing.T) {
+			extension := &RawMessageExtension{}
+			encoder := extension.CreateEncoder(tc.inType)
+			assert.IsType(t, encoder, tc.expectedValEncoder)
+		})
+	}
+}
+
+func TestEncode(t *testing.T) {
+	jsonBlob := json.RawMessage(`{
+  "properties": {
+    "string": "Blanks spaces in between words to not be removed if compacted",
+    "integer": 5,
+    "string_array": [
+      "string array elem one",
+      "string array elem two"
+    ]
+  }
+}`)
+
+	t.Run(
+		"Nil_pointer",
+		func(t *testing.T) {
+			// set test
+			encoder := &rawMessageCodec{}
+			output := bytes.NewBuffer([]byte{})
+			stream := jsoniter.NewStream(jsonConfigValidationOn, output, len(jsonBlob))
+
+			// run
+			encoder.Encode(nil, stream)
+
+			// assertions
+			assert.Equal(t, "", output.String())
+			assert.Equal(t, true, encoder.IsEmpty(nil))
+		},
+	)
+	t.Run(
+		"json.RawMessage_compact_JSON",
+		func(t *testing.T) {
+			// set test
+			encoder := &rawMessageCodec{}
+			output := bytes.NewBuffer([]byte{})
+			stream := jsoniter.NewStream(jsonConfigValidationOn, output, len(jsonBlob))
+
+			// run
+			encoder.Encode(unsafe.Pointer(&jsonBlob), stream)
+
+			// assertions
+			assert.Equal(
+				t,
+				`{"properties":{"string":"Blanks spaces in between words to not be removed if compacted","integer":5,"string_array":["string array elem one","string array elem two"]}}`,
+				output.String(),
+			)
+			assert.Equal(t, false, encoder.IsEmpty(unsafe.Pointer(&jsonBlob)))
+		},
+	)
+}
diff --git a/util/stringutil/stringutil_test.go b/util/stringutil/stringutil_test.go
index 94988ee4..a7aa0010 100644
--- a/util/stringutil/stringutil_test.go
+++ b/util/stringutil/stringutil_test.go
@@ -30,7 +30,7 @@ func TestStrToInt8Slice(t *testing.T) {
 			in:   "malformed",
 			expected: testOutput{
 				arr: nil,
-				err: &strconv.NumError{"ParseInt", "malformed", strconv.ErrSyntax},
+				err: &strconv.NumError{Func: "ParseInt", Num: "malformed", Err: strconv.ErrSyntax},
 			},
 		},
 		{
@@ -38,7 +38,7 @@ func TestStrToInt8Slice(t *testing.T) {
 			in:   "malformed,2,malformed",
 			expected: testOutput{
 				arr: nil,
-				err: &strconv.NumError{"ParseInt", "malformed", strconv.ErrSyntax},
+				err: &strconv.NumError{Func: "ParseInt", Num: "malformed", Err: strconv.ErrSyntax},
 			},
 		},
 		{
@@ -46,7 +46,7 @@ func TestStrToInt8Slice(t *testing.T) {
 			in:   "128",
 			expected: testOutput{
 				arr: nil,
-				err: &strconv.NumError{"ParseInt", "128", strconv.ErrRange},
+				err: &strconv.NumError{Func: "ParseInt", Num: "128", Err: strconv.ErrRange},
 			},
 		},
 		{