Repository: prebid/prebid-server
Pull request #3498 (2024-02-14 06:51:27)

New Adapter: Theadx
Theadx server adaptor added.

github-actions[bot]: ## Code coverage summary 
Note: 
- Prebid team doesn't anticipate tests covering code paths that might result in marshal and unmarshal errors 
- Coverage summary encompasses all commits leading up to the latest one, 2b954b712457629b271182082254d88939e06ab6 
#### theadx 
Refer [here](https://htmlpreview.github.io/?https://github.com/prebid/prebid-server/coverage-preview/.github/preview/7897540156_1707893532/theadx.html) for heat map coverage report 
``` 
github.com/prebid/prebid-server/v2/adapters/theadx/theadx.go:20:	Builder			100.0%
github.com/prebid/prebid-server/v2/adapters/theadx/theadx.go:26:	getHeaders		76.9%
github.com/prebid/prebid-server/v2/adapters/theadx/theadx.go:49:	MakeRequests		70.0%
github.com/prebid/prebid-server/v2/adapters/theadx/theadx.go:92:	MakeBids		90.9%
github.com/prebid/prebid-server/v2/adapters/theadx/theadx.go:136:	getMediaTypeForBid	83.3%
total:									(statements)		81.0%

 ``` 

github-actions[bot]: ## Code coverage summary 
Note: 
- Prebid team doesn't anticipate tests covering code paths that might result in marshal and unmarshal errors 
- Coverage summary encompasses all commits leading up to the latest one, f1b72243e308a88b2d37711d69ae7a0c473ba6d4 
#### theadx 
Refer [here](https://htmlpreview.github.io/?https://github.com/prebid/prebid-server/coverage-preview/.github/preview/7911908017_1707977946/theadx.html) for heat map coverage report 
``` 
github.com/prebid/prebid-server/v2/adapters/theadx/theadx.go:20:	Builder			100.0%
github.com/prebid/prebid-server/v2/adapters/theadx/theadx.go:26:	getHeaders		76.9%
github.com/prebid/prebid-server/v2/adapters/theadx/theadx.go:49:	MakeRequests		70.0%
github.com/prebid/prebid-server/v2/adapters/theadx/theadx.go:92:	MakeBids		90.9%
github.com/prebid/prebid-server/v2/adapters/theadx/theadx.go:136:	getMediaTypeForBid	83.3%
total:									(statements)		81.0%

 ``` 

github-actions[bot]: ## Code coverage summary 
Note: 
- Prebid team doesn't anticipate tests covering code paths that might result in marshal and unmarshal errors 
- Coverage summary encompasses all commits leading up to the latest one, f0b27ac651d4dfc389081a43933b33b06f06669e 
#### theadx 
Refer [here](https://htmlpreview.github.io/?https://github.com/prebid/prebid-server/coverage-preview/.github/preview/7911915447_1707978002/theadx.html) for heat map coverage report 
``` 
github.com/prebid/prebid-server/v2/adapters/theadx/theadx.go:20:	Builder			100.0%
github.com/prebid/prebid-server/v2/adapters/theadx/theadx.go:26:	getHeaders		76.9%
github.com/prebid/prebid-server/v2/adapters/theadx/theadx.go:49:	MakeRequests		70.0%
github.com/prebid/prebid-server/v2/adapters/theadx/theadx.go:92:	MakeBids		90.9%
github.com/prebid/prebid-server/v2/adapters/theadx/theadx.go:136:	getMediaTypeForBid	83.3%
total:									(statements)		81.0%

 ``` 

github-actions[bot]: ## Code coverage summary 
Note: 
- Prebid team doesn't anticipate tests covering code paths that might result in marshal and unmarshal errors 
- Coverage summary encompasses all commits leading up to the latest one, dde7749dba69f5ec4e562c33575cd14c1d06728e 
#### theadx 
Refer [here](https://htmlpreview.github.io/?https://github.com/prebid/prebid-server/coverage-preview/.github/preview/7911927265_1707978079/theadx.html) for heat map coverage report 
``` 
github.com/prebid/prebid-server/v2/adapters/theadx/theadx.go:20:	Builder			100.0%
github.com/prebid/prebid-server/v2/adapters/theadx/theadx.go:26:	getHeaders		76.9%
github.com/prebid/prebid-server/v2/adapters/theadx/theadx.go:49:	MakeRequests		70.0%
github.com/prebid/prebid-server/v2/adapters/theadx/theadx.go:92:	MakeBids		90.9%
github.com/prebid/prebid-server/v2/adapters/theadx/theadx.go:136:	getMediaTypeForBid	83.3%
total:									(statements)		81.0%

 ``` 

github-actions[bot]: ## Code coverage summary 
Note: 
- Prebid team doesn't anticipate tests covering code paths that might result in marshal and unmarshal errors 
- Coverage summary encompasses all commits leading up to the latest one, 7811aaf4e9d2856c445ff3903401c5cde01d33c8 
#### theadx 
Refer [here](https://htmlpreview.github.io/?https://github.com/prebid/prebid-server/coverage-preview/.github/preview/7912141733_1707979733/theadx.html) for heat map coverage report 
``` 
github.com/prebid/prebid-server/v2/adapters/theadx/theadx.go:20:	Builder			100.0%
github.com/prebid/prebid-server/v2/adapters/theadx/theadx.go:26:	getHeaders		76.9%
github.com/prebid/prebid-server/v2/adapters/theadx/theadx.go:49:	MakeRequests		70.0%
github.com/prebid/prebid-server/v2/adapters/theadx/theadx.go:92:	MakeBids		90.9%
github.com/prebid/prebid-server/v2/adapters/theadx/theadx.go:136:	getMediaTypeForBid	83.3%
total:									(statements)		81.0%

 ``` 

github-actions[bot]: ## Code coverage summary 
Note: 
- Prebid team doesn't anticipate tests covering code paths that might result in marshal and unmarshal errors 
- Coverage summary encompasses all commits leading up to the latest one, 7811aaf4e9d2856c445ff3903401c5cde01d33c8 
#### theadx 
Refer [here](https://htmlpreview.github.io/?https://github.com/prebid/prebid-server/coverage-preview/.github/preview/8000128396_1708581625/theadx.html) for heat map coverage report 
``` 
github.com/prebid/prebid-server/v2/adapters/theadx/theadx.go:20:	Builder			100.0%
github.com/prebid/prebid-server/v2/adapters/theadx/theadx.go:26:	getHeaders		76.9%
github.com/prebid/prebid-server/v2/adapters/theadx/theadx.go:49:	MakeRequests		70.0%
github.com/prebid/prebid-server/v2/adapters/theadx/theadx.go:92:	MakeBids		90.9%
github.com/prebid/prebid-server/v2/adapters/theadx/theadx.go:136:	getMediaTypeForBid	83.3%
total:									(statements)		81.0%

 ``` 

github-actions[bot]: ## Code coverage summary 
Note: 
- Prebid team doesn't anticipate tests covering code paths that might result in marshal and unmarshal errors 
- Coverage summary encompasses all commits leading up to the latest one, 7811aaf4e9d2856c445ff3903401c5cde01d33c8 
#### theadx 
Refer [here](https://htmlpreview.github.io/?https://github.com/prebid/prebid-server/coverage-preview/.github/preview/8016200561_1708674833/theadx.html) for heat map coverage report 
``` 
github.com/prebid/prebid-server/v2/adapters/theadx/theadx.go:20:	Builder			100.0%
github.com/prebid/prebid-server/v2/adapters/theadx/theadx.go:26:	getHeaders		76.9%
github.com/prebid/prebid-server/v2/adapters/theadx/theadx.go:49:	MakeRequests		70.0%
github.com/prebid/prebid-server/v2/adapters/theadx/theadx.go:92:	MakeBids		90.9%
github.com/prebid/prebid-server/v2/adapters/theadx/theadx.go:136:	getMediaTypeForBid	83.3%
total:									(statements)		81.0%

 ``` 

github-actions[bot]: ## Code coverage summary 
Note: 
- Prebid team doesn't anticipate tests covering code paths that might result in marshal and unmarshal errors 
- Coverage summary encompasses all commits leading up to the latest one, 591be64d2d0fc6a7cd8b2337f6c9a08d0b32ed9e 
#### theadx 
Refer [here](https://htmlpreview.github.io/?https://github.com/prebid/prebid-server/coverage-preview/.github/preview/8044708153_1708929362/theadx.html) for heat map coverage report 
``` 
github.com/prebid/prebid-server/v2/adapters/theadx/theadx.go:20:	Builder			100.0%
github.com/prebid/prebid-server/v2/adapters/theadx/theadx.go:26:	getHeaders		76.9%
github.com/prebid/prebid-server/v2/adapters/theadx/theadx.go:49:	MakeRequests		70.0%
github.com/prebid/prebid-server/v2/adapters/theadx/theadx.go:92:	MakeBids		90.9%
github.com/prebid/prebid-server/v2/adapters/theadx/theadx.go:136:	getMediaTypeForBid	83.3%
total:									(statements)		81.0%

 ``` 

onkarvhanumante: @mustafakemal16  should create docs PR in https://github.com/prebid/prebid.github.io repo. This PR is needed for include ResetDigital on prebid server docs - https://prebid.org/product-suite/prebid-server/
github-actions[bot]: ## Code coverage summary 
Note: 
- Prebid team doesn't anticipate tests covering code paths that might result in marshal and unmarshal errors 
- Coverage summary encompasses all commits leading up to the latest one, 2f8ff1be5cd514af20aa4070a74e0500eda06a2e 
#### theadx 
Refer [here](https://htmlpreview.github.io/?https://github.com/prebid/prebid-server/coverage-preview/.github/preview/8136591431_1709534617/theadx.html) for heat map coverage report 
``` 
github.com/prebid/prebid-server/v2/adapters/theadx/theadx.go:20:	Builder			100.0%
github.com/prebid/prebid-server/v2/adapters/theadx/theadx.go:26:	getHeaders		76.9%
github.com/prebid/prebid-server/v2/adapters/theadx/theadx.go:49:	MakeRequests		70.0%
github.com/prebid/prebid-server/v2/adapters/theadx/theadx.go:92:	MakeBids		90.9%
github.com/prebid/prebid-server/v2/adapters/theadx/theadx.go:136:	getMediaTypeForBid	83.3%
total:									(statements)		81.0%

 ``` 

github-actions[bot]: ## Code coverage summary 
Note: 
- Prebid team doesn't anticipate tests covering code paths that might result in marshal and unmarshal errors 
- Coverage summary encompasses all commits leading up to the latest one, 2615d881934d269fe3e50fbe0e28b8b62bde1eee 
#### theadx 
Refer [here](https://htmlpreview.github.io/?https://github.com/prebid/prebid-server/coverage-preview/.github/preview/8137335608_1709539930/theadx.html) for heat map coverage report 
``` 
github.com/prebid/prebid-server/v2/adapters/theadx/theadx.go:20:	Builder			100.0%
github.com/prebid/prebid-server/v2/adapters/theadx/theadx.go:26:	getHeaders		76.9%
github.com/prebid/prebid-server/v2/adapters/theadx/theadx.go:49:	MakeRequests		70.0%
github.com/prebid/prebid-server/v2/adapters/theadx/theadx.go:92:	MakeBids		90.9%
github.com/prebid/prebid-server/v2/adapters/theadx/theadx.go:136:	getMediaTypeForBid	83.3%
total:									(statements)		81.0%

 ``` 

mustafakemal16: > @mustafakemal16 should create docs PR in https://github.com/prebid/prebid.github.io repo. This PR is needed for include Theadx on prebid server docs - https://prebid.org/product-suite/prebid-server/

https://github.com/prebid/prebid.github.io/pull/5184

Patch:
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/theadx/theadx.go b/adapters/theadx/theadx.go
new file mode 100644
index 00000000..5b3f019a
--- /dev/null
+++ b/adapters/theadx/theadx.go
@@ -0,0 +1,148 @@
+package theadx
+
+import (
+	"encoding/json"
+	"fmt"
+	"net/http"
+
+	"github.com/prebid/openrtb/v20/openrtb2"
+	"github.com/prebid/prebid-server/v2/adapters"
+	"github.com/prebid/prebid-server/v2/config"
+	"github.com/prebid/prebid-server/v2/errortypes"
+	"github.com/prebid/prebid-server/v2/openrtb_ext"
+)
+
+type adapter struct {
+	endpoint string
+}
+
+// Builder builds a new instance of the theadx adapter for the given bidder with the given config.
+func Builder(bidderName openrtb_ext.BidderName, config config.Adapter, server config.Server) (adapters.Bidder, error) {
+	bidder := &adapter{
+		endpoint: config.Endpoint,
+	}
+	return bidder, nil
+}
+func getHeaders(request *openrtb2.BidRequest) http.Header {
+	headers := http.Header{}
+	headers.Add("Content-Type", "application/json;charset=utf-8")
+	headers.Add("Accept", "application/json")
+	headers.Add("X-Openrtb-Version", "2.5")
+	headers.Add("X-TEST", "1")
+
+	if request.Device != nil {
+		if len(request.Device.UA) > 0 {
+			headers.Add("X-Device-User-Agent", request.Device.UA)
+		}
+
+		if len(request.Device.IPv6) > 0 {
+			headers.Add("X-Real-IP", request.Device.IPv6)
+		}
+
+		if len(request.Device.IP) > 0 {
+			headers.Add("X-Real-IP", request.Device.IP)
+		}
+	}
+
+	return headers
+}
+func (a *adapter) MakeRequests(request *openrtb2.BidRequest, requestInfo *adapters.ExtraRequestInfo) ([]*adapters.RequestData, []error) {
+	var errors []error
+	var validImps = make([]openrtb2.Imp, 0, len(request.Imp))
+
+	for _, imp := range request.Imp {
+		var bidderExt adapters.ExtImpBidder
+		if err := json.Unmarshal(imp.Ext, &bidderExt); err != nil {
+			errors = append(errors, &errortypes.BadInput{
+				Message: err.Error(),
+			})
+			continue
+		}
+
+		var theadxImpExt openrtb_ext.ExtImpTheadx
+		if err := json.Unmarshal(bidderExt.Bidder, &theadxImpExt); err != nil {
+			errors = append(errors, &errortypes.BadInput{
+				Message: err.Error(),
+			})
+			continue
+		}
+
+		imp.TagID = theadxImpExt.TagID.String()
+		validImps = append(validImps, imp)
+	}
+
+	request.Imp = validImps
+
+	requestJSON, err := json.Marshal(request)
+	if err != nil {
+		errors = append(errors, err)
+		return nil, errors
+	}
+
+	requestData := &adapters.RequestData{
+		Method:  "POST",
+		Uri:     a.endpoint,
+		Body:    requestJSON,
+		Headers: getHeaders(request),
+	}
+
+	return []*adapters.RequestData{requestData}, errors
+}
+
+func (a *adapter) MakeBids(request *openrtb2.BidRequest, requestData *adapters.RequestData, responseData *adapters.ResponseData) (*adapters.BidderResponse, []error) {
+	if responseData.StatusCode == http.StatusNoContent {
+		return nil, nil
+	}
+
+	if responseData.StatusCode == http.StatusBadRequest {
+		err := &errortypes.BadInput{
+			Message: fmt.Sprintf("Unexpected status code: %d. Run with request.debug = 1 for more info", responseData.StatusCode),
+		}
+		return nil, []error{err}
+	}
+
+	if responseData.StatusCode != http.StatusOK {
+		err := &errortypes.BadServerResponse{
+			Message: fmt.Sprintf("Unexpected status code: %d.", responseData.StatusCode),
+		}
+		return nil, []error{err}
+	}
+
+	var response openrtb2.BidResponse
+	if err := json.Unmarshal(responseData.Body, &response); err != nil {
+		return nil, []error{err}
+	}
+
+	bidResponse := adapters.NewBidderResponseWithBidsCapacity(len(request.Imp))
+	bidResponse.Currency = response.Cur
+	var errors []error
+	for _, seatBid := range response.SeatBid {
+		for i, bid := range seatBid.Bid {
+			bidType, err := getMediaTypeForBid(bid)
+			if err != nil {
+				errors = append(errors, err)
+				continue
+			}
+			bidResponse.Bids = append(bidResponse.Bids, &adapters.TypedBid{
+				Bid:     &seatBid.Bid[i],
+				BidType: bidType,
+			})
+		}
+	}
+
+	return bidResponse, errors
+}
+
+func getMediaTypeForBid(bid openrtb2.Bid) (openrtb_ext.BidType, error) {
+	if bid.Ext != nil {
+		var bidExt openrtb_ext.ExtBid
+		err := json.Unmarshal(bid.Ext, &bidExt)
+		if err == nil && bidExt.Prebid != nil {
+			return openrtb_ext.ParseBidType(string(bidExt.Prebid.Type))
+		}
+	}
+
+	return "", &errortypes.BadServerResponse{
+		Message: fmt.Sprintf("Failed to parse impression \"%s\" mediatype", bid.ImpID),
+	}
+}
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/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/adapter_builders.go b/exchange/adapter_builders.go
index e188d293..90a0059c 100755
--- a/exchange/adapter_builders.go
+++ b/exchange/adapter_builders.go
@@ -171,6 +171,7 @@ import (
 	"github.com/prebid/prebid-server/v2/adapters/tappx"
 	"github.com/prebid/prebid-server/v2/adapters/teads"
 	"github.com/prebid/prebid-server/v2/adapters/telaria"
+	"github.com/prebid/prebid-server/v2/adapters/theadx"
 	"github.com/prebid/prebid-server/v2/adapters/tpmn"
 	"github.com/prebid/prebid-server/v2/adapters/trafficgate"
 	"github.com/prebid/prebid-server/v2/adapters/triplelift"
@@ -375,6 +376,7 @@ func newAdapterBuilders() map[openrtb_ext.BidderName]adapters.Builder {
 		openrtb_ext.BidderTappx:             tappx.Builder,
 		openrtb_ext.BidderTeads:             teads.Builder,
 		openrtb_ext.BidderTelaria:           telaria.Builder,
+		openrtb_ext.BidderTheadx:            theadx.Builder,
 		openrtb_ext.BidderTpmn:              tpmn.Builder,
 		openrtb_ext.BidderTrafficGate:       trafficgate.Builder,
 		openrtb_ext.BidderTriplelift:        triplelift.Builder,
diff --git a/exchange/exchange.go b/exchange/exchange.go
index 41134104..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"))
 					}
 				}
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/openrtb_ext/bidders.go b/openrtb_ext/bidders.go
index fcd2b15c..f1493add 100644
--- a/openrtb_ext/bidders.go
+++ b/openrtb_ext/bidders.go
@@ -189,6 +189,7 @@ var coreBidderNames []BidderName = []BidderName{
 	BidderTappx,
 	BidderTeads,
 	BidderTelaria,
+	BidderTheadx,
 	BidderTpmn,
 	BidderTrafficGate,
 	BidderTriplelift,
@@ -469,6 +470,7 @@ const (
 	BidderTappx             BidderName = "tappx"
 	BidderTeads             BidderName = "teads"
 	BidderTelaria           BidderName = "telaria"
+	BidderTheadx            BidderName = "theadx"
 	BidderTpmn              BidderName = "tpmn"
 	BidderTrafficGate       BidderName = "trafficgate"
 	BidderTriplelift        BidderName = "triplelift"
diff --git a/openrtb_ext/imp_theadx.go b/openrtb_ext/imp_theadx.go
new file mode 100644
index 00000000..bee3ac60
--- /dev/null
+++ b/openrtb_ext/imp_theadx.go
@@ -0,0 +1,12 @@
+package openrtb_ext
+
+import (
+	"encoding/json"
+)
+
+type ExtImpTheadx struct {
+	TagID             json.Number `json:"tagid"`
+	InventorySourceID int         `json:"wid,omitempty"`
+	MemberID          int         `json:"pid,optional,omitempty"`
+	PlacementName     string      `json:"pname,optional,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/static/bidder-info/theadx.yaml b/static/bidder-info/theadx.yaml
new file mode 100644
index 00000000..01d3312a
--- /dev/null
+++ b/static/bidder-info/theadx.yaml
@@ -0,0 +1,23 @@
+endpoint: "https://ssp.theadx.com/request?pbs=1"
+maintainer:
+  email: "ssp@theadx.com"
+gvlVendorID: 556
+debug:
+  allow: true
+capabilities:
+  app:
+    mediaTypes:
+      - banner
+      - native
+      - video
+      - audio
+  site:
+    mediaTypes:
+      - banner
+      - native
+      - video
+      - audio
+userSync:
+  redirect:
+    url: "https://ssp.theadx.com/cookie?redirect_url={{.RedirectURL}}&?pbs=1"
+    userMacro: "$UID"
diff --git a/static/bidder-params/theadx.json b/static/bidder-params/theadx.json
new file mode 100644
index 00000000..cdbbf22f
--- /dev/null
+++ b/static/bidder-params/theadx.json
@@ -0,0 +1,32 @@
+{
+  "$schema": "http://json-schema.org/draft-04/schema#",
+  "title": "Theadx Adapter Params",
+  "description": "A schema which validates params accepted by the theadx adapter",
+  "type": "object",
+  "properties": {
+    "pid": {
+      "type": ["integer", "string"],
+      "pattern": "^\\d+$",
+      "description": "An ID which identifies the partner selling the impression"
+    },
+    "tagid": {
+      "type": ["integer", "string"],
+      "pattern": "^\\d+$",
+      "description": "An ID which identifies the placement selling the impression"
+    },
+    "wid": {
+      "type": ["integer", "string"],
+      "description": "An ID which identifies the Theadx inventory source id"
+    }
+   
+  },
+  "anyOf":[
+    {
+      "required": ["tagid"]
+    }
+    , {
+      "required": ["pid", "wid","tagid"]
+    }
+
+  ]
+}
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/theadx/params_test.go b/adapters/theadx/params_test.go
new file mode 100644
index 00000000..c9e10e2f
--- /dev/null
+++ b/adapters/theadx/params_test.go
@@ -0,0 +1,66 @@
+package theadx
+
+import (
+	"encoding/json"
+	"testing"
+
+	"github.com/prebid/prebid-server/v2/openrtb_ext"
+)
+
+// This file actually intends to test static/bidder-params/theadx.json
+//
+// These also validate the format of the external API: request.imp[i].ext.prebid.bidder.theadx
+
+// TestValidParams makes sure that the theadx schema accepts all imp.ext fields which we intend to support.
+func TestValidParams(t *testing.T) {
+	validator, err := openrtb_ext.NewBidderParamsValidator("../../static/bidder-params")
+	if err != nil {
+		t.Fatalf("Failed to fetch the json-schemas. %v", err)
+	}
+
+	for _, validParam := range validParams {
+		if err := validator.Validate(openrtb_ext.BidderTheadx, json.RawMessage(validParam)); err != nil {
+			t.Errorf("Schema rejected theadx params: %s", validParam)
+		}
+	}
+}
+
+// TestInvalidParams makes sure that the theadx schema rejects all the imp.ext fields we don't support.
+func TestInvalidParams(t *testing.T) {
+	validator, err := openrtb_ext.NewBidderParamsValidator("../../static/bidder-params")
+	if err != nil {
+		t.Fatalf("Failed to fetch the json-schemas. %v", err)
+	}
+
+	for _, invalidParam := range invalidParams {
+		if err := validator.Validate(openrtb_ext.BidderTheadx, json.RawMessage(invalidParam)); err == nil {
+			t.Errorf("Schema allowed unexpected params: %s", invalidParam)
+		}
+	}
+}
+
+var validParams = []string{
+	`{"tagid":321}`,
+	`{"tagid":321,"wid":"456"}`,
+	`{"tagid":321,"pid":"12345"}`,
+	`{"tagid":321,"pname":"plc_mobile_300x250"}`,
+	`{"tagid":321,"inv":321,"mname":"pcl1"}`,
+	`{"tagid":"123","wid":"456","pid":"12345","pname":"plc_mobile_300x250"}`,
+}
+
+var invalidParams = []string{
+	``,
+	`null`,
+	`true`,
+	`5`,
+	`4.2`,
+	`[]`,
+	`{}`,
+	`{"notmid":"123"}`,
+	`{"mid":"placementID"}`,
+	`{"inv":321,"mname":12345}`,
+	`{"inv":321}`,
+	`{"inv":"321"}`,
+	`{"mname":"12345"}`,
+	`{"mid":"123","priceType":"GROSS"}`,
+}
diff --git a/adapters/theadx/theadx_test.go b/adapters/theadx/theadx_test.go
new file mode 100644
index 00000000..e64126c6
--- /dev/null
+++ b/adapters/theadx/theadx_test.go
@@ -0,0 +1,20 @@
+package theadx
+
+import (
+	"testing"
+
+	"github.com/prebid/prebid-server/v2/adapters/adapterstest"
+	"github.com/prebid/prebid-server/v2/config"
+	"github.com/prebid/prebid-server/v2/openrtb_ext"
+)
+
+func TestJsonSamples(t *testing.T) {
+	bidder, buildErr := Builder(openrtb_ext.BidderTheadx, config.Adapter{
+		Endpoint: "https://ssp.theadx.com/request"}, config.Server{ExternalUrl: "http://hosturl.com", GvlID: 1, DataCenter: "2"})
+
+	if buildErr != nil {
+		t.Fatalf("Builder returned unexpected error %v", buildErr)
+	}
+
+	adapterstest.RunJSONBidderTest(t, "theadxtest", bidder)
+}
diff --git a/adapters/theadx/theadxtest/exemplary/dynamic-tag.json b/adapters/theadx/theadxtest/exemplary/dynamic-tag.json
new file mode 100644
index 00000000..738a426d
--- /dev/null
+++ b/adapters/theadx/theadxtest/exemplary/dynamic-tag.json
@@ -0,0 +1,100 @@
+{
+  "mockBidRequest": {
+    "id": "test-request-id",
+    "imp": [{
+      "id": "test-imp-id",
+      "ext": {
+        "bidder": {
+          "tagid": "123",
+          "pname": "placement"
+        }
+      },
+      "banner": {
+        "format": [{
+          "w": 300,
+          "h": 250
+        }]
+      }
+    }, {
+      "id": "test-imp-id2",
+      "ext": {
+        "bidder": {
+          "tagid": "123",
+          "wid": 456,
+          "pname": "placement1"
+        }
+      },
+      "banner": {
+        "format": [{
+          "w": 300,
+          "h": 300
+        }]
+      }
+    }],
+    "site": {
+      "publisher": {
+        "id": "1"
+      },
+      "page": "some-page-url"
+    },
+    "device": {
+      "w": 1920,
+      "h": 800
+    }
+  },
+  "httpCalls": [{
+    "expectedRequest": {
+      "uri": "https://ssp.theadx.com/request",
+      "body": {
+        "id": "test-request-id",
+        "imp": [{
+          "id": "test-imp-id",
+          "tagid": "123",
+          "ext": {
+            "bidder": {
+              "tagid": "123",
+              "pname": "placement"
+            }
+          },
+          "banner": {
+            "format": [{
+              "w": 300,
+              "h": 250
+            }]
+          }
+        }, {
+          "id": "test-imp-id2",
+          "tagid": "123",
+          "ext": {
+            "bidder": {
+              "tagid": "123",
+              "wid": 456,
+              "pname": "placement1"
+            }
+          },
+          "banner": {
+            "format": [{
+              "w": 300,
+              "h": 300
+            }]
+          }
+        }],
+        "site": {
+          "publisher": {
+            "id": "1"
+          },
+          "page": "some-page-url"
+        },
+        "device": {
+          "w": 1920,
+          "h": 800
+        }
+      }
+    },
+    "mockResponse": {
+      "status": 204
+    }
+  }],
+  "expectedMakeRequestsErrors": [],
+  "expectedBidResponses": []
+}
diff --git a/adapters/theadx/theadxtest/exemplary/multi-format.json b/adapters/theadx/theadxtest/exemplary/multi-format.json
new file mode 100644
index 00000000..8f188547
--- /dev/null
+++ b/adapters/theadx/theadxtest/exemplary/multi-format.json
@@ -0,0 +1,162 @@
+{
+  "mockBidRequest": {
+    "id": "test-request-id",
+    "imp": [{
+      "id": "test-imp-id-1",
+      "ext": {
+        "bidder": {
+          "tagid": "123"
+        }
+      },
+      "video": {
+        "mimes": [
+          "video/mp4"
+        ],
+        "placement": 1
+      },
+      "banner": {
+        "format": [{
+          "w": 300,
+          "h": 250
+        }]
+      }
+    }, {
+      "id": "test-imp-id-2",
+      "ext": {
+        "bidder": {
+          "tagid": "123"
+        }
+      },
+      "video": {
+        "mimes": [
+          "video/mp4"
+        ],
+        "placement": 1
+      },
+      "banner": {
+        "format": [{
+          "w": 300,
+          "h": 250
+        }]
+      }
+    }]
+  },
+  "httpCalls": [{
+    "expectedRequest": {
+      "uri": "https://ssp.theadx.com/request",
+      "body": {
+        "id": "test-request-id",
+        "imp": [{
+          "id": "test-imp-id-1",
+          "ext": {
+            "bidder": {
+              "tagid": "123"
+            }
+          },
+          "video": {
+            "mimes": [
+              "video/mp4"
+            ],
+            "placement": 1
+          },
+          "banner": {
+            "format": [{
+              "w": 300,
+              "h": 250
+            }]
+          },
+          "tagid": "123"
+        }, {
+          "id": "test-imp-id-2",
+          "ext": {
+            "bidder": {
+              "tagid": "123"
+            }
+          },
+          "video": {
+            "mimes": [
+              "video/mp4"
+            ],
+            "placement": 1
+          },
+          "banner": {
+            "format": [{
+              "w": 300,
+              "h": 250
+            }]
+          },
+          "tagid": "123"
+        }]
+      }
+    },
+    "mockResponse": {
+      "status": 200,
+      "body": {
+        "id": "test-request-id",
+        "seatbid": [{
+          "bid": [{
+            "id": "test-bid-id-1",
+            "impid": "test-imp-id-1",
+            "price": 10,
+            "adm": "{video xml}",
+            "adomain": [],
+            "crid": "test-creative-id-1",
+            "ext": {
+              "prebid": {
+                "type": "video"
+              }
+            }
+          }]
+        }, {
+          "bid": [{
+            "id": "test-bid-id-2",
+            "impid": "test-imp-id-2",
+            "price": 2,
+            "adm": "{banner html}",
+            "adomain": [ "ad-domain" ],
+            "crid": "test-creative-id-2",
+            "ext": {
+              "prebid": {
+                "type": "banner"
+              }
+            }
+          }]
+        }],
+        "cur": "TRY"
+      }
+    }
+  }],
+  "expectedBidResponses": [{
+    "currency": "TRY",
+    "bids": [{
+      "bid": {
+        "id": "test-bid-id-1",
+        "impid": "test-imp-id-1",
+        "price": 10,
+        "adm": "{video xml}",
+        "crid": "test-creative-id-1",
+        "ext": {
+          "prebid": {
+            "type": "video"
+          }
+        }
+      },
+      "type": "video"
+    }, {
+      "bid": {
+        "id": "test-bid-id-2",
+        "impid": "test-imp-id-2",
+        "price": 2,
+        "adm": "{banner html}",
+        "adomain": [ "ad-domain" ],
+        "crid": "test-creative-id-2",
+        "ext": {
+          "prebid": {
+            "type": "banner"
+          }
+        }
+      },
+      "type": "banner"
+    }]
+  }]
+}
diff --git a/adapters/theadx/theadxtest/exemplary/multi-native.json b/adapters/theadx/theadxtest/exemplary/multi-native.json
new file mode 100644
index 00000000..d025b0ea
--- /dev/null
+++ b/adapters/theadx/theadxtest/exemplary/multi-native.json
@@ -0,0 +1,128 @@
+{
+  "mockBidRequest": {
+    "id": "test-request-id",
+    "imp": [{
+      "id": "test-imp-id-1",
+      "ext": {
+        "bidder": {
+          "tagid": "123"
+        }
+      },
+      "native": {
+        "request": "{json string 1}",
+        "ver": "1.2"
+      }
+    }, {
+      "id": "test-imp-id-2",
+      "ext": {
+        "bidder": {
+          "tagid": "124"
+        }
+      },
+      "native": {
+        "request": "{json string 2}",
+        "ver": "1.2"
+      }
+    }]
+  },
+  "httpCalls": [{
+    "expectedRequest": {
+      "uri": "https://ssp.theadx.com/request",
+      "body": {
+        "id": "test-request-id",
+        "imp": [{
+          "id": "test-imp-id-1",
+          "ext": {
+            "bidder": {
+              "tagid": "123"
+            }
+          },
+          "native": {
+            "request": "{json string 1}",
+            "ver": "1.2"
+          },
+          "tagid": "123"
+        }, {
+          "id": "test-imp-id-2",
+          "ext": {
+            "bidder": {
+              "tagid": "124"
+            }
+          },
+          "native": {
+            "request": "{json string 2}",
+            "ver": "1.2"
+          },
+          "tagid": "124"
+        }]
+      }
+    },
+    "mockResponse": {
+      "status": 200,
+      "body": {
+        "id": "test-request-id",
+        "seatbid": [{
+          "bid": [{
+            "id": "test-bid-id-1",
+            "impid": "test-imp-id-1",
+            "price": 10,
+            "adm": "{json response string 1}",
+            "adomain": [],
+            "crid": "test-creative-id-1",
+            "ext": {
+              "prebid": {
+                "type": "native"
+              }
+            }
+          }, {
+            "id": "test-bid-id-2",
+            "impid": "test-imp-id-2",
+            "price": 2,
+            "adm": "{json response string 2}",
+            "adomain": [ "ad-domain" ],
+            "crid": "test-creative-id-2",
+            "ext": {
+              "prebid": {
+                "type": "native"
+              }
+            }
+          }]
+        }],
+        "cur": "EUR"
+      }
+    }
+  }],
+  "expectedBidResponses": [{
+    "currency": "EUR",
+    "bids": [{
+      "bid": {
+        "id": "test-bid-id-1",
+        "impid": "test-imp-id-1",
+        "price": 10,
+        "adm": "{json response string 1}",
+        "crid": "test-creative-id-1",
+        "ext": {
+          "prebid": {
+            "type": "native"
+          }
+        }
+      },
+      "type": "native"
+    }, {
+      "bid": {
+        "id": "test-bid-id-2",
+        "impid": "test-imp-id-2",
+        "price": 2,
+        "adm": "{json response string 2}",
+        "adomain": [ "ad-domain" ],
+        "crid": "test-creative-id-2",
+        "ext": {
+          "prebid": {
+            "type": "native"
+          }
+        }
+      },
+      "type": "native"
+    }]
+  }]
+}
diff --git a/adapters/theadx/theadxtest/exemplary/single-banner.json b/adapters/theadx/theadxtest/exemplary/single-banner.json
new file mode 100644
index 00000000..32a4357d
--- /dev/null
+++ b/adapters/theadx/theadxtest/exemplary/single-banner.json
@@ -0,0 +1,105 @@
+{
+  "mockBidRequest": {
+    "id": "test-request-id",
+    "imp": [{
+      "id": "test-imp-id",
+      "ext": {
+        "bidder": {
+          "tagid": "123"
+        }
+      },
+      "banner": {
+        "format": [{
+          "w": 300,
+          "h": 250
+        }]
+      }
+    }],
+    "site": {
+      "publisher": {
+        "id": "1"
+      },
+      "page": "some-page-url"
+    },
+    "device": {
+      "w": 1920,
+      "h": 800
+    }
+  },
+  "httpCalls": [{
+    "expectedRequest": {
+      "uri": "https://ssp.theadx.com/request",
+      "body": {
+        "id": "test-request-id",
+        "imp": [{
+          "id": "test-imp-id",
+          "ext": {
+            "bidder": {
+              "tagid": "123"
+            }
+          },
+          "banner": {
+            "format": [{
+              "w": 300,
+              "h": 250
+            }]
+          },
+          "tagid": "123"
+        }],
+        "site": {
+          "publisher": {
+            "id": "1"
+          },
+          "page": "some-page-url"
+        },
+        "device": {
+          "w": 1920,
+          "h": 800
+        }
+      }
+    },
+    "mockResponse": {
+      "status": 200,
+      "body": {
+        "id": "test-request-id",
+        "seatbid": [{
+          "bid": [{
+            "id": "test-bid-id",
+            "impid": "test-imp-id",
+            "price": 10,
+            "adm": "{banner html}",
+            "adomain": [ "test.com" ],
+            "crid": "test-creative-id",
+            "ext": {
+              "prebid": {
+                "type": "banner"
+              }
+            }
+          }]
+        }],
+        "cur": "USD"
+      }
+    }
+  }],
+  "expectedBidResponses": [{
+    "currency": "USD",
+    "bids": [
+      {
+        "bid": {
+          "id": "test-bid-id",
+          "impid": "test-imp-id",
+          "price": 10,
+          "adm": "{banner html}",
+          "crid": "test-creative-id",
+          "adomain": [ "test.com" ],
+          "ext": {
+            "prebid": {
+              "type": "banner"
+            }
+          }
+        },
+        "type": "banner"
+      }
+    ]
+  }]
+}
diff --git a/adapters/theadx/theadxtest/exemplary/single-native.json b/adapters/theadx/theadxtest/exemplary/single-native.json
new file mode 100644
index 00000000..09e97dcf
--- /dev/null
+++ b/adapters/theadx/theadxtest/exemplary/single-native.json
@@ -0,0 +1,100 @@
+{
+  "mockBidRequest": {
+    "id": "test-request-id",
+    "imp": [{
+      "id": "test-imp-id",
+      "ext": {
+        "bidder": {
+          "tagid": "123"
+        }
+      },
+      "native": {
+        "request": "{json string}",
+        "ver": "1.2"
+      }
+    }],
+    "site": {
+      "publisher": {
+        "id": "1"
+      },
+      "page": "some-page-url"
+    },
+    "device": {
+      "w": 1920,
+      "h": 800
+    }
+  },
+  "httpCalls": [{
+    "expectedRequest": {
+      "uri": "https://ssp.theadx.com/request",
+      "body": {
+        "id": "test-request-id",
+        "imp": [{
+          "id": "test-imp-id",
+          "ext": {
+            "bidder": {
+              "tagid": "123"
+            }
+          },
+          "native": {
+            "request": "{json string}",
+            "ver": "1.2"
+          },
+          "tagid": "123"
+        }],
+        "site": {
+          "publisher": {
+            "id": "1"
+          },
+          "page": "some-page-url"
+        },
+        "device": {
+          "w": 1920,
+          "h": 800
+        }
+      }
+    },
+    "mockResponse": {
+      "status": 200,
+      "body": {
+        "id": "test-request-id",
+        "seatbid": [{
+          "bid": [{
+            "id": "test-bid-id",
+            "impid": "test-imp-id",
+            "price": 10,
+            "adm": "{json response string}",
+            "adomain": [],
+            "crid": "test-creative-id",
+            "ext": {
+              "prebid": {
+                "type": "native"
+              }
+            }
+          }]
+        }],
+        "cur": "USD"
+      }
+    }
+  }],
+  "expectedBidResponses": [{
+    "currency": "USD",
+    "bids": [
+      {
+        "bid": {
+          "id": "test-bid-id",
+          "impid": "test-imp-id",
+          "price": 10,
+          "adm": "{json response string}",
+          "crid": "test-creative-id",
+          "ext": {
+            "prebid": {
+              "type": "native"
+            }
+          }
+        },
+        "type": "native"
+      }
+    ]
+  }]
+}
diff --git a/adapters/theadx/theadxtest/exemplary/single-video.json b/adapters/theadx/theadxtest/exemplary/single-video.json
new file mode 100644
index 00000000..9d3b5f77
--- /dev/null
+++ b/adapters/theadx/theadxtest/exemplary/single-video.json
@@ -0,0 +1,103 @@
+{
+  "mockBidRequest": {
+    "id": "test-request-id",
+    "imp": [{
+      "id": "test-imp-id",
+      "ext": {
+        "bidder": {
+          "tagid": "123"
+        }
+      },
+      "video": {
+        "mimes": [
+          "video/mp4"
+        ],
+        "placement": 1
+      }
+    }],
+    "site": {
+      "publisher": {
+        "id": "1"
+      },
+      "page": "some-page-url"
+    },
+    "device": {
+      "w": 1920,
+      "h": 800
+    }
+  },
+  "httpCalls": [{
+    "expectedRequest": {
+      "uri": "https://ssp.theadx.com/request",
+      "body": {
+        "id": "test-request-id",
+        "imp": [{
+          "id": "test-imp-id",
+          "ext": {
+            "bidder": {
+              "tagid": "123"
+            }
+          },
+          "video": {
+            "mimes": [
+              "video/mp4"
+            ],
+            "placement": 1
+          },
+          "tagid": "123"
+        }],
+        "site": {
+          "publisher": {
+            "id": "1"
+          },
+          "page": "some-page-url"
+        },
+        "device": {
+          "w": 1920,
+          "h": 800
+        }
+      }
+    },
+    "mockResponse": {
+      "status": 200,
+      "body": {
+        "id": "test-request-id",
+        "seatbid": [{
+          "bid": [{
+            "id": "test-bid-id",
+            "impid": "test-imp-id",
+            "price": 10,
+            "adm": "{vast xml}",
+            "crid": "test-creative-id",
+            "ext": {
+              "prebid": {
+                "type": "video"
+              }
+            }
+          }]
+        }],
+        "cur": "USD"
+      }
+    }
+  }],
+  "expectedBidResponses": [{
+    "currency": "USD",
+    "bids": [
+      {
+        "bid": {
+          "id": "test-bid-id",
+          "impid": "test-imp-id",
+          "price": 10,
+          "adm": "{vast xml}",
+          "crid": "test-creative-id",
+          "ext": {
+            "prebid": {
+              "type": "video"
+            }
+          }
+        },
+        "type": "video"
+      }
+    ]
+  }]
+}
diff --git a/adapters/theadx/theadxtest/supplemental/bad-request.json b/adapters/theadx/theadxtest/supplemental/bad-request.json
new file mode 100644
index 00000000..52aefa57
--- /dev/null
+++ b/adapters/theadx/theadxtest/supplemental/bad-request.json
@@ -0,0 +1,48 @@
+{
+  "mockBidRequest": {
+    "id": "test-request-id",
+    "imp": [{
+        "id": "test-imp-id",
+        "native": {
+          "request": ""
+        },
+        "ext": {
+          "bidder": {
+            "tagid": 12345
+          }
+        }
+      }]
+  },
+  "httpCalls": [
+    {
+      "expectedRequest": {
+        "uri": "https://ssp.theadx.com/request",
+        "body": {
+          "id": "test-request-id",
+          "imp": [{
+            "ext": {
+              "bidder": {
+                "tagid": 12345
+              }
+            },
+            "id": "test-imp-id",
+            "native": {
+              "request": ""
+            },
+            "tagid": "12345"
+          }]
+        }
+      },
+      "mockResponse": {
+        "status": 400
+      }
+    }
+  ],
+  "expectedBidResponses": [],
+  "expectedMakeBidsErrors": [ 
+    {
+      "value": "Unexpected status code: 400. Run with request.debug = 1 for more info",
+      "comparison": "literal" 
+    }
+  ]
+}
diff --git a/adapters/theadx/theadxtest/supplemental/empty-response.json b/adapters/theadx/theadxtest/supplemental/empty-response.json
new file mode 100644
index 00000000..43546563
--- /dev/null
+++ b/adapters/theadx/theadxtest/supplemental/empty-response.json
@@ -0,0 +1,42 @@
+{
+  "mockBidRequest": {
+    "id": "test-request-id",
+    "imp": [{
+        "id": "test-imp-id",
+        "native": {
+          "request": ""
+        },
+        "ext": {
+          "bidder": {
+            "tagid": 123
+          }
+        }
+      }]
+  },
+  "httpCalls": [
+    {
+      "expectedRequest": {
+        "uri": "https://ssp.theadx.com/request",
+        "body": {
+          "id": "test-request-id",
+          "imp": [{
+            "ext": {
+              "bidder": {
+                "tagid": 123
+              }
+            },
+            "id": "test-imp-id",
+            "native": {
+              "request": ""
+            },
+            "tagid": "123"
+          }]
+        }
+      },
+      "mockResponse": {
+        "status": 204
+      }
+    }
+  ],
+  "expectedBidResponses": []
+}
diff --git a/adapters/theadx/theadxtest/supplemental/nobid-response.json b/adapters/theadx/theadxtest/supplemental/nobid-response.json
new file mode 100644
index 00000000..14e15de4
--- /dev/null
+++ b/adapters/theadx/theadxtest/supplemental/nobid-response.json
@@ -0,0 +1,49 @@
+{
+  "mockBidRequest": {
+    "id": "test-request-id",
+    "imp": [{
+        "id": "test-imp-id",
+        "native": {
+          "request": ""
+        },
+        "ext": {
+          "bidder": {
+            "tagid": 123
+          }
+        }
+      }]
+  },
+  "httpCalls": [
+    {
+      "expectedRequest": {
+        "uri": "https://ssp.theadx.com/request",
+        "body": {
+          "id": "test-request-id",
+          "imp": [{
+            "ext": {
+              "bidder": {
+                "tagid": 123
+              }
+            },
+            "id": "test-imp-id",
+            "native": {
+              "request": ""
+            },
+            "tagid": "123"
+          }]
+        }
+      },
+      "mockResponse": {
+        "status": 204,
+        "body": {
+          "id": "test-request-id",
+          "seatbid": null,
+          "bidid": null,
+          "cur": null
+        }
+      }
+    }
+  ],
+  "expectedBidResponses": [],
+  "expectedMakeBidsErrors": []
+}
diff --git a/adapters/theadx/theadxtest/supplemental/server-error.json b/adapters/theadx/theadxtest/supplemental/server-error.json
new file mode 100644
index 00000000..5130aba4
--- /dev/null
+++ b/adapters/theadx/theadxtest/supplemental/server-error.json
@@ -0,0 +1,49 @@
+{
+  "mockBidRequest": {
+    "id": "test-request-id",
+    "imp": [{
+        "id": "test-imp-id",
+        "native": {
+          "request": ""
+        },
+        "ext": {
+          "bidder": {
+            "tagid": 123
+          }
+        }
+      }]
+  },
+  "httpCalls": [
+    {
+      "expectedRequest": {
+        "uri": "https://ssp.theadx.com/request",
+        "body": {
+          "id": "test-request-id",
+          "imp": [{
+            "ext": {
+              "bidder": {
+                "tagid": 123
+              }
+            },
+            "id": "test-imp-id",
+            "native": {
+              "request": ""
+            },
+            "tagid": "123"
+          }]
+        }
+      },
+      "mockResponse": {
+        "status": 500,
+        "body": "Server error"
+      }
+    }
+  ],
+  "expectedBidResponses": [],
+  "expectedMakeBidsErrors": [ 
+    {
+      "value": "Unexpected status code: 500.",
+      "comparison": "literal" 
+    }
+  ]
+}
diff --git a/adapters/theadx/theadxtest/supplemental/unparsable-response.json b/adapters/theadx/theadxtest/supplemental/unparsable-response.json
new file mode 100644
index 00000000..2affa437
--- /dev/null
+++ b/adapters/theadx/theadxtest/supplemental/unparsable-response.json
@@ -0,0 +1,49 @@
+{
+  "mockBidRequest": {
+    "id": "test-request-id",
+    "imp": [{
+        "id": "test-imp-id",
+        "native": {
+          "request": ""
+        },
+        "ext": {
+          "bidder": {
+            "tagid": 123
+          }
+        }
+      }]
+  },
+  "httpCalls": [
+    {
+      "expectedRequest": {
+        "uri": "https://ssp.theadx.com/request",
+        "body": {
+          "id": "test-request-id",
+          "imp": [{
+            "ext": {
+              "bidder": {
+                "tagid": 123
+              }
+            },
+            "id": "test-imp-id",
+            "native": {
+              "request": ""
+            },
+            "tagid": "123"
+          }]
+        }
+      },
+      "mockResponse": {
+        "status": 200,
+        "body": ""
+      }
+    }
+  ],
+  "expectedBidResponses": [],
+  "expectedMakeBidsErrors": [
+    {
+      "value": "json: cannot unmarshal string into Go value of type openrtb2.BidResponse",
+      "comparison": "literal" 
+    }
+  ]
+}
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/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/openrtb2/auction_test.go b/endpoints/openrtb2/auction_test.go
index 9df8b540..f51979db 100644
--- a/endpoints/openrtb2/auction_test.go
+++ b/endpoints/openrtb2/auction_test.go
@@ -1650,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
 	}{
@@ -1870,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 {
@@ -3053,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})
@@ -3104,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)",
@@ -3158,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})
@@ -3210,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")
 }
 
@@ -3257,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})
@@ -3826,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})
@@ -5134,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
@@ -5675,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)
 		})
 	}
@@ -6114,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 9156fc06..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 {
@@ -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)
@@ -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")
 
@@ -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{
@@ -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/openrtb_ext/request_wrapper_test.go b/openrtb_ext/request_wrapper_test.go
index 425229e5..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
 			},
 		},
 	}