Repository: prebid/prebid-server
Pull request #2906 (2023-07-06 07:27:56)

TransmitUserFPD and TransmitPreciseGeo activity integration 
This PR contains existing GDPR related code refactoring due to activity integration.

I deviate from existing scrubber strategy implementation. New scrubber function `ScrubRequest` receives enforcer data, not a strategy. There are couple of reasons for this:
1) We run 2 activities and we need to handle 2 strategies at once: 1st UFPD  2nd PreciseGeo  
2) Both UFPD  and PreciseGeo  should modify User and Device together, not just one of them. 
3) I think it's better and cleaner to have separate variables for activities, not mix them with existing CCPA, COPPA, etc
4) Existing GDPR strategies are slightly different from what activity should do, like for User we need to scrub additional `bidRequest.User.EIDs = nil`, `bidRequest.User.Data = nil` and in User.Ext remove "data" instead of "eids".

With this implementation all existing unit tests pass, local testing of activities looks good. 


VeronikaSolovei9: > IMHO, we should follow the IPv6 scrubbing approach used by PBS-Java.

Will it be ok if I put a follow up PR with this change only? 

Patch:
diff --git a/Makefile b/Makefile
index baf69caf..b5b7281e 100644
--- a/Makefile
+++ b/Makefile
@@ -2,7 +2,7 @@
 
 all: deps test build-modules build
 
-.PHONY: deps test build-modules build image
+.PHONY: deps test build-modules build image format
 
 # deps will clean out the vendor directory and use go mod for a fresh install
 deps:
@@ -29,3 +29,7 @@ build: test
 # image will build a docker image
 image:
 	docker build -t prebid-server .
+
+# format runs format
+format:
+	./scripts/format.sh -f true
\ No newline at end of file
diff --git a/README.md b/README.md
index 9b657f35..cb64ed9a 100644
--- a/README.md
+++ b/README.md
@@ -47,6 +47,15 @@ go build .
 ./prebid-server
 ```
 
+Run format:
+```
+make format
+```
+or
+```bash
+./scripts/format.sh -f true
+```
+
 Load the landing page in your browser at `http://localhost:8000/`.
 For the full API reference, see [the endpoint documentation](https://docs.prebid.org/prebid-server/endpoints/pbs-endpoint-overview.html)
 
diff --git a/adapters/brightroll/brightroll.go b/adapters/brightroll/brightroll.go
deleted file mode 100644
index a917fe87..00000000
--- a/adapters/brightroll/brightroll.go
+++ /dev/null
@@ -1,263 +0,0 @@
-package brightroll
-
-import (
-	"encoding/json"
-	"fmt"
-	"net/http"
-	"strconv"
-
-	"github.com/prebid/openrtb/v19/adcom1"
-	"github.com/prebid/openrtb/v19/openrtb2"
-	"github.com/prebid/prebid-server/adapters"
-	"github.com/prebid/prebid-server/config"
-	"github.com/prebid/prebid-server/errortypes"
-	"github.com/prebid/prebid-server/openrtb_ext"
-)
-
-type BrightrollAdapter struct {
-	URI       string
-	extraInfo ExtraInfo
-}
-
-type ExtraInfo struct {
-	Accounts []Account `json:"accounts"`
-}
-
-type Account struct {
-	ID       string   `json:"id"`
-	Badv     []string `json:"badv"`
-	Bcat     []string `json:"bcat"`
-	Battr    []int8   `json:"battr"`
-	BidFloor float64  `json:"bidfloor"`
-}
-
-func (a *BrightrollAdapter) MakeRequests(requestIn *openrtb2.BidRequest, reqInfo *adapters.ExtraRequestInfo) ([]*adapters.RequestData, []error) {
-
-	request := *requestIn
-	errs := make([]error, 0, len(request.Imp))
-	if len(request.Imp) == 0 {
-		err := &errortypes.BadInput{
-			Message: "No impression in the bid request",
-		}
-		errs = append(errs, err)
-		return nil, errs
-	}
-
-	errors := make([]error, 0, 1)
-
-	var bidderExt adapters.ExtImpBidder
-	err := json.Unmarshal(request.Imp[0].Ext, &bidderExt)
-	if err != nil {
-		err = &errortypes.BadInput{
-			Message: "ext.bidder not provided",
-		}
-		errors = append(errors, err)
-		return nil, errors
-	}
-	var brightrollExt openrtb_ext.ExtImpBrightroll
-	err = json.Unmarshal(bidderExt.Bidder, &brightrollExt)
-	if err != nil {
-		err = &errortypes.BadInput{
-			Message: "ext.bidder.publisher not provided",
-		}
-		errors = append(errors, err)
-		return nil, errors
-	}
-	if brightrollExt.Publisher == "" {
-		err = &errortypes.BadInput{
-			Message: "publisher is empty",
-		}
-		errors = append(errors, err)
-		return nil, errors
-	}
-
-	var account *Account
-	for _, a := range a.extraInfo.Accounts {
-		if a.ID == brightrollExt.Publisher {
-			account = &a
-			break
-		}
-	}
-
-	if account == nil {
-		err = &errortypes.BadInput{
-			Message: "Invalid publisher",
-		}
-		errors = append(errors, err)
-		return nil, errors
-	}
-
-	validImpExists := false
-	for i := 0; i < len(request.Imp); i++ {
-		//Brightroll supports only banner and video impressions as of now
-		if request.Imp[i].Banner != nil {
-			bannerCopy := *request.Imp[i].Banner
-			if bannerCopy.W == nil && bannerCopy.H == nil && len(bannerCopy.Format) > 0 {
-				firstFormat := bannerCopy.Format[0]
-				bannerCopy.W = &(firstFormat.W)
-				bannerCopy.H = &(firstFormat.H)
-			}
-
-			if len(account.Battr) > 0 {
-				bannerCopy.BAttr = getBlockedCreativetypes(account.Battr)
-			}
-			request.Imp[i].Banner = &bannerCopy
-			validImpExists = true
-		} else if request.Imp[i].Video != nil {
-			validImpExists = true
-			if brightrollExt.Publisher == "adthrive" {
-				videoCopy := *request.Imp[i].Video
-				if len(account.Battr) > 0 {
-					videoCopy.BAttr = getBlockedCreativetypes(account.Battr)
-				}
-				request.Imp[i].Video = &videoCopy
-			}
-		}
-		if validImpExists && request.Imp[i].BidFloor == 0 && account.BidFloor > 0 {
-			request.Imp[i].BidFloor = account.BidFloor
-		}
-	}
-	if !validImpExists {
-		err := &errortypes.BadInput{
-			Message: fmt.Sprintf("No valid impression in the bid request"),
-		}
-		errs = append(errs, err)
-		return nil, errs
-	}
-
-	request.AT = 1 //Defaulting to first price auction for all prebid requests
-
-	if len(account.Bcat) > 0 {
-		request.BCat = account.Bcat
-	}
-
-	if len(account.Badv) > 0 {
-		request.BAdv = account.Badv
-	}
-	reqJSON, err := json.Marshal(request)
-	if err != nil {
-		errs = append(errs, err)
-		return nil, errs
-	}
-	thisURI := a.URI
-	thisURI = thisURI + "?publisher=" + brightrollExt.Publisher
-	headers := http.Header{}
-	headers.Add("Content-Type", "application/json;charset=utf-8")
-	headers.Add("Accept", "application/json")
-	headers.Add("x-openrtb-version", "2.5")
-
-	if request.Device != nil {
-		addHeaderIfNonEmpty(headers, "User-Agent", request.Device.UA)
-		addHeaderIfNonEmpty(headers, "X-Forwarded-For", request.Device.IP)
-		addHeaderIfNonEmpty(headers, "Accept-Language", request.Device.Language)
-		if request.Device.DNT != nil {
-			addHeaderIfNonEmpty(headers, "DNT", strconv.Itoa(int(*request.Device.DNT)))
-		}
-	}
-
-	return []*adapters.RequestData{{
-		Method:  "POST",
-		Uri:     thisURI,
-		Body:    reqJSON,
-		Headers: headers,
-	}}, errors
-}
-
-func (a *BrightrollAdapter) MakeBids(internalRequest *openrtb2.BidRequest, externalRequest *adapters.RequestData, response *adapters.ResponseData) (*adapters.BidderResponse, []error) {
-
-	if response.StatusCode == http.StatusNoContent {
-		return nil, nil
-	}
-
-	if response.StatusCode == http.StatusBadRequest {
-		return nil, []error{&errortypes.BadInput{
-			Message: fmt.Sprintf("Unexpected status code: %d. ", response.StatusCode),
-		}}
-	}
-
-	if response.StatusCode != http.StatusOK {
-		return nil, []error{&errortypes.BadServerResponse{
-			Message: fmt.Sprintf("unexpected status code: %d. Run with request.debug = 1 for more info", response.StatusCode),
-		}}
-	}
-
-	var bidResp openrtb2.BidResponse
-	if err := json.Unmarshal(response.Body, &bidResp); err != nil {
-		return nil, []error{&errortypes.BadServerResponse{
-			Message: fmt.Sprintf("bad server response: %d. ", err),
-		}}
-	}
-
-	bidResponse := adapters.NewBidderResponseWithBidsCapacity(len(bidResp.SeatBid[0].Bid))
-	sb := bidResp.SeatBid[0]
-	for i := 0; i < len(sb.Bid); i++ {
-		bid := sb.Bid[i]
-		bidResponse.Bids = append(bidResponse.Bids, &adapters.TypedBid{
-			Bid:     &bid,
-			BidType: getMediaTypeForImp(bid.ImpID, internalRequest.Imp),
-		})
-	}
-	return bidResponse, nil
-}
-
-func getBlockedCreativetypes(attr []int8) []adcom1.CreativeAttribute {
-	var creativeAttr []adcom1.CreativeAttribute
-	for i := 0; i < len(attr); i++ {
-		creativeAttr = append(creativeAttr, adcom1.CreativeAttribute(attr[i]))
-	}
-	return creativeAttr
-}
-
-// Adding header fields to request header
-func addHeaderIfNonEmpty(headers http.Header, headerName string, headerValue string) {
-	if len(headerValue) > 0 {
-		headers.Add(headerName, headerValue)
-	}
-}
-
-// getMediaTypeForImp figures out which media type this bid is for.
-func getMediaTypeForImp(impId string, imps []openrtb2.Imp) openrtb_ext.BidType {
-	mediaType := openrtb_ext.BidTypeBanner //default type
-	for _, imp := range imps {
-		if imp.ID == impId {
-			if imp.Video != nil {
-				mediaType = openrtb_ext.BidTypeVideo
-			}
-			return mediaType
-		}
-	}
-	return mediaType
-}
-
-// Builder builds a new instance of the Brightroll adapter for the given bidder with the given config.
-func Builder(bidderName openrtb_ext.BidderName, config config.Adapter, server config.Server) (adapters.Bidder, error) {
-	extraInfo, err := getExtraInfo(config.ExtraAdapterInfo)
-	if err != nil {
-		return nil, err
-	}
-
-	bidder := &BrightrollAdapter{
-		URI:       config.Endpoint,
-		extraInfo: extraInfo,
-	}
-	return bidder, nil
-}
-
-func getExtraInfo(v string) (ExtraInfo, error) {
-	if len(v) == 0 {
-		return getDefaultExtraInfo(), nil
-	}
-
-	var extraInfo ExtraInfo
-	if err := json.Unmarshal([]byte(v), &extraInfo); err != nil {
-		return extraInfo, fmt.Errorf("invalid extra info: %v", err)
-	}
-
-	return extraInfo, nil
-}
-
-func getDefaultExtraInfo() ExtraInfo {
-	return ExtraInfo{
-		Accounts: []Account{},
-	}
-}
diff --git a/adapters/ownadx/ownadx.go b/adapters/ownadx/ownadx.go
new file mode 100644
index 00000000..c19343ba
--- /dev/null
+++ b/adapters/ownadx/ownadx.go
@@ -0,0 +1,213 @@
+package ownadx
+
+import (
+	"encoding/json"
+	"fmt"
+	"github.com/prebid/openrtb/v19/openrtb2"
+	"github.com/prebid/prebid-server/adapters"
+	"github.com/prebid/prebid-server/config"
+	"github.com/prebid/prebid-server/errortypes"
+	"github.com/prebid/prebid-server/macros"
+	"github.com/prebid/prebid-server/openrtb_ext"
+	"net/http"
+	"text/template"
+)
+
+type adapter struct {
+	endpoint *template.Template
+}
+type bidExt struct {
+	MediaType string `json:"mediaType"`
+}
+
+func (adapter *adapter) getRequestData(bidRequest *openrtb2.BidRequest, impExt *openrtb_ext.ExtImpOwnAdx, imps []openrtb2.Imp) (*adapters.RequestData, error) {
+	pbidRequest := createBidRequest(bidRequest, imps)
+	reqJSON, err := json.Marshal(pbidRequest)
+	if err != nil {
+		return nil, &errortypes.BadInput{
+			Message: "Prebid bidder request not valid or can't be marshalled. Err: " + err.Error(),
+		}
+	}
+	url, err := adapter.buildEndpointURL(impExt)
+	if err != nil {
+		return nil, &errortypes.BadInput{
+			Message: "Error while creating endpoint. Err: " + err.Error(),
+		}
+	}
+
+	headers := http.Header{}
+	headers.Add("Content-Type", "application/json;charset=utf-8")
+	headers.Add("Accept", "application/json")
+	headers.Add("x-openrtb-version", "2.5")
+
+	return &adapters.RequestData{
+		Method:  "POST",
+		Uri:     url,
+		Body:    reqJSON,
+		Headers: headers}, nil
+
+}
+func createBidRequest(rtbBidRequest *openrtb2.BidRequest, imps []openrtb2.Imp) *openrtb2.BidRequest {
+	bidRequest := *rtbBidRequest
+	bidRequest.Imp = imps
+	return &bidRequest
+}
+func (adapter *adapter) buildEndpointURL(params *openrtb_ext.ExtImpOwnAdx) (string, error) {
+	endpointParams := macros.EndpointTemplateParams{
+		ZoneID:    params.SspId,
+		AccountID: params.SeatId,
+		SourceId:  params.TokenId,
+	}
+	return macros.ResolveMacros(adapter.endpoint, endpointParams)
+}
+
+func getImpressionExt(imp *openrtb2.Imp) (*openrtb_ext.ExtImpOwnAdx, error) {
+	var bidderExt adapters.ExtImpBidder
+	if err := json.Unmarshal(imp.Ext, &bidderExt); err != nil {
+		return nil, &errortypes.BadInput{
+			Message: "Bidder extension not valid or can't be unmarshalled",
+		}
+	}
+
+	var ownAdxExt openrtb_ext.ExtImpOwnAdx
+	if err := json.Unmarshal(bidderExt.Bidder, &ownAdxExt); err != nil {
+		return nil, &errortypes.BadInput{
+			Message: "Error while unmarshaling bidder extension",
+		}
+	}
+
+	return &ownAdxExt, nil
+}
+
+func (adapter *adapter) MakeRequests(request *openrtb2.BidRequest, reqInfo *adapters.ExtraRequestInfo) ([]*adapters.RequestData, []error) {
+
+	errs := make([]error, 0, len(request.Imp))
+	if len(request.Imp) == 0 {
+		errs = append(errs, &errortypes.BadInput{
+			Message: "No impression in the bid request"},
+		)
+		return nil, errs
+	}
+	extImps, errors := groupImpsByExt(request.Imp)
+	if len(errors) != 0 {
+		errs = append(errs, errors...)
+	}
+	if len(extImps) == 0 {
+		return nil, errs
+	}
+	reqDetail := make([]*adapters.RequestData, 0, len(extImps))
+	for k, imps := range extImps {
+		bidRequest, err := adapter.getRequestData(request, &k, imps)
+		if err != nil {
+			errs = append(errs, err)
+		} else {
+			reqDetail = append(reqDetail, bidRequest)
+		}
+	}
+	return reqDetail, errs
+}
+func groupImpsByExt(imps []openrtb2.Imp) (map[openrtb_ext.ExtImpOwnAdx][]openrtb2.Imp, []error) {
+	respExt := make(map[openrtb_ext.ExtImpOwnAdx][]openrtb2.Imp)
+	errors := make([]error, 0, len(imps))
+	for _, imp := range imps {
+		ownAdxExt, err := getImpressionExt(&(imp))
+		if err != nil {
+			errors = append(errors, err)
+			continue
+		}
+
+		respExt[*ownAdxExt] = append(respExt[*ownAdxExt], imp)
+	}
+	return respExt, errors
+}
+
+func (adapter *adapter) MakeBids(internalRequest *openrtb2.BidRequest, externalRequest *adapters.RequestData, response *adapters.ResponseData) (*adapters.BidderResponse, []error) {
+	if response.StatusCode == http.StatusNoContent {
+		return nil, nil
+	}
+	if response.StatusCode == http.StatusBadRequest {
+		return nil, []error{
+			&errortypes.BadServerResponse{
+				Message: fmt.Sprintf("Bad request: %d", response.StatusCode),
+			},
+		}
+	}
+	if response.StatusCode != http.StatusOK {
+		return nil, []error{
+			&errortypes.BadServerResponse{
+				Message: fmt.Sprintf("Unexpected status code: %d. Run with request.test = 1 for more info.", response.StatusCode),
+			},
+		}
+	}
+	var bidResp openrtb2.BidResponse
+	if err := json.Unmarshal(response.Body, &bidResp); err != nil {
+		return nil, []error{
+			&errortypes.BadServerResponse{
+				Message: fmt.Sprintf("Bad server response "),
+			},
+		}
+	}
+	if len(bidResp.SeatBid) == 0 {
+		return nil, []error{
+			&errortypes.BadServerResponse{
+				Message: fmt.Sprintf("Array SeatBid cannot be empty "),
+			},
+		}
+	}
+
+	seatBid := bidResp.SeatBid[0]
+	bidResponse := adapters.NewBidderResponseWithBidsCapacity(len(bidResp.SeatBid[0].Bid))
+	if len(seatBid.Bid) == 0 {
+		return nil, []error{
+			&errortypes.BadServerResponse{
+				Message: fmt.Sprintf("Bid cannot be empty "),
+			},
+		}
+	}
+	for i := 0; i < len(seatBid.Bid); i++ {
+		var bidType openrtb_ext.BidType
+		bid := seatBid.Bid[i]
+
+		bidType, err := getMediaType(bid)
+		if err != nil {
+			return nil, []error{&errortypes.BadServerResponse{
+				Message: "Bid type is invalid",
+			}}
+		}
+		bidResponse.Bids = append(bidResponse.Bids, &adapters.TypedBid{
+			Bid:     &bid,
+			BidType: bidType,
+		})
+	}
+
+	return bidResponse, nil
+}
+
+// Builder builds a new instance of the OwnAdx adapter for the given bidder with the given config
+func Builder(bidderName openrtb_ext.BidderName, config config.Adapter, server config.Server) (adapters.Bidder, error) {
+	template, err := template.New("endpointTemplate").Parse(config.Endpoint)
+	if err != nil {
+		return nil, fmt.Errorf("unable to parse endpoint url template: %v", err)
+	}
+
+	bidder := &adapter{
+		endpoint: template,
+	}
+
+	return bidder, nil
+}
+
+func getMediaType(bid openrtb2.Bid) (openrtb_ext.BidType, error) {
+	switch bid.MType {
+	case openrtb2.MarkupBanner:
+		return openrtb_ext.BidTypeBanner, nil
+	case openrtb2.MarkupVideo:
+		return openrtb_ext.BidTypeVideo, nil
+	case openrtb2.MarkupAudio:
+		return openrtb_ext.BidTypeAudio, nil
+	case openrtb2.MarkupNative:
+		return openrtb_ext.BidTypeNative, nil
+	default:
+		return "", fmt.Errorf("invalid BidType: %d", bid.MType)
+	}
+}
diff --git a/adapters/rtbhouse/rtbhouse.go b/adapters/rtbhouse/rtbhouse.go
index c8afb0c4..dc67df9a 100644
--- a/adapters/rtbhouse/rtbhouse.go
+++ b/adapters/rtbhouse/rtbhouse.go
@@ -4,6 +4,7 @@ import (
 	"encoding/json"
 	"fmt"
 	"net/http"
+	"strings"
 
 	"github.com/prebid/openrtb/v19/openrtb2"
 	"github.com/prebid/prebid-server/adapters"
@@ -12,6 +13,15 @@ import (
 	"github.com/prebid/prebid-server/openrtb_ext"
 )
 
+const (
+	BidderCurrency string = "USD"
+)
+
+// RTBHouseAdapter implements the Bidder interface.
+type RTBHouseAdapter struct {
+	endpoint string
+}
+
 // Builder builds a new instance of the RTBHouse 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 := &RTBHouseAdapter{
@@ -20,11 +30,6 @@ func Builder(bidderName openrtb_ext.BidderName, config config.Adapter, server co
 	return bidder, nil
 }
 
-// RTBHouseAdapter implements the Bidder interface.
-type RTBHouseAdapter struct {
-	endpoint string
-}
-
 // MakeRequests prepares the HTTP requests which should be made to fetch bids.
 func (adapter *RTBHouseAdapter) MakeRequests(
 	openRTBRequest *openrtb2.BidRequest,
@@ -33,7 +38,48 @@ func (adapter *RTBHouseAdapter) MakeRequests(
 	requestsToBidder []*adapters.RequestData,
 	errs []error,
 ) {
-	openRTBRequestJSON, err := json.Marshal(openRTBRequest)
+
+	reqCopy := *openRTBRequest
+	reqCopy.Imp = []openrtb2.Imp{}
+	for _, imp := range openRTBRequest.Imp {
+		var bidFloorCur = imp.BidFloorCur
+		var bidFloor = imp.BidFloor
+		if bidFloorCur == "" && bidFloor == 0 {
+			rtbhouseExt, err := getImpressionExt(imp)
+			if err != nil {
+				return nil, []error{err}
+			}
+			if rtbhouseExt.BidFloor > 0 && len(reqCopy.Cur) > 0 {
+				bidFloorCur = reqCopy.Cur[0]
+				bidFloor = rtbhouseExt.BidFloor
+			}
+		}
+
+		// Check if imp comes with bid floor amount defined in a foreign currency
+		if bidFloor > 0 && bidFloorCur != "" && strings.ToUpper(bidFloorCur) != BidderCurrency {
+			// Convert to US dollars
+			convertedValue, err := reqInfo.ConvertCurrency(bidFloor, bidFloorCur, BidderCurrency)
+			if err != nil {
+				return nil, []error{err}
+			}
+
+			bidFloorCur = BidderCurrency
+			bidFloor = convertedValue
+		}
+
+		if bidFloor > 0 && bidFloorCur == BidderCurrency {
+			// Update after conversion. All imp elements inside request.Imp are shallow copies
+			// therefore, their non-pointer values are not shared memory and are safe to modify.
+			imp.BidFloorCur = bidFloorCur
+			imp.BidFloor = bidFloor
+		}
+
+		// Set the CUR of bid to BIDDER_CURRENCY after converting all floors
+		reqCopy.Cur = []string{BidderCurrency}
+		reqCopy.Imp = append(reqCopy.Imp, imp)
+	}
+
+	openRTBRequestJSON, err := json.Marshal(reqCopy)
 	if err != nil {
 		errs = append(errs, err)
 		return nil, errs
@@ -52,6 +98,24 @@ func (adapter *RTBHouseAdapter) MakeRequests(
 	return requestsToBidder, errs
 }
 
+func getImpressionExt(imp openrtb2.Imp) (*openrtb_ext.ExtImpRTBHouse, error) {
+	var bidderExt adapters.ExtImpBidder
+	if err := json.Unmarshal(imp.Ext, &bidderExt); err != nil {
+		return nil, &errortypes.BadInput{
+			Message: "Bidder extension not provided or can't be unmarshalled",
+		}
+	}
+
+	var rtbhouseExt openrtb_ext.ExtImpRTBHouse
+	if err := json.Unmarshal(bidderExt.Bidder, &rtbhouseExt); err != nil {
+		return nil, &errortypes.BadInput{
+			Message: "Error while unmarshaling bidder extension",
+		}
+	}
+
+	return &rtbhouseExt, nil
+}
+
 const unexpectedStatusCodeFormat = "" +
 	"Unexpected status code: %d. Run with request.debug = 1 for more info"
 
@@ -97,6 +161,8 @@ func (adapter *RTBHouseAdapter) MakeBids(
 		}
 	}
 
+	bidderResponse.Currency = BidderCurrency
+
 	return bidderResponse, nil
 
 }
diff --git a/adapters/vox/vox.go b/adapters/vox/vox.go
new file mode 100644
index 00000000..0b56fcbf
--- /dev/null
+++ b/adapters/vox/vox.go
@@ -0,0 +1,86 @@
+package vox
+
+import (
+	"encoding/json"
+	"fmt"
+	"github.com/prebid/openrtb/v19/openrtb2"
+	"github.com/prebid/prebid-server/adapters"
+	"github.com/prebid/prebid-server/config"
+	"github.com/prebid/prebid-server/openrtb_ext"
+)
+
+type adapter struct {
+	endpoint string
+}
+
+func Builder(bidderName openrtb_ext.BidderName, config config.Adapter, server config.Server) (adapters.Bidder, error) {
+	bidder := &adapter{
+		endpoint: config.Endpoint,
+	}
+	return bidder, nil
+}
+
+func (a *adapter) MakeRequests(request *openrtb2.BidRequest, requestInfo *adapters.ExtraRequestInfo) ([]*adapters.RequestData, []error) {
+	requestJSON, err := json.Marshal(request)
+	if err != nil {
+		return nil, []error{err}
+	}
+
+	requestData := &adapters.RequestData{
+		Method: "POST",
+		Uri:    a.endpoint,
+		Body:   requestJSON,
+	}
+
+	return []*adapters.RequestData{requestData}, nil
+}
+
+func (a *adapter) MakeBids(request *openrtb2.BidRequest, requestData *adapters.RequestData, responseData *adapters.ResponseData) (*adapters.BidderResponse, []error) {
+	if adapters.IsResponseStatusCodeNoContent(responseData) {
+		return nil, nil
+	}
+
+	if err := adapters.CheckResponseStatusCodeForErrors(responseData); err != nil {
+		return nil, []error{err}
+	}
+
+	var response openrtb2.BidResponse
+	if err := json.Unmarshal(responseData.Body, &response); err != nil {
+		return nil, []error{err}
+	}
+
+	bidResponse := adapters.NewBidderResponseWithBidsCapacity(len(request.Imp))
+	bidResponse.Currency = response.Cur
+
+	for _, seatBid := range response.SeatBid {
+		for i, bid := range seatBid.Bid {
+			bidType, err := getMediaTypeForBid(bid)
+			if err != nil {
+				return nil, []error{err}
+			}
+
+			b := &adapters.TypedBid{
+				Bid:     &seatBid.Bid[i],
+				BidType: bidType,
+			}
+			bidResponse.Bids = append(bidResponse.Bids, b)
+		}
+	}
+
+	return bidResponse, nil
+}
+
+func getMediaTypeForBid(bid openrtb2.Bid) (openrtb_ext.BidType, error) {
+	switch bid.MType {
+	case openrtb2.MarkupBanner:
+		return openrtb_ext.BidTypeBanner, nil
+	case openrtb2.MarkupVideo:
+		return openrtb_ext.BidTypeVideo, nil
+	case openrtb2.MarkupAudio:
+		return openrtb_ext.BidTypeAudio, nil
+	case openrtb2.MarkupNative:
+		return openrtb_ext.BidTypeNative, nil
+	default:
+		return "", fmt.Errorf("Unable to fetch mediaType in multi-format: %s", bid.ImpID)
+	}
+}
diff --git a/config/bidderinfo.go b/config/bidderinfo.go
index b4d79001..630952a6 100644
--- a/config/bidderinfo.go
+++ b/config/bidderinfo.go
@@ -162,7 +162,7 @@ type SyncerEndpoint struct {
 	// startup:
 	//
 	//  {{.ExternalURL}} - This will be replaced with the host server's externally reachable http path.
-	//  {{.SyncerKey}}   - This will be replaced with the syncer key.
+	//  {{.BidderName}}  - This will be replaced with the bidder name.
 	//  {{.SyncType}}    - This will be replaced with the sync type, either 'b' for iframe syncs or 'i'
 	//                     for redirect/image syncs.
 	//  {{.UserMacro}}   - This will be replaced with the bidder server's user id macro.
diff --git a/endpoints/openrtb2/amp_auction.go b/endpoints/openrtb2/amp_auction.go
index 9e06272b..0d5c35f9 100644
--- a/endpoints/openrtb2/amp_auction.go
+++ b/endpoints/openrtb2/amp_auction.go
@@ -69,6 +69,7 @@ func NewAmpEndpoint(
 	bidderMap map[string]openrtb_ext.BidderName,
 	storedRespFetcher stored_requests.Fetcher,
 	hookExecutionPlanBuilder hooks.ExecutionPlanBuilder,
+	tmaxAdjustments *exchange.TmaxAdjustmentsPreprocessed,
 ) (httprouter.Handle, error) {
 
 	if ex == nil || validator == nil || requestsById == nil || accounts == nil || cfg == nil || metricsEngine == nil {
@@ -100,7 +101,9 @@ func NewAmpEndpoint(
 		nil,
 		ipValidator,
 		storedRespFetcher,
-		hookExecutionPlanBuilder}).AmpAuction), nil
+		hookExecutionPlanBuilder,
+		tmaxAdjustments,
+	}).AmpAuction), nil
 
 }
 
@@ -251,6 +254,7 @@ func (deps *endpointDeps) AmpAuction(w http.ResponseWriter, r *http.Request, _ h
 		QueryParams:                r.URL.Query(),
 		TCF2Config:                 tcf2Config,
 		Activities:                 activities,
+		TmaxAdjustments:            deps.tmaxAdjustments,
 	}
 
 	auctionResponse, err := deps.ex.HoldAuction(ctx, auctionRequest, nil)
@@ -292,6 +296,9 @@ func (deps *endpointDeps) AmpAuction(w http.ResponseWriter, r *http.Request, _ h
 	}
 
 	labels, ao = sendAmpResponse(w, hookExecutor, auctionResponse, reqWrapper, account, labels, ao, errL)
+	if len(ao.Errors) == 0 && !auctionRequest.BidderResponseStartTime.IsZero() {
+		deps.metricsEngine.RecordOverheadTime(metrics.MakeAuctionResponse, time.Since(auctionRequest.BidderResponseStartTime))
+	}
 }
 
 func rejectAmpRequest(
diff --git a/endpoints/openrtb2/auction.go b/endpoints/openrtb2/auction.go
index 087d0a5d..8e01552c 100644
--- a/endpoints/openrtb2/auction.go
+++ b/endpoints/openrtb2/auction.go
@@ -92,6 +92,7 @@ func NewEndpoint(
 	bidderMap map[string]openrtb_ext.BidderName,
 	storedRespFetcher stored_requests.Fetcher,
 	hookExecutionPlanBuilder hooks.ExecutionPlanBuilder,
+	tmaxAdjustments *exchange.TmaxAdjustmentsPreprocessed,
 ) (httprouter.Handle, error) {
 	if ex == nil || validator == nil || requestsById == nil || accounts == nil || cfg == nil || metricsEngine == nil {
 		return nil, errors.New("NewEndpoint requires non-nil arguments.")
@@ -122,7 +123,8 @@ func NewEndpoint(
 		nil,
 		ipValidator,
 		storedRespFetcher,
-		hookExecutionPlanBuilder}).Auction), nil
+		hookExecutionPlanBuilder,
+		tmaxAdjustments}).Auction), nil
 }
 
 type endpointDeps struct {
@@ -144,6 +146,7 @@ type endpointDeps struct {
 	privateNetworkIPValidator iputil.IPValidator
 	storedRespFetcher         stored_requests.Fetcher
 	hookExecutionPlanBuilder  hooks.ExecutionPlanBuilder
+	tmaxAdjustments           *exchange.TmaxAdjustmentsPreprocessed
 }
 
 func (deps *endpointDeps) Auction(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
@@ -250,6 +253,7 @@ func (deps *endpointDeps) Auction(w http.ResponseWriter, r *http.Request, _ http
 		HookExecutor:               hookExecutor,
 		TCF2Config:                 tcf2Config,
 		Activities:                 activities,
+		TmaxAdjustments:            deps.tmaxAdjustments,
 	}
 	auctionResponse, err := deps.ex.HoldAuction(ctx, auctionRequest, nil)
 	defer func() {
@@ -288,6 +292,9 @@ func (deps *endpointDeps) Auction(w http.ResponseWriter, r *http.Request, _ http
 		glog.Errorf("Error setting seat non-bid: %v", err)
 	}
 	labels, ao = sendAuctionResponse(w, hookExecutor, response, req.BidRequest, account, labels, ao)
+	if len(ao.Errors) == 0 && !auctionRequest.BidderResponseStartTime.IsZero() {
+		deps.metricsEngine.RecordOverheadTime(metrics.MakeAuctionResponse, time.Since(auctionRequest.BidderResponseStartTime))
+	}
 }
 
 // setSeatNonBidRaw is transitional function for setting SeatNonBid inside bidResponse.Ext
diff --git a/endpoints/openrtb2/video_auction.go b/endpoints/openrtb2/video_auction.go
index 565baf02..a2a1e03e 100644
--- a/endpoints/openrtb2/video_auction.go
+++ b/endpoints/openrtb2/video_auction.go
@@ -56,6 +56,7 @@ func NewVideoEndpoint(
 	defReqJSON []byte,
 	bidderMap map[string]openrtb_ext.BidderName,
 	cache prebid_cache_client.Client,
+	tmaxAdjustments *exchange.TmaxAdjustmentsPreprocessed,
 ) (httprouter.Handle, error) {
 
 	if ex == nil || validator == nil || requestsById == nil || accounts == nil || cfg == nil || met == nil {
@@ -89,7 +90,8 @@ func NewVideoEndpoint(
 		videoEndpointRegexp,
 		ipValidator,
 		empty_fetcher.EmptyFetcher{},
-		hooks.EmptyPlanBuilder{}}).VideoAuctionEndpoint), nil
+		hooks.EmptyPlanBuilder{},
+		tmaxAdjustments}).VideoAuctionEndpoint), nil
 }
 
 /*
@@ -311,6 +313,7 @@ func (deps *endpointDeps) VideoAuctionEndpoint(w http.ResponseWriter, r *http.Re
 		GlobalPrivacyControlHeader: secGPC,
 		PubID:                      labels.PubID,
 		HookExecutor:               hookexecution.EmptyHookExecutor{},
+		TmaxAdjustments:            deps.tmaxAdjustments,
 	}
 
 	auctionResponse, err := deps.ex.HoldAuction(ctx, auctionRequest, &debugLog)
@@ -372,6 +375,10 @@ func (deps *endpointDeps) VideoAuctionEndpoint(w http.ResponseWriter, r *http.Re
 		return
 	}
 
+	if len(vo.Errors) == 0 && !auctionRequest.BidderResponseStartTime.IsZero() {
+		deps.metricsEngine.RecordOverheadTime(metrics.MakeAuctionResponse, time.Since(auctionRequest.BidderResponseStartTime))
+	}
+
 	w.Header().Set("Content-Type", "application/json")
 	w.Write(resp)
 
diff --git a/endpoints/setuid.go b/endpoints/setuid.go
index 905fa26b..25fb2465 100644
--- a/endpoints/setuid.go
+++ b/endpoints/setuid.go
@@ -39,13 +39,6 @@ func NewSetUIDEndpoint(cfg *config.Configuration, syncersByBidder map[string]use
 	encoder := usersync.Base64Encoder{}
 	decoder := usersync.Base64Decoder{}
 
-	// convert map of syncers by bidder to map of syncers by key
-	// - its safe to assume that if multiple bidders map to the same key, the syncers are interchangeable.
-	syncersByKey := make(map[string]usersync.Syncer, len(syncersByBidder))
-	for _, v := range syncersByBidder {
-		syncersByKey[v.Key()] = v
-	}
-
 	return httprouter.Handle(func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
 		so := analytics.SetUIDObject{
 			Status: http.StatusOK,
@@ -65,7 +58,7 @@ func NewSetUIDEndpoint(cfg *config.Configuration, syncersByBidder map[string]use
 
 		query := r.URL.Query()
 
-		syncer, err := getSyncer(query, syncersByKey)
+		syncer, err := getSyncer(query, syncersByBidder)
 		if err != nil {
 			w.WriteHeader(http.StatusBadRequest)
 			w.Write([]byte(err.Error()))
@@ -313,14 +306,14 @@ func parseConsentFromGppStr(gppQueryValue string) (string, error) {
 	return gdprConsent, nil
 }
 
-func getSyncer(query url.Values, syncersByKey map[string]usersync.Syncer) (usersync.Syncer, error) {
-	key := query.Get("bidder")
+func getSyncer(query url.Values, syncersByBidder map[string]usersync.Syncer) (usersync.Syncer, error) {
+	bidder := query.Get("bidder")
 
-	if key == "" {
+	if bidder == "" {
 		return nil, errors.New(`"bidder" query param is required`)
 	}
 
-	syncer, syncerExists := syncersByKey[key]
+	syncer, syncerExists := syncersByBidder[bidder]
 	if !syncerExists {
 		return nil, errors.New("The bidder name provided is not supported by Prebid Server")
 	}
diff --git a/exchange/adapter_builders.go b/exchange/adapter_builders.go
index f8344525..a1038d71 100755
--- a/exchange/adapter_builders.go
+++ b/exchange/adapter_builders.go
@@ -56,7 +56,6 @@ import (
 	"github.com/prebid/prebid-server/adapters/bmtm"
 	"github.com/prebid/prebid-server/adapters/boldwin"
 	"github.com/prebid/prebid-server/adapters/brave"
-	"github.com/prebid/prebid-server/adapters/brightroll"
 	"github.com/prebid/prebid-server/adapters/ccx"
 	"github.com/prebid/prebid-server/adapters/coinzilla"
 	"github.com/prebid/prebid-server/adapters/colossus"
@@ -128,6 +127,7 @@ import (
 	"github.com/prebid/prebid-server/adapters/operaads"
 	"github.com/prebid/prebid-server/adapters/orbidder"
 	"github.com/prebid/prebid-server/adapters/outbrain"
+	"github.com/prebid/prebid-server/adapters/ownadx"
 	"github.com/prebid/prebid-server/adapters/pangle"
 	"github.com/prebid/prebid-server/adapters/pubmatic"
 	"github.com/prebid/prebid-server/adapters/pubnative"
@@ -169,6 +169,7 @@ import (
 	"github.com/prebid/prebid-server/adapters/vidoomy"
 	"github.com/prebid/prebid-server/adapters/visiblemeasures"
 	"github.com/prebid/prebid-server/adapters/visx"
+	"github.com/prebid/prebid-server/adapters/vox"
 	"github.com/prebid/prebid-server/adapters/vrtcal"
 	"github.com/prebid/prebid-server/adapters/xeworks"
 	"github.com/prebid/prebid-server/adapters/yahooAds"
@@ -242,7 +243,6 @@ func newAdapterBuilders() map[openrtb_ext.BidderName]adapters.Builder {
 		openrtb_ext.BidderBmtm:              bmtm.Builder,
 		openrtb_ext.BidderBoldwin:           boldwin.Builder,
 		openrtb_ext.BidderBrave:             brave.Builder,
-		openrtb_ext.BidderBrightroll:        brightroll.Builder,
 		openrtb_ext.BidderCcx:               ccx.Builder,
 		openrtb_ext.BidderCoinzilla:         coinzilla.Builder,
 		openrtb_ext.BidderColossus:          colossus.Builder,
@@ -321,6 +321,7 @@ func newAdapterBuilders() map[openrtb_ext.BidderName]adapters.Builder {
 		openrtb_ext.BidderOperaads:          operaads.Builder,
 		openrtb_ext.BidderOrbidder:          orbidder.Builder,
 		openrtb_ext.BidderOutbrain:          outbrain.Builder,
+		openrtb_ext.BidderOwnAdx:            ownadx.Builder,
 		openrtb_ext.BidderPangle:            pangle.Builder,
 		openrtb_ext.BidderPGAM:              adtelligent.Builder,
 		openrtb_ext.BidderPubmatic:          pubmatic.Builder,
@@ -369,6 +370,7 @@ func newAdapterBuilders() map[openrtb_ext.BidderName]adapters.Builder {
 		openrtb_ext.BidderViewdeos:          adtelligent.Builder,
 		openrtb_ext.BidderVisibleMeasures:   visiblemeasures.Builder,
 		openrtb_ext.BidderVisx:              visx.Builder,
+		openrtb_ext.BidderVox:               vox.Builder,
 		openrtb_ext.BidderVrtcal:            vrtcal.Builder,
 		openrtb_ext.BidderXeworks:           xeworks.Builder,
 		openrtb_ext.BidderYahooAds:          yahooAds.Builder,
diff --git a/exchange/adapter_util.go b/exchange/adapter_util.go
index 9551ab3e..624a22ce 100644
--- a/exchange/adapter_util.go
+++ b/exchange/adapter_util.go
@@ -97,6 +97,7 @@ func GetDisabledBiddersErrorMessages(infos config.BidderInfos) map[string]string
 		"oftmedia":       `Bidder "oftmedia" is no longer available in Prebid Server. Please update your configuration.`,
 		"groupm":         `Bidder "groupm" is no longer available in Prebid Server. Please update your configuration.`,
 		"verizonmedia":   `Bidder "verizonmedia" is no longer available in Prebid Server. Please update your configuration.`,
+		"brightroll":     `Bidder "brightroll" is no longer available in Prebid Server. Please update your configuration.`,
 	}
 
 	for name, info := range infos {
diff --git a/exchange/bidder.go b/exchange/bidder.go
index 27012ff4..5d9d5cba 100644
--- a/exchange/bidder.go
+++ b/exchange/bidder.go
@@ -67,6 +67,7 @@ type bidRequestOptions struct {
 	headerDebugAllowed  bool
 	addCallSignHeader   bool
 	bidAdjustments      map[string]float64
+	tmaxAdjustments     *TmaxAdjustmentsPreprocessed
 }
 
 type extraBidderRespInfo struct {
@@ -143,6 +144,11 @@ func (bidder *bidderAdapter) requestBid(ctx context.Context, bidderRequest Bidde
 	//check if real request exists for this bidder or it only has stored responses
 	dataLen := 0
 	if len(bidderRequest.BidRequest.Imp) > 0 {
+		// Reducing the amount of time bidders have to compensate for the processing time used by PBS to fetch a stored request (if needed), validate the OpenRTB request and split it into multiple requests sanitized for each bidder
+		// As well as for the time needed by PBS to prepare the auction response
+		if bidRequestOptions.tmaxAdjustments != nil && bidRequestOptions.tmaxAdjustments.IsEnforced {
+			bidderRequest.BidRequest.TMax = getBidderTmax(&bidderTmaxCtx{ctx}, bidderRequest.BidRequest.TMax, *bidRequestOptions.tmaxAdjustments)
+		}
 		reqData, errs = bidder.Bidder.MakeRequests(bidderRequest.BidRequest, reqInfo)
 
 		if len(reqData) == 0 {
diff --git a/exchange/exchange.go b/exchange/exchange.go
index 390db973..1436f3ea 100644
--- a/exchange/exchange.go
+++ b/exchange/exchange.go
@@ -6,7 +6,6 @@ import (
 	"encoding/json"
 	"errors"
 	"fmt"
-	"github.com/prebid/prebid-server/privacy"
 	"math/rand"
 	"net/url"
 	"runtime/debug"
@@ -15,6 +14,8 @@ import (
 	"strings"
 	"time"
 
+	"github.com/prebid/prebid-server/privacy"
+
 	"github.com/prebid/prebid-server/adapters"
 	"github.com/prebid/prebid-server/adservertargeting"
 	"github.com/prebid/prebid-server/bidadjustment"
@@ -211,6 +212,7 @@ type AuctionRequest struct {
 	HookExecutor            hookexecution.StageExecutor
 	QueryParams             url.Values
 	BidderResponseStartTime time.Time
+	TmaxAdjustments         *TmaxAdjustmentsPreprocessed
 }
 
 // BidderRequest holds the bidder specific request and all other
@@ -368,7 +370,7 @@ func (e *exchange) HoldAuction(ctx context.Context, r *AuctionRequest, debugLog
 			alternateBidderCodes = *r.Account.AlternateBidderCodes
 		}
 		var extraRespInfo extraAuctionResponseInfo
-		adapterBids, adapterExtra, extraRespInfo = e.getAllBids(auctionCtx, bidderRequests, bidAdjustmentFactors, conversions, accountDebugAllow, r.GlobalPrivacyControlHeader, debugLog.DebugOverride, alternateBidderCodes, requestExtLegacy.Prebid.Experiment, r.HookExecutor, r.StartTime, bidAdjustmentRules)
+		adapterBids, adapterExtra, extraRespInfo = e.getAllBids(auctionCtx, bidderRequests, bidAdjustmentFactors, conversions, accountDebugAllow, r.GlobalPrivacyControlHeader, debugLog.DebugOverride, alternateBidderCodes, requestExtLegacy.Prebid.Experiment, r.HookExecutor, r.StartTime, bidAdjustmentRules, r.TmaxAdjustments)
 		fledge = extraRespInfo.fledge
 		anyBidsReturned = extraRespInfo.bidsFound
 		r.BidderResponseStartTime = extraRespInfo.bidderResponseStartTime
@@ -660,7 +662,8 @@ func (e *exchange) getAllBids(
 	experiment *openrtb_ext.Experiment,
 	hookExecutor hookexecution.StageExecutor,
 	pbsRequestStartTime time.Time,
-	bidAdjustmentRules map[string][]openrtb_ext.Adjustment) (
+	bidAdjustmentRules map[string][]openrtb_ext.Adjustment,
+	tmaxAdjustments *TmaxAdjustmentsPreprocessed) (
 	map[openrtb_ext.BidderName]*entities.PbsOrtbSeatBid,
 	map[openrtb_ext.BidderName]*seatResponseExtra,
 	extraAuctionResponseInfo) {
@@ -699,6 +702,7 @@ func (e *exchange) getAllBids(
 				headerDebugAllowed:  headerDebugAllowed,
 				addCallSignHeader:   isAdsCertEnabled(experiment, e.bidderInfo[string(bidderRequest.BidderName)]),
 				bidAdjustments:      bidAdjustments,
+				tmaxAdjustments:     tmaxAdjustments,
 			}
 			seatBids, extraBidderRespInfo, err := e.adapterMap[bidderRequest.BidderCoreName].requestBid(ctx, bidderRequest, conversions, &reqInfo, e.adsCertSigner, bidReqOptions, alternateBidderCodes, hookExecutor, bidAdjustmentRules)
 			brw.bidderResponseStartTime = extraBidderRespInfo.respProcessingStartTime
diff --git a/exchange/tmax_adjustments.go b/exchange/tmax_adjustments.go
new file mode 100644
index 00000000..86934ecc
--- /dev/null
+++ b/exchange/tmax_adjustments.go
@@ -0,0 +1,57 @@
+package exchange
+
+import (
+	"context"
+	"time"
+
+	"github.com/prebid/prebid-server/config"
+)
+
+type TmaxAdjustmentsPreprocessed struct {
+	BidderNetworkLatencyBuffer     uint
+	PBSResponsePreparationDuration uint
+	BidderResponseDurationMin      uint
+
+	IsEnforced bool
+}
+
+func ProcessTMaxAdjustments(adjustmentsConfig config.TmaxAdjustments) *TmaxAdjustmentsPreprocessed {
+	if !adjustmentsConfig.Enabled {
+		return nil
+	}
+
+	isEnforced := adjustmentsConfig.BidderResponseDurationMin != 0 &&
+		(adjustmentsConfig.BidderNetworkLatencyBuffer != 0 || adjustmentsConfig.PBSResponsePreparationDuration != 0)
+
+	tmax := &TmaxAdjustmentsPreprocessed{
+		BidderNetworkLatencyBuffer:     adjustmentsConfig.BidderNetworkLatencyBuffer,
+		PBSResponsePreparationDuration: adjustmentsConfig.PBSResponsePreparationDuration,
+		BidderResponseDurationMin:      adjustmentsConfig.BidderResponseDurationMin,
+		IsEnforced:                     isEnforced,
+	}
+
+	return tmax
+}
+
+type bidderTmaxContext interface {
+	Deadline() (deadline time.Time, ok bool)
+	RemainingDurationMS(deadline time.Time) int64
+}
+type bidderTmaxCtx struct{ ctx context.Context }
+
+func (b *bidderTmaxCtx) RemainingDurationMS(deadline time.Time) int64 {
+	return time.Until(deadline).Milliseconds()
+}
+func (b *bidderTmaxCtx) Deadline() (deadline time.Time, ok bool) {
+	deadline, ok = b.ctx.Deadline()
+	return
+}
+
+func getBidderTmax(ctx bidderTmaxContext, requestTmaxMS int64, tmaxAdjustments TmaxAdjustmentsPreprocessed) int64 {
+	if tmaxAdjustments.IsEnforced {
+		if deadline, ok := ctx.Deadline(); ok {
+			return ctx.RemainingDurationMS(deadline) - int64(tmaxAdjustments.BidderNetworkLatencyBuffer) - int64(tmaxAdjustments.PBSResponsePreparationDuration)
+		}
+	}
+	return requestTmaxMS
+}
diff --git a/exchange/utils.go b/exchange/utils.go
index 5b29fd62..e70f5249 100644
--- a/exchange/utils.go
+++ b/exchange/utils.go
@@ -112,15 +112,13 @@ func (rs *requestSplitter) cleanOpenRTBRequests(ctx context.Context,
 	lmtEnforcer := extractLMT(req.BidRequest, rs.privacyConfig)
 
 	// request level privacy policies
-	privacyEnforcement := privacy.Enforcement{
-		COPPA: req.BidRequest.Regs != nil && req.BidRequest.Regs.COPPA == 1,
-		LMT:   lmtEnforcer.ShouldEnforce(unknownBidder),
-	}
+	coppa := req.BidRequest.Regs != nil && req.BidRequest.Regs.COPPA == 1
+	lmt := lmtEnforcer.ShouldEnforce(unknownBidder)
 
 	privacyLabels.CCPAProvided = ccpaEnforcer.CanEnforce()
 	privacyLabels.CCPAEnforced = ccpaEnforcer.ShouldEnforce(unknownBidder)
-	privacyLabels.COPPAEnforced = privacyEnforcement.COPPA
-	privacyLabels.LMTEnforced = lmtEnforcer.ShouldEnforce(unknownBidder)
+	privacyLabels.COPPAEnforced = coppa
+	privacyLabels.LMTEnforced = lmt
 
 	var gdprEnforced bool
 	var gdprPerms gdpr.Permissions = &gdpr.AlwaysAllow{}
@@ -148,46 +146,75 @@ func (rs *requestSplitter) cleanOpenRTBRequests(ctx context.Context,
 
 	// bidder level privacy policies
 	for _, bidderRequest := range allBidderRequests {
-		bidRequestAllowed := true
+		privacyEnforcement := privacy.Enforcement{
+			COPPA: coppa,
+			LMT:   lmt,
+		}
 
 		// fetchBids activity
-		fetchBidsActivityAllowed := auctionReq.Activities.Allow(privacy.ActivityFetchBids,
-			privacy.ScopedName{Scope: privacy.ScopeTypeBidder, Name: bidderRequest.BidderName.String()})
+		scopedName := privacy.ScopedName{Scope: privacy.ScopeTypeBidder, Name: bidderRequest.BidderName.String()}
+		fetchBidsActivityAllowed := auctionReq.Activities.Allow(privacy.ActivityFetchBids, scopedName)
 		if fetchBidsActivityAllowed == privacy.ActivityDeny {
 			// skip the call to a bidder if fetchBids activity is not allowed
 			// do not add this bidder to allowedBidderRequests
 			continue
 		}
 
-		// CCPA
-		privacyEnforcement.CCPA = ccpaEnforcer.ShouldEnforce(bidderRequest.BidderName.String())
+		var auctionPermissions gdpr.AuctionPermissions
+		var gdprErr error
 
-		// GDPR
 		if gdprEnforced {
-			auctionPermissions, err := gdprPerms.AuctionActivitiesAllowed(ctx, bidderRequest.BidderCoreName, bidderRequest.BidderName)
-			bidRequestAllowed = auctionPermissions.AllowBidRequest
-
-			if err == nil {
-				privacyEnforcement.GDPRGeo = !auctionPermissions.PassGeo
-				privacyEnforcement.GDPRID = !auctionPermissions.PassID
-			} else {
-				privacyEnforcement.GDPRGeo = true
-				privacyEnforcement.GDPRID = true
+			auctionPermissions, gdprErr = gdprPerms.AuctionActivitiesAllowed(ctx, bidderRequest.BidderCoreName, bidderRequest.BidderName)
+			if !auctionPermissions.AllowBidRequest {
+				// auction request is not permitted by GDPR
+				// do not add this bidder to allowedBidderRequests
+				rs.me.RecordAdapterGDPRRequestBlocked(bidderRequest.BidderCoreName)
+				continue
 			}
+		}
 
-			if !bidRequestAllowed {
-				rs.me.RecordAdapterGDPRRequestBlocked(bidderRequest.BidderCoreName)
+		passIDActivityAllowed := auctionReq.Activities.Allow(privacy.ActivityTransmitUserFPD, scopedName)
+		if passIDActivityAllowed == privacy.ActivityDeny {
+			privacyEnforcement.UFPD = true
+		} else {
+			// run existing policies (GDPR, CCPA, COPPA, LMT)
+			// potentially block passing IDs based on GDPR
+			if gdprEnforced {
+				if gdprErr == nil {
+					privacyEnforcement.GDPRID = !auctionPermissions.PassID
+				} else {
+					privacyEnforcement.GDPRID = true
+				}
 			}
+			// potentially block passing IDs based on CCPA
+			privacyEnforcement.CCPA = ccpaEnforcer.ShouldEnforce(bidderRequest.BidderName.String())
+		}
+
+		passGeoActivityAllowed := auctionReq.Activities.Allow(privacy.ActivityTransmitPreciseGeo, scopedName)
+		if passGeoActivityAllowed == privacy.ActivityDeny {
+			privacyEnforcement.PreciseGeo = true
+		} else {
+			// run existing policies (GDPR, CCPA, COPPA, LMT)
+			// potentially block passing geo based on GDPR
+			if gdprEnforced {
+				if gdprErr == nil {
+					privacyEnforcement.GDPRGeo = !auctionPermissions.PassGeo
+				} else {
+					privacyEnforcement.GDPRGeo = true
+				}
+			}
+			// potentially block passing geo based on CCPA
+			privacyEnforcement.CCPA = ccpaEnforcer.ShouldEnforce(bidderRequest.BidderName.String())
+
 		}
 
 		if auctionReq.FirstPartyData != nil && auctionReq.FirstPartyData[bidderRequest.BidderName] != nil {
 			applyFPD(auctionReq.FirstPartyData[bidderRequest.BidderName], bidderRequest.BidRequest)
 		}
 
-		if bidRequestAllowed {
-			privacyEnforcement.Apply(bidderRequest.BidRequest)
-			allowedBidderRequests = append(allowedBidderRequests, bidderRequest)
-		}
+		privacyEnforcement.Apply(bidderRequest.BidRequest)
+		allowedBidderRequests = append(allowedBidderRequests, bidderRequest)
+
 		// GPP downgrade: always downgrade unless we can confirm GPP is supported
 		if shouldSetLegacyPrivacy(rs.bidderInfo, string(bidderRequest.BidderCoreName)) {
 			setLegacyGDPRFromGPP(bidderRequest.BidRequest, gpp)
@@ -363,12 +390,13 @@ func buildRequestExtForBidder(bidder string, requestExt json.RawMessage, request
 	}
 
 	if requestExtParsed != nil {
-		prebid.CurrencyConversions = requestExtParsed.Prebid.CurrencyConversions
-		prebid.Integration = requestExtParsed.Prebid.Integration
 		prebid.Channel = requestExtParsed.Prebid.Channel
+		prebid.CurrencyConversions = requestExtParsed.Prebid.CurrencyConversions
 		prebid.Debug = requestExtParsed.Prebid.Debug
-		prebid.Server = requestExtParsed.Prebid.Server
+		prebid.Integration = requestExtParsed.Prebid.Integration
 		prebid.MultiBid = buildRequestExtMultiBid(bidder, requestExtParsed.Prebid.MultiBid, alternateBidderCodes)
+		prebid.Sdk = requestExtParsed.Prebid.Sdk
+		prebid.Server = requestExtParsed.Prebid.Server
 	}
 
 	// Marshal New Prebid Object
diff --git a/openrtb_ext/bid.go b/openrtb_ext/bid.go
index 20bbf0b3..7dc620d4 100644
--- a/openrtb_ext/bid.go
+++ b/openrtb_ext/bid.go
@@ -50,6 +50,7 @@ type ExtBidPrebidCacheBids struct {
 
 // ExtBidPrebidMeta defines the contract for bidresponse.seatbid.bid[i].ext.prebid.meta
 type ExtBidPrebidMeta struct {
+	AdapterCode          string          `json:"adaptercode,omitempty"`
 	AdvertiserDomains    []string        `json:"advertiserDomains,omitempty"`
 	AdvertiserID         int             `json:"advertiserId,omitempty"`
 	AdvertiserName       string          `json:"advertiserName,omitempty"`
@@ -57,14 +58,15 @@ type ExtBidPrebidMeta struct {
 	AgencyName           string          `json:"agencyName,omitempty"`
 	BrandID              int             `json:"brandId,omitempty"`
 	BrandName            string          `json:"brandName,omitempty"`
-	DemandSource         string          `json:"demandSource,omitempty"`
 	DChain               json.RawMessage `json:"dchain,omitempty"`
+	DemandSource         string          `json:"demandSource,omitempty"`
 	MediaType            string          `json:"mediaType,omitempty"`
 	NetworkID            int             `json:"networkId,omitempty"`
 	NetworkName          string          `json:"networkName,omitempty"`
 	PrimaryCategoryID    string          `json:"primaryCatId,omitempty"`
+	RendererName         string          `json:"rendererName,omitempty"`
+	RendererVersion      string          `json:"rendererVersion,omitempty"`
 	SecondaryCategoryIDs []string        `json:"secondaryCatIds,omitempty"`
-	AdapterCode          string          `json:"adaptercode,omitempty"`
 }
 
 // ExtBidPrebidVideo defines the contract for bidresponse.seatbid.bid[i].ext.prebid.video
diff --git a/openrtb_ext/bidders.go b/openrtb_ext/bidders.go
index 978af5e0..e9cc81f8 100644
--- a/openrtb_ext/bidders.go
+++ b/openrtb_ext/bidders.go
@@ -142,7 +142,6 @@ const (
 	BidderBmtm              BidderName = "bmtm"
 	BidderBoldwin           BidderName = "boldwin"
 	BidderBrave             BidderName = "brave"
-	BidderBrightroll        BidderName = "brightroll"
 	BidderCcx               BidderName = "ccx"
 	BidderCoinzilla         BidderName = "coinzilla"
 	BidderColossus          BidderName = "colossus"
@@ -221,6 +220,7 @@ const (
 	BidderOperaads          BidderName = "operaads"
 	BidderOrbidder          BidderName = "orbidder"
 	BidderOutbrain          BidderName = "outbrain"
+	BidderOwnAdx            BidderName = "ownadx"
 	BidderPangle            BidderName = "pangle"
 	BidderPGAM              BidderName = "pgam"
 	BidderPubmatic          BidderName = "pubmatic"
@@ -269,6 +269,7 @@ const (
 	BidderViewdeos          BidderName = "viewdeos"
 	BidderVisibleMeasures   BidderName = "visiblemeasures"
 	BidderVisx              BidderName = "visx"
+	BidderVox               BidderName = "vox"
 	BidderVrtcal            BidderName = "vrtcal"
 	BidderXeworks           BidderName = "xeworks"
 	BidderYahooAds          BidderName = "yahooAds"
@@ -341,7 +342,6 @@ func CoreBidderNames() []BidderName {
 		BidderBmtm,
 		BidderBoldwin,
 		BidderBrave,
-		BidderBrightroll,
 		BidderCcx,
 		BidderCoinzilla,
 		BidderColossus,
@@ -420,6 +420,7 @@ func CoreBidderNames() []BidderName {
 		BidderOperaads,
 		BidderOrbidder,
 		BidderOutbrain,
+		BidderOwnAdx,
 		BidderPangle,
 		BidderPGAM,
 		BidderPubmatic,
@@ -468,6 +469,7 @@ func CoreBidderNames() []BidderName {
 		BidderViewdeos,
 		BidderVisibleMeasures,
 		BidderVisx,
+		BidderVox,
 		BidderVrtcal,
 		BidderXeworks,
 		BidderYahooAds,
diff --git a/openrtb_ext/imp_brightroll.go b/openrtb_ext/imp_brightroll.go
deleted file mode 100644
index bd42d4aa..00000000
--- a/openrtb_ext/imp_brightroll.go
+++ /dev/null
@@ -1,6 +0,0 @@
-package openrtb_ext
-
-// ExtImpBrightroll defines the contract for bidrequest.imp[i].ext.prebid.bidder.brightroll
-type ExtImpBrightroll struct {
-	Publisher string `json:"publisher"`
-}
diff --git a/openrtb_ext/imp_ownadx.go b/openrtb_ext/imp_ownadx.go
new file mode 100644
index 00000000..caac600b
--- /dev/null
+++ b/openrtb_ext/imp_ownadx.go
@@ -0,0 +1,7 @@
+package openrtb_ext
+
+type ExtImpOwnAdx struct {
+	SspId   string `json:"sspId"`
+	SeatId  string `json:"seatId"`
+	TokenId string `json:"tokenId"`
+}
diff --git a/openrtb_ext/imp_rtbhouse.go b/openrtb_ext/imp_rtbhouse.go
new file mode 100644
index 00000000..747ea158
--- /dev/null
+++ b/openrtb_ext/imp_rtbhouse.go
@@ -0,0 +1,10 @@
+package openrtb_ext
+
+// ExtImpRTBHouse defines the contract for bidrequest.imp[i].ext.prebid.bidder.rtbhouse
+type ExtImpRTBHouse struct {
+	PublisherId string `json:"publisherId"`
+	Region      string `json:"region"`
+
+	BidFloor float64 `json:"bidfloor,omitempty"`
+	Channel  string  `json:"channel,omitempty"`
+}
diff --git a/openrtb_ext/imp_vox.go b/openrtb_ext/imp_vox.go
new file mode 100644
index 00000000..a6152507
--- /dev/null
+++ b/openrtb_ext/imp_vox.go
@@ -0,0 +1,7 @@
+package openrtb_ext
+
+type ImpExtVox struct {
+	PlacementID  string   `json:"placementId"`
+	ImageUrl     string   `json:"imageUrl"`
+	DisplaySizes []string `json:"displaySizes"`
+}
diff --git a/openrtb_ext/request.go b/openrtb_ext/request.go
index adf00fcd..6f39f0be 100644
--- a/openrtb_ext/request.go
+++ b/openrtb_ext/request.go
@@ -41,52 +41,54 @@ type ExtRequest struct {
 
 // ExtRequestPrebid defines the contract for bidrequest.ext.prebid
 type ExtRequestPrebid struct {
-	Aliases              map[string]string         `json:"aliases,omitempty"`
-	AliasGVLIDs          map[string]uint16         `json:"aliasgvlids,omitempty"`
-	BidAdjustmentFactors map[string]float64        `json:"bidadjustmentfactors,omitempty"`
-	BidderConfigs        []BidderConfig            `json:"bidderconfig,omitempty"`
-	BidderParams         json.RawMessage           `json:"bidderparams,omitempty"`
-	Cache                *ExtRequestPrebidCache    `json:"cache,omitempty"`
-	Channel              *ExtRequestPrebidChannel  `json:"channel,omitempty"`
-	CurrencyConversions  *ExtRequestCurrency       `json:"currency,omitempty"`
-	Data                 *ExtRequestPrebidData     `json:"data,omitempty"`
-	Debug                bool                      `json:"debug,omitempty"`
-	Events               json.RawMessage           `json:"events,omitempty"`
-	Experiment           *Experiment               `json:"experiment,omitempty"`
-	Integration          string                    `json:"integration,omitempty"`
-	MultiBid             []*ExtMultiBid            `json:"multibid,omitempty"`
-	Passthrough          json.RawMessage           `json:"passthrough,omitempty"`
-	SChains              []*ExtRequestPrebidSChain `json:"schains,omitempty"`
-	Server               *ExtRequestPrebidServer   `json:"server,omitempty"`
-	StoredRequest        *ExtStoredRequest         `json:"storedrequest,omitempty"`
-	SupportDeals         bool                      `json:"supportdeals,omitempty"`
-	Targeting            *ExtRequestTargeting      `json:"targeting,omitempty"`
+	AdServerTargeting    []AdServerTarget                `json:"adservertargeting,omitempty"`
+	Aliases              map[string]string               `json:"aliases,omitempty"`
+	AliasGVLIDs          map[string]uint16               `json:"aliasgvlids,omitempty"`
+	BidAdjustmentFactors map[string]float64              `json:"bidadjustmentfactors,omitempty"`
+	BidAdjustments       *ExtRequestPrebidBidAdjustments `json:"bidadjustments,omitempty"`
+	BidderConfigs        []BidderConfig                  `json:"bidderconfig,omitempty"`
+	BidderParams         json.RawMessage                 `json:"bidderparams,omitempty"`
+	Cache                *ExtRequestPrebidCache          `json:"cache,omitempty"`
+	Channel              *ExtRequestPrebidChannel        `json:"channel,omitempty"`
+	CurrencyConversions  *ExtRequestCurrency             `json:"currency,omitempty"`
+	Data                 *ExtRequestPrebidData           `json:"data,omitempty"`
+	Debug                bool                            `json:"debug,omitempty"`
+	Events               json.RawMessage                 `json:"events,omitempty"`
+	Experiment           *Experiment                     `json:"experiment,omitempty"`
+	Floors               *PriceFloorRules                `json:"floors,omitempty"`
+	Integration          string                          `json:"integration,omitempty"`
+	MultiBid             []*ExtMultiBid                  `json:"multibid,omitempty"`
+	MultiBidMap          map[string]ExtMultiBid          `json:"-"`
+	Passthrough          json.RawMessage                 `json:"passthrough,omitempty"`
+	SChains              []*ExtRequestPrebidSChain       `json:"schains,omitempty"`
+	Sdk                  *ExtRequestSdk                  `json:"sdk,omitempty"`
+	Server               *ExtRequestPrebidServer         `json:"server,omitempty"`
+	StoredRequest        *ExtStoredRequest               `json:"storedrequest,omitempty"`
+	SupportDeals         bool                            `json:"supportdeals,omitempty"`
+	Targeting            *ExtRequestTargeting            `json:"targeting,omitempty"`
+
+	//AlternateBidderCodes is populated with host's AlternateBidderCodes config if not defined in request
+	AlternateBidderCodes *ExtAlternateBidderCodes `json:"alternatebiddercodes,omitempty"`
+
+	// Macros specifies list of custom macros along with the values. This is used while forming
+	// the tracker URLs, where PBS will replace the Custom Macro with its value with url-encoding
+	Macros map[string]string `json:"macros,omitempty"`
 
 	// NoSale specifies bidders with whom the publisher has a legal relationship where the
 	// passing of personally identifiable information doesn't constitute a sale per CCPA law.
 	// The array may contain a single sstar ('*') entry to represent all bidders.
 	NoSale []string `json:"nosale,omitempty"`
 
-	//AlternateBidderCodes is populated with host's AlternateBidderCodes config if not defined in request
-	AlternateBidderCodes *ExtAlternateBidderCodes `json:"alternatebiddercodes,omitempty"`
+	// ReturnAllBidStatus if true populates bidresponse.ext.prebid.seatnonbid with all bids which was
+	// either rejected, nobid, input error
+	ReturnAllBidStatus bool `json:"returnallbidstatus,omitempty"`
 
-	Floors      *PriceFloorRules       `json:"floors,omitempty"`
-	MultiBidMap map[string]ExtMultiBid `json:"-"`
 	// Trace controls the level of detail in the output information returned from executing hooks.
 	// There are two options:
 	// - verbose: sets maximum level of output information
 	// - basic: excludes debugmessages and analytic_tags from output
 	// any other value or an empty string disables trace output at all.
 	Trace string `json:"trace,omitempty"`
-
-	// Macros specifies list of custom macros along with the values. This is used while forming
-	// the tracker URLs, where PBS will replace the Custom Macro with its value with url-encoding
-	Macros            map[string]string               `json:"macros,omitempty"`
-	AdServerTargeting []AdServerTarget                `json:"adservertargeting,omitempty"`
-	BidAdjustments    *ExtRequestPrebidBidAdjustments `json:"bidadjustments,omitempty"`
-	// ReturnAllBidStatus if true populates bidresponse.ext.prebid.seatnonbid with all bids which was
-	// either rejected, nobid, input error
-	ReturnAllBidStatus bool `json:"returnallbidstatus,omitempty"`
 }
 
 type AdServerTarget struct {
@@ -345,6 +347,15 @@ type ExtRequestPrebidDataEidPermission struct {
 	Bidders []string `json:"bidders"`
 }
 
+type ExtRequestSdk struct {
+	Renderers []ExtRequestSdkRenderer `json:"renderers,omitempty"`
+}
+
+type ExtRequestSdkRenderer struct {
+	Name    string `json:"name,omitempty"`
+	Version string `json:"version,omitempty"`
+}
+
 type ExtMultiBid struct {
 	Bidder                 string   `json:"bidder,omitempty"`
 	Bidders                []string `json:"bidders,omitempty"`
diff --git a/privacy/enforcement.go b/privacy/enforcement.go
index 50d42e89..1a66cfab 100644
--- a/privacy/enforcement.go
+++ b/privacy/enforcement.go
@@ -9,22 +9,39 @@ type Enforcement struct {
 	GDPRGeo bool
 	GDPRID  bool
 	LMT     bool
+
+	// activities
+	UFPD       bool
+	Eids       bool
+	PreciseGeo bool
+	TID        bool
 }
 
 // Any returns true if at least one privacy policy requires enforcement.
-func (e Enforcement) Any() bool {
+func (e Enforcement) AnyLegacy() bool {
 	return e.CCPA || e.COPPA || e.GDPRGeo || e.GDPRID || e.LMT
 }
 
+func (e Enforcement) AnyActivities() bool {
+	return e.UFPD || e.PreciseGeo || e.Eids || e.TID
+}
+
 // Apply cleans personally identifiable information from an OpenRTB bid request.
 func (e Enforcement) Apply(bidRequest *openrtb2.BidRequest) {
 	e.apply(bidRequest, NewScrubber())
 }
 
 func (e Enforcement) apply(bidRequest *openrtb2.BidRequest, scrubber Scrubber) {
-	if bidRequest != nil && e.Any() {
-		bidRequest.Device = scrubber.ScrubDevice(bidRequest.Device, e.getDeviceIDScrubStrategy(), e.getIPv4ScrubStrategy(), e.getIPv6ScrubStrategy(), e.getGeoScrubStrategy())
-		bidRequest.User = scrubber.ScrubUser(bidRequest.User, e.getUserScrubStrategy(), e.getGeoScrubStrategy())
+	if bidRequest != nil {
+		if e.AnyActivities() {
+			bidRequest = scrubber.ScrubRequest(bidRequest, e)
+		}
+		if e.AnyLegacy() && !(e.UFPD && e.PreciseGeo && e.Eids) {
+			bidRequest.User = scrubber.ScrubUser(bidRequest.User, e.getUserScrubStrategy(), e.getGeoScrubStrategy())
+		}
+		if e.AnyLegacy() && !(e.UFPD && e.PreciseGeo) {
+			bidRequest.Device = scrubber.ScrubDevice(bidRequest.Device, e.getDeviceIDScrubStrategy(), e.getIPv4ScrubStrategy(), e.getIPv6ScrubStrategy(), e.getGeoScrubStrategy())
+		}
 	}
 }
 
diff --git a/privacy/enforcer.go b/privacy/enforcer.go
index d63cd8de..a8685e29 100644
--- a/privacy/enforcer.go
+++ b/privacy/enforcer.go
@@ -3,7 +3,6 @@ package privacy
 import (
 	"fmt"
 	"github.com/prebid/prebid-server/config"
-	"github.com/prebid/prebid-server/errortypes"
 	"strings"
 )
 
@@ -33,9 +32,6 @@ func NewActivityControl(privacyConf *config.AccountPrivacy) (ActivityControl, er
 
 	if privacyConf == nil {
 		return ac, err
-	} else {
-		//temporarily disable Activities if they are specified at the account level
-		return ac, &errortypes.Warning{Message: "account.Privacy has no effect as the feature is under development."}
 	}
 
 	plans := make(map[Activity]ActivityPlan)
diff --git a/privacy/scrubber.go b/privacy/scrubber.go
index 428193bc..97419128 100644
--- a/privacy/scrubber.go
+++ b/privacy/scrubber.go
@@ -2,6 +2,7 @@ package privacy
 
 import (
 	"encoding/json"
+	"github.com/prebid/prebid-server/util/ptrutil"
 	"strings"
 
 	"github.com/prebid/openrtb/v19/openrtb2"
@@ -70,6 +71,7 @@ const (
 
 // Scrubber removes PII from parts of an OpenRTB request.
 type Scrubber interface {
+	ScrubRequest(bidRequest *openrtb2.BidRequest, enforcement Enforcement) *openrtb2.BidRequest
 	ScrubDevice(device *openrtb2.Device, id ScrubStrategyDeviceID, ipv4 ScrubStrategyIPV4, ipv6 ScrubStrategyIPV6, geo ScrubStrategyGeo) *openrtb2.Device
 	ScrubUser(user *openrtb2.User, strategy ScrubStrategyUser, geo ScrubStrategyGeo) *openrtb2.User
 }
@@ -81,6 +83,99 @@ func NewScrubber() Scrubber {
 	return scrubber{}
 }
 
+func (scrubber) ScrubRequest(bidRequest *openrtb2.BidRequest, enforcement Enforcement) *openrtb2.BidRequest {
+	var userExtParsed map[string]json.RawMessage
+	userExtModified := false
+
+	var userCopy *openrtb2.User
+	userCopy = ptrutil.Clone(bidRequest.User)
+
+	var deviceCopy *openrtb2.Device
+	deviceCopy = ptrutil.Clone(bidRequest.Device)
+
+	if userCopy != nil && (enforcement.UFPD || enforcement.Eids) {
+		if len(userCopy.Ext) != 0 {
+			json.Unmarshal(userCopy.Ext, &userExtParsed)
+		}
+	}
+
+	if enforcement.UFPD {
+		// transmitUfpd covers user.ext.data, user.data, user.id, user.buyeruid, user.yob, user.gender, user.keywords, user.kwarray
+		// and device.{ifa, macsha1, macmd5, dpidsha1, dpidmd5, didsha1, didmd5}
+		if deviceCopy != nil {
+			deviceCopy.DIDMD5 = ""
+			deviceCopy.DIDSHA1 = ""
+			deviceCopy.DPIDMD5 = ""
+			deviceCopy.DPIDSHA1 = ""
+			deviceCopy.IFA = ""
+			deviceCopy.MACMD5 = ""
+			deviceCopy.MACSHA1 = ""
+		}
+		if userCopy != nil {
+			userCopy.Data = nil
+			userCopy.ID = ""
+			userCopy.BuyerUID = ""
+			userCopy.Yob = 0
+			userCopy.Gender = ""
+			userCopy.Keywords = ""
+			userCopy.KwArray = nil
+
+			_, hasField := userExtParsed["data"]
+			if hasField {
+				delete(userExtParsed, "data")
+				userExtModified = true
+			}
+		}
+	}
+	if enforcement.Eids {
+		//transmitEids covers user.eids and user.ext.eids
+		if userCopy != nil {
+			userCopy.EIDs = nil
+			_, hasField := userExtParsed["eids"]
+			if hasField {
+				delete(userExtParsed, "eids")
+				userExtModified = true
+			}
+		}
+	}
+
+	if userExtModified {
+		userExt, _ := json.Marshal(userExtParsed)
+		userCopy.Ext = userExt
+	}
+
+	if enforcement.TID {
+		//remove source.tid and imp.ext.tid
+		if bidRequest.Source != nil {
+			bidRequest.Source.TID = ""
+		}
+		for ind, imp := range bidRequest.Imp {
+			impExt := scrubExtIDs(imp.Ext, "tid")
+			bidRequest.Imp[ind].Ext = impExt
+		}
+	}
+
+	if enforcement.PreciseGeo {
+		//round user's geographic location by rounding off IP address and lat/lng data.
+		//this applies to both device.geo and user.geo
+		if userCopy != nil && userCopy.Geo != nil {
+			userCopy.Geo = scrubGeoPrecision(userCopy.Geo)
+		}
+
+		if deviceCopy != nil {
+			if deviceCopy.Geo != nil {
+				deviceCopy.Geo = scrubGeoPrecision(deviceCopy.Geo)
+			}
+			deviceCopy.IP = scrubIPV4Lowest8(deviceCopy.IP)
+			deviceCopy.IPv6 = scrubIPV6Lowest32Bits(deviceCopy.IPv6)
+		}
+	}
+
+	bidRequest.Device = deviceCopy
+	bidRequest.User = userCopy
+	return bidRequest
+}
+
 func (scrubber) ScrubDevice(device *openrtb2.Device, id ScrubStrategyDeviceID, ipv4 ScrubStrategyIPV4, ipv6 ScrubStrategyIPV6, geo ScrubStrategyGeo) *openrtb2.Device {
 	if device == nil {
 		return nil
@@ -131,7 +226,7 @@ func (scrubber) ScrubUser(user *openrtb2.User, strategy ScrubStrategyUser, geo S
 	if strategy == ScrubStrategyUserIDAndDemographic {
 		userCopy.BuyerUID = ""
 		userCopy.ID = ""
-		userCopy.Ext = scrubUserExtIDs(userCopy.Ext)
+		userCopy.Ext = scrubExtIDs(userCopy.Ext, "eids")
 		userCopy.Yob = 0
 		userCopy.Gender = ""
 	}
@@ -205,25 +300,25 @@ func scrubGeoPrecision(geo *openrtb2.Geo) *openrtb2.Geo {
 	return &geoCopy
 }
 
-func scrubUserExtIDs(userExt json.RawMessage) json.RawMessage {
-	if len(userExt) == 0 {
-		return userExt
+func scrubExtIDs(ext json.RawMessage, fieldName string) json.RawMessage {
+	if len(ext) == 0 {
+		return ext
 	}
 
 	var userExtParsed map[string]json.RawMessage
-	err := json.Unmarshal(userExt, &userExtParsed)
+	err := json.Unmarshal(ext, &userExtParsed)
 	if err != nil {
-		return userExt
+		return ext
 	}
 
-	_, hasEids := userExtParsed["eids"]
-	if hasEids {
-		delete(userExtParsed, "eids")
+	_, hasField := userExtParsed[fieldName]
+	if hasField {
+		delete(userExtParsed, fieldName)
 		result, err := json.Marshal(userExtParsed)
 		if err == nil {
 			return result
 		}
 	}
 
-	return userExt
+	return ext
 }
diff --git a/router/router.go b/router/router.go
index 8fc99589..12bfbf59 100644
--- a/router/router.go
+++ b/router/router.go
@@ -214,21 +214,22 @@ func New(cfg *config.Configuration, rateConvertor *currency.RateConverter) (r *R
 		glog.Fatalf("Failed to create ads cert signer: %v", err)
 	}
 
+	tmaxAdjustments := exchange.ProcessTMaxAdjustments(cfg.TmaxAdjustments)
 	planBuilder := hooks.NewExecutionPlanBuilder(cfg.Hooks, repo)
 	macroReplacer := macros.NewStringIndexBasedReplacer()
 	theExchange := exchange.NewExchange(adapters, cacheClient, cfg, syncersByBidder, r.MetricsEngine, cfg.BidderInfos, gdprPermsBuilder, rateConvertor, categoriesFetcher, adsCertSigner, macroReplacer)
 	var uuidGenerator uuidutil.UUIDRandomGenerator
-	openrtbEndpoint, err := openrtb2.NewEndpoint(uuidGenerator, theExchange, paramsValidator, fetcher, accounts, cfg, r.MetricsEngine, pbsAnalytics, disabledBidders, defReqJSON, activeBidders, storedRespFetcher, planBuilder)
+	openrtbEndpoint, err := openrtb2.NewEndpoint(uuidGenerator, theExchange, paramsValidator, fetcher, accounts, cfg, r.MetricsEngine, pbsAnalytics, disabledBidders, defReqJSON, activeBidders, storedRespFetcher, planBuilder, tmaxAdjustments)
 	if err != nil {
 		glog.Fatalf("Failed to create the openrtb2 endpoint handler. %v", err)
 	}
 
-	ampEndpoint, err := openrtb2.NewAmpEndpoint(uuidGenerator, theExchange, paramsValidator, ampFetcher, accounts, cfg, r.MetricsEngine, pbsAnalytics, disabledBidders, defReqJSON, activeBidders, storedRespFetcher, planBuilder)
+	ampEndpoint, err := openrtb2.NewAmpEndpoint(uuidGenerator, theExchange, paramsValidator, ampFetcher, accounts, cfg, r.MetricsEngine, pbsAnalytics, disabledBidders, defReqJSON, activeBidders, storedRespFetcher, planBuilder, tmaxAdjustments)
 	if err != nil {
 		glog.Fatalf("Failed to create the amp endpoint handler. %v", err)
 	}
 
-	videoEndpoint, err := openrtb2.NewVideoEndpoint(uuidGenerator, theExchange, paramsValidator, fetcher, videoFetcher, accounts, cfg, r.MetricsEngine, pbsAnalytics, disabledBidders, defReqJSON, activeBidders, cacheClient)
+	videoEndpoint, err := openrtb2.NewVideoEndpoint(uuidGenerator, theExchange, paramsValidator, fetcher, videoFetcher, accounts, cfg, r.MetricsEngine, pbsAnalytics, disabledBidders, defReqJSON, activeBidders, cacheClient, tmaxAdjustments)
 	if err != nil {
 		glog.Fatalf("Failed to create the video endpoint handler. %v", err)
 	}
diff --git a/scripts/format.sh b/scripts/format.sh
new file mode 100755
index 00000000..d5f33e51
--- /dev/null
+++ b/scripts/format.sh
@@ -0,0 +1,34 @@
+#!/bin/bash
+
+die() { echo -e "$@" 1>&2 ; exit 1;  }
+
+AUTOFMT=true
+while getopts 'f:' OPTION; do
+  case "$OPTION" in
+    f)
+      AUTOFMT="$OPTARG"
+      ;;
+  esac
+done
+
+# Build a list of all the top-level directories in the project.
+for DIRECTORY in */ ; do
+  GOGLOB="$GOGLOB ${DIRECTORY%/}"
+done
+GOGLOB="${GOGLOB/ docs/}"
+GOGLOB="${GOGLOB/ vendor/}"
+
+# Check that there are no formatting issues
+GOFMT_LINES=`gofmt -s -l $GOGLOB | tr '\\\\' '/' | wc -l | xargs`
+if $AUTOFMT; then
+  # if there are files with formatting issues, they will be automatically corrected using the gofmt -w <file> command
+  if [[ $GOFMT_LINES -ne 0 ]]; then
+    FMT_FILES=`gofmt -s -l $GOGLOB | tr '\\\\' '/' | xargs`
+    for FILE in $FMT_FILES; do
+        echo "Running: gofmt -s -w $FILE"
+        `gofmt -s -w $FILE`
+    done
+  fi
+else
+  test $GOFMT_LINES -eq 0 || die "gofmt needs to be run, ${GOFMT_LINES} files have issues.  Below is a list of files to review:\n`gofmt -s -l $GOGLOB`"
+fi
\ No newline at end of file
diff --git a/static/bidder-info/brightroll.yaml b/static/bidder-info/brightroll.yaml
deleted file mode 100644
index 196344e9..00000000
--- a/static/bidder-info/brightroll.yaml
+++ /dev/null
@@ -1,17 +0,0 @@
-endpoint: "http://east-bid.ybp.yahoo.com/bid/appnexuspbs"
-maintainer:
-  email: "dsp-supply-prebid@verizonmedia.com"
-gvlVendorID: 25
-capabilities:
-  app:
-    mediaTypes:
-      - banner
-      - video
-  site:
-    mediaTypes:
-      - banner
-      - video
-userSync:
-  redirect:
-    url: "https://pr-bh.ybp.yahoo.com/sync/appnexusprebidserver/?gdpr={{.GDPR}}&euconsent={{.GDPRConsent}}&us_privacy={{.USPrivacy}}&url={{.RedirectURL}}"
-    userMacro: "$UID"
diff --git a/static/bidder-info/ownadx.yaml b/static/bidder-info/ownadx.yaml
new file mode 100644
index 00000000..37567db1
--- /dev/null
+++ b/static/bidder-info/ownadx.yaml
@@ -0,0 +1,12 @@
+endpoint: "https://pbs.prebid-ownadx.com/bidder/bid/{{.AccountID}}/{{.ZoneID}}?token={{.SourceId}}"
+maintainer:
+  email: prebid-team@techbravo.com
+capabilities:
+  app:
+    mediaTypes:
+      - banner
+      - video
+  site:
+    mediaTypes:
+      - banner
+      - video
diff --git a/static/bidder-info/rtbhouse.yaml b/static/bidder-info/rtbhouse.yaml
index 6a116448..ad2fbfcb 100644
--- a/static/bidder-info/rtbhouse.yaml
+++ b/static/bidder-info/rtbhouse.yaml
@@ -1,6 +1,7 @@
 endpoint: "http://prebidserver-s2s-ams.creativecdn.com/bidder/prebidserver/bids"
 maintainer:
   email: "prebid@rtbhouse.com"
+endpointCompression: gzip
 gvlVendorID: 16
 capabilities:
   site:
diff --git a/static/bidder-info/vox.yaml b/static/bidder-info/vox.yaml
new file mode 100644
index 00000000..e2b5c902
--- /dev/null
+++ b/static/bidder-info/vox.yaml
@@ -0,0 +1,19 @@
+endpoint: "https://ssp.hybrid.ai/prebid/server/v1/auction"
+maintainer:
+  email: prebid@hybrid.ai
+endpointCompression: gzip
+gvlVendorID: 206
+modifyingVastXmlAllowed: true
+capabilities:
+  site:
+    mediaTypes:
+      - banner
+      - video
+  app:
+    mediaTypes:
+      - banner
+      - video
+userSync:
+  redirect:
+    url: "https://ssp.hybrid.ai/prebid/server/v1/userSync?consent={{.GDPRConsent}}&redirect={{.RedirectURL}}"
+    userMacro: $UID
\ No newline at end of file
diff --git a/static/bidder-params/brightroll.json b/static/bidder-params/brightroll.json
deleted file mode 100644
index 48e48d8a..00000000
--- a/static/bidder-params/brightroll.json
+++ /dev/null
@@ -1,13 +0,0 @@
-{
-  "$schema": "http://json-schema.org/draft-04/schema#",
-  "title": "Brightroll Adapter Params",
-  "description": "A schema which validates params accepted by the Brightroll adapter",
-  "type": "object",
-  "properties": {
-      "publisher": {
-        "type": "string",
-        "description": "Publisher Name to use."
-    }
-  },
-  "required": ["publisher"]
-}
diff --git a/static/bidder-params/ownadx.json b/static/bidder-params/ownadx.json
new file mode 100644
index 00000000..f529e74c
--- /dev/null
+++ b/static/bidder-params/ownadx.json
@@ -0,0 +1,27 @@
+{
+  "$schema": "http://json-schema.org/draft-04/schema#",
+  "title": "OwnAdx Adapter Params",
+  "description": "A schema which validates params accepted by the OwnAdx adapter",
+  "type": "object",
+
+  "properties": {
+    "sspId": {
+      "type": "string",
+      "description": "Ssp ID"
+    },
+    "seatId": {
+      "type": "string",
+      "description": "Seat ID"
+    },
+    "tokenId": {
+      "type": "string",
+      "description": "Token ID"
+    }
+  },
+
+  "oneOf": [
+    { "required": ["sspId"] },
+    { "required": ["feedId"] },
+    { "required": ["token"] }
+  ]
+}
diff --git a/static/bidder-params/rtbhouse.json b/static/bidder-params/rtbhouse.json
index 00732bed..727f51bc 100644
--- a/static/bidder-params/rtbhouse.json
+++ b/static/bidder-params/rtbhouse.json
@@ -7,6 +7,18 @@
         "publisherId": {
             "type": "string",
             "description": "The publisher’s ID provided by RTB House"
+        },
+        "region": {
+            "type": "string",
+            "description": "The publisher’s assigned datacenter region"
+        },
+        "bidfloor": {
+            "type": "number",
+            "description": "(optional) Minimum bid price in CPM that will be considered"
+        },
+        "channel": {
+            "type": "string",
+            "description": "Inventory channel identifier, limited to 50 characters"
         }
     },
     "required": ["publisherId"]
diff --git a/static/bidder-params/vox.json b/static/bidder-params/vox.json
new file mode 100644
index 00000000..2ae65e99
--- /dev/null
+++ b/static/bidder-params/vox.json
@@ -0,0 +1,27 @@
+{
+  "$schema": "http://json-schema.org/draft-04/schema#",
+  "title": "Vox Adapter Params",
+  "description": "A schema which validates params accepted by the Vox adapter",
+  "type": "object",
+
+  "properties": {
+    "placementId": {
+      "type": "string",
+      "description": "Placement ID"
+    },
+    "imageUrl": {
+      "type": "string",
+      "description": "An URL of image on which banner should be put(InImage Ad format)."
+    },
+    "displaySizes": {
+      "type": "array",
+      "items": {
+        "type": "string",
+        "description": "<Width>x<Height>(Example: 123x90, 720x100)"
+      },
+      "description": "Display banner sizes which could be shown on top of image."
+    }
+  },
+
+  "required": ["placementId"]
+}
\ No newline at end of file
diff --git a/usersync/syncer.go b/usersync/syncer.go
index 2e0e4102..fb14201f 100644
--- a/usersync/syncer.go
+++ b/usersync/syncer.go
@@ -63,7 +63,7 @@ var ErrSyncerKeyRequired = errors.New("key is required")
 
 // NewSyncer creates a new Syncer from the provided configuration, or return an error if macro substition
 // fails or an endpoint url is invalid.
-func NewSyncer(hostConfig config.UserSync, syncerConfig config.Syncer) (Syncer, error) {
+func NewSyncer(hostConfig config.UserSync, syncerConfig config.Syncer, bidder string) (Syncer, error) {
 	if syncerConfig.Key == "" {
 		return nil, ErrSyncerKeyRequired
 	}
@@ -80,7 +80,7 @@ func NewSyncer(hostConfig config.UserSync, syncerConfig config.Syncer) (Syncer,
 
 	if syncerConfig.IFrame != nil {
 		var err error
-		syncer.iframe, err = buildTemplate(syncerConfig.Key, setuidSyncTypeIFrame, hostConfig, syncerConfig.ExternalURL, *syncerConfig.IFrame)
+		syncer.iframe, err = buildTemplate(bidder, setuidSyncTypeIFrame, hostConfig, syncerConfig.ExternalURL, *syncerConfig.IFrame)
 		if err != nil {
 			return nil, fmt.Errorf("iframe %v", err)
 		}
@@ -91,7 +91,7 @@ func NewSyncer(hostConfig config.UserSync, syncerConfig config.Syncer) (Syncer,
 
 	if syncerConfig.Redirect != nil {
 		var err error
-		syncer.redirect, err = buildTemplate(syncerConfig.Key, setuidSyncTypeRedirect, hostConfig, syncerConfig.ExternalURL, *syncerConfig.Redirect)
+		syncer.redirect, err = buildTemplate(bidder, setuidSyncTypeRedirect, hostConfig, syncerConfig.ExternalURL, *syncerConfig.Redirect)
 		if err != nil {
 			return nil, fmt.Errorf("redirect %v", err)
 		}
@@ -114,13 +114,14 @@ func resolveDefaultSyncType(syncerConfig config.Syncer) SyncType {
 var (
 	macroRegexExternalHost = regexp.MustCompile(`{{\s*\.ExternalURL\s*}}`)
 	macroRegexSyncerKey    = regexp.MustCompile(`{{\s*\.SyncerKey\s*}}`)
+	macroRegexBidderName   = regexp.MustCompile(`{{\s*\.BidderName\s*}}`)
 	macroRegexSyncType     = regexp.MustCompile(`{{\s*\.SyncType\s*}}`)
 	macroRegexUserMacro    = regexp.MustCompile(`{{\s*\.UserMacro\s*}}`)
 	macroRegexRedirect     = regexp.MustCompile(`{{\s*\.RedirectURL\s*}}`)
 	macroRegex             = regexp.MustCompile(`{{\s*\..*?\s*}}`)
 )
 
-func buildTemplate(key, syncTypeValue string, hostConfig config.UserSync, syncerExternalURL string, syncerEndpoint config.SyncerEndpoint) (*template.Template, error) {
+func buildTemplate(bidderName, syncTypeValue string, hostConfig config.UserSync, syncerExternalURL string, syncerEndpoint config.SyncerEndpoint) (*template.Template, error) {
 	redirectTemplate := syncerEndpoint.RedirectURL
 	if redirectTemplate == "" {
 		redirectTemplate = hostConfig.RedirectURL
@@ -128,7 +129,8 @@ func buildTemplate(key, syncTypeValue string, hostConfig config.UserSync, syncer
 
 	externalURL := chooseExternalURL(syncerEndpoint.ExternalURL, syncerExternalURL, hostConfig.ExternalURL)
 
-	redirectURL := macroRegexSyncerKey.ReplaceAllLiteralString(redirectTemplate, key)
+	redirectURL := macroRegexSyncerKey.ReplaceAllLiteralString(redirectTemplate, bidderName)
+	redirectURL = macroRegexBidderName.ReplaceAllLiteralString(redirectURL, bidderName)
 	redirectURL = macroRegexSyncType.ReplaceAllLiteralString(redirectURL, syncTypeValue)
 	redirectURL = macroRegexUserMacro.ReplaceAllLiteralString(redirectURL, syncerEndpoint.UserMacro)
 	redirectURL = macroRegexExternalHost.ReplaceAllLiteralString(redirectURL, externalURL)
@@ -136,7 +138,7 @@ func buildTemplate(key, syncTypeValue string, hostConfig config.UserSync, syncer
 
 	url := macroRegexRedirect.ReplaceAllString(syncerEndpoint.URL, redirectURL)
 
-	templateName := strings.ToLower(key) + "_usersync_url"
+	templateName := strings.ToLower(bidderName) + "_usersync_url"
 	return template.New(templateName).Parse(url)
 }
 
diff --git a/usersync/syncersbuilder.go b/usersync/syncersbuilder.go
index c521d36d..9a52a740 100644
--- a/usersync/syncersbuilder.go
+++ b/usersync/syncersbuilder.go
@@ -59,17 +59,16 @@ func BuildSyncers(hostConfig *config.Configuration, bidderInfos config.BidderInf
 			continue
 		}
 
-		syncer, err := NewSyncer(hostUserSyncConfig, primaryCfg.cfg)
-		if err != nil {
-			errs = append(errs, SyncerBuildError{
-				Bidder:    primaryCfg.name,
-				SyncerKey: key,
-				Err:       err,
-			})
-			continue
-		}
-
 		for _, bidder := range cfgGroup {
+			syncer, err := NewSyncer(hostUserSyncConfig, primaryCfg.cfg, bidder.name)
+			if err != nil {
+				errs = append(errs, SyncerBuildError{
+					Bidder:    primaryCfg.name,
+					SyncerKey: key,
+					Err:       err,
+				})
+				continue
+			}
 			syncers[bidder.name] = syncer
 		}
 	}
diff --git a/validate.sh b/validate.sh
index b81ade34..becd42ce 100755
--- a/validate.sh
+++ b/validate.sh
@@ -17,29 +17,7 @@ while true; do
   esac
 done
 
-die() { echo -e "$@" 1>&2 ; exit 1;  }
-
-# Build a list of all the top-level directories in the project.
-for DIRECTORY in */ ; do
-  GOGLOB="$GOGLOB ${DIRECTORY%/}"
-done
-GOGLOB="${GOGLOB/ docs/}"
-GOGLOB="${GOGLOB/ vendor/}"
-
-# Check that there are no formatting issues
-GOFMT_LINES=`gofmt -s -l $GOGLOB | tr '\\\\' '/' | wc -l | xargs`
-if $AUTOFMT; then
-  # if there are files with formatting issues, they will be automatically corrected using the gofmt -w <file> command
-  if [[ $GOFMT_LINES -ne 0 ]]; then
-    FMT_FILES=`gofmt -s -l $GOGLOB | tr '\\\\' '/' | xargs`
-    for FILE in $FMT_FILES; do
-        echo "Running: gofmt -s -w $FILE"
-        `gofmt -s -w $FILE`
-    done
-  fi
-else
-  test $GOFMT_LINES -eq 0 || die "gofmt needs to be run, ${GOFMT_LINES} files have issues.  Below is a list of files to review:\n`gofmt -s -l $GOGLOB`"
-fi
+./scripts/format.sh -f $AUTOFMT
 
 # Run the actual tests. Make sure there's enough coverage too, if the flags call for it.
 if $COVERAGE; then


Test patch:
diff --git a/adapters/brightroll/brightroll_test.go b/adapters/brightroll/brightroll_test.go
deleted file mode 100644
index 4cf0f46f..00000000
--- a/adapters/brightroll/brightroll_test.go
+++ /dev/null
@@ -1,73 +0,0 @@
-package brightroll
-
-import (
-	"testing"
-
-	"github.com/stretchr/testify/assert"
-
-	"github.com/prebid/prebid-server/adapters/adapterstest"
-	"github.com/prebid/prebid-server/config"
-	"github.com/prebid/prebid-server/openrtb_ext"
-)
-
-func TestEmptyConfig(t *testing.T) {
-	bidder, buildErr := Builder(openrtb_ext.BidderBrightroll, config.Adapter{
-		Endpoint:         `http://test-bid.ybp.yahoo.com/bid/appnexuspbs`,
-		ExtraAdapterInfo: ``,
-	}, config.Server{ExternalUrl: "http://hosturl.com", GvlID: 1, DataCenter: "2"})
-
-	if buildErr != nil {
-		t.Fatalf("Builder returned unexpected error %v", buildErr)
-	}
-
-	ex := ExtraInfo{
-		Accounts: []Account{},
-	}
-	expected := &BrightrollAdapter{
-		URI:       "http://test-bid.ybp.yahoo.com/bid/appnexuspbs",
-		extraInfo: ex,
-	}
-	assert.Equal(t, expected, bidder)
-}
-
-func TestNonEmptyConfig(t *testing.T) {
-	bidder, buildErr := Builder(openrtb_ext.BidderBrightroll, config.Adapter{
-		Endpoint:         `http://test-bid.ybp.yahoo.com/bid/appnexuspbs`,
-		ExtraAdapterInfo: `{"accounts": [{"id": "test","bidfloor":0.1}]}`,
-	}, config.Server{ExternalUrl: "http://hosturl.com", GvlID: 1, DataCenter: "2"})
-
-	if buildErr != nil {
-		t.Fatalf("Builder returned unexpected error %v", buildErr)
-	}
-
-	ex := ExtraInfo{
-		Accounts: []Account{{ID: "test", BidFloor: 0.1}},
-	}
-	expected := &BrightrollAdapter{
-		URI:       "http://test-bid.ybp.yahoo.com/bid/appnexuspbs",
-		extraInfo: ex,
-	}
-	assert.Equal(t, expected, bidder)
-}
-
-func TestMalformedEmpty(t *testing.T) {
-	_, buildErr := Builder(openrtb_ext.BidderBrightroll, config.Adapter{
-		Endpoint:         `http://test-bid.ybp.yahoo.com/bid/appnexuspbs`,
-		ExtraAdapterInfo: `malformed`,
-	}, config.Server{ExternalUrl: "http://hosturl.com", GvlID: 1, DataCenter: "2"})
-
-	assert.Error(t, buildErr)
-}
-
-func TestJsonSamples(t *testing.T) {
-	bidder, buildErr := Builder(openrtb_ext.BidderBrightroll, config.Adapter{
-		Endpoint:         `http://test-bid.ybp.yahoo.com/bid/appnexuspbs`,
-		ExtraAdapterInfo: `{"accounts": [{"id": "adthrive","badv": [], "bcat": ["IAB8-5","IAB8-18"],"battr": [1,2,3], "bidfloor":0.0}]}`,
-	}, config.Server{ExternalUrl: "http://hosturl.com", GvlID: 1, DataCenter: "2"})
-
-	if buildErr != nil {
-		t.Fatalf("Builder returned unexpected error %v", buildErr)
-	}
-
-	adapterstest.RunJSONBidderTest(t, "brightrolltest", bidder)
-}
diff --git a/adapters/brightroll/brightrolltest/exemplary/banner-native-audio.json b/adapters/brightroll/brightrolltest/exemplary/banner-native-audio.json
deleted file mode 100644
index f67fa259..00000000
--- a/adapters/brightroll/brightrolltest/exemplary/banner-native-audio.json
+++ /dev/null
@@ -1,169 +0,0 @@
-{
-  "mockBidRequest": {
-    "id": "test-request-id",
-    "imp": [
-      {
-        "id": "test-imp-id",
-        "banner": {
-          "format": [
-            {
-              "w": 300,
-              "h": 250
-            },
-            {
-              "w": 300,
-              "h": 600
-            }
-          ]
-        },
-        "ext": {
-          "bidder": {
-            "publisher": "adthrive"
-          }
-        }
-      },
-      {
-        "id": "unsupported-native-imp",
-        "native": {
-          "ver": "1.1",
-          "request": "{\"ver\":\"1.1\",\"context\":1,\"contextsubtype\":11,\"assets\":[{\"id\":1,\"required\":1,\"title\":{\"len\":500}},{\"id\":2,\"required\":1,\"img\":{\"type\":3,\"wmin\":1,\"hmin\":1}},{\"id\":3,\"required\":0,\"data\":{\"type\":1,\"len\":200}},{\"id\":4,\"required\":0,\"data\":{\"type\":2,\"len\":15000}},{\"id\":5,\"required\":0,\"data\":{\"type\":6,\"len\":40}},{\"id\":6,\"required\":0,\"data\":{\"type\":500}}]}"
-        },
-        "ext": {
-          "bidder": {
-            "publisher": "adthrive"
-          }
-        }
-      },
-      {
-        "id": "unsupported-audio-imp",
-        "audio": {
-          "mimes": [
-            "video/mp4"
-          ]
-        },
-        "ext": {
-          "bidder": {
-            "publisher": "adthrive"
-          }
-        }
-      }
-    ]
-  },
-  "httpCalls": [
-    {
-      "expectedRequest": {
-        "uri": "http://test-bid.ybp.yahoo.com/bid/appnexuspbs?publisher=adthrive",
-        "body": {
-          "id": "test-request-id",
-          "at":1,
-          "bcat": [
-            "IAB8-5",
-            "IAB8-18"
-          ],
-          "imp": [
-            {
-              "id": "test-imp-id",
-              "banner": {
-                "battr": [
-                  1,
-                  2,
-                  3
-                ],
-                "format": [
-                  {
-                    "w": 300,
-                    "h": 250
-                  },
-                  {
-                    "w": 300,
-                    "h": 600
-                  }
-                ],
-                "w": 300,
-                "h": 250
-              },
-              "ext": {
-                "bidder": {
-                  "publisher": "adthrive"
-                }
-              }
-            },
-            {
-              "id": "unsupported-native-imp",
-              "native": {
-                "ver": "1.1",
-                "request": "{\"ver\":\"1.1\",\"context\":1,\"contextsubtype\":11,\"assets\":[{\"id\":1,\"required\":1,\"title\":{\"len\":500}},{\"id\":2,\"required\":1,\"img\":{\"type\":3,\"wmin\":1,\"hmin\":1}},{\"id\":3,\"required\":0,\"data\":{\"type\":1,\"len\":200}},{\"id\":4,\"required\":0,\"data\":{\"type\":2,\"len\":15000}},{\"id\":5,\"required\":0,\"data\":{\"type\":6,\"len\":40}},{\"id\":6,\"required\":0,\"data\":{\"type\":500}}]}"
-              },
-              "ext": {
-                "bidder": {
-                  "publisher": "adthrive"
-                }
-              }
-            },
-            {
-              "id": "unsupported-audio-imp",
-              "audio": {
-                "mimes": [
-                  "video/mp4"
-                ]
-              },
-              "ext": {
-                "bidder": {
-                  "publisher": "adthrive"
-                }
-              }
-            }
-          ]
-        }
-      },
-      "mockResponse": {
-        "status": 200,
-        "body": {
-          "id": "test-request-id",
-          "seatbid": [
-            {
-              "seat": "958",
-              "bid": [{
-                "id": "7706636740145184841",
-                "impid": "test-imp-id",
-                "price": 0.500000,
-                "adid": "29681110",
-                "adm": "some-test-ad",
-                "adomain": ["yahoo.com"],
-                "cid": "958",
-                "crid": "29681110",
-                "h": 250,
-                "w": 300
-              }]
-            }
-          ],
-          "bidid": "5778926625248726496",
-          "cur": "USD"
-        }
-      }
-    }
-  ],
-
-  "expectedBidResponses": [
-    {
-      "bids": [
-        {
-          "bid": {
-            "id": "7706636740145184841",
-            "impid": "test-imp-id",
-            "price": 0.5,
-            "adm": "some-test-ad",
-            "adid": "29681110",
-            "adomain": ["yahoo.com"],
-            "cid": "958",
-            "crid": "29681110",
-            "w": 300,
-            "h": 250
-          },
-          "type": "banner"
-        }
-      ]
-    }
-  ]
-
-}
diff --git a/adapters/brightroll/brightrolltest/exemplary/banner-video-native.json b/adapters/brightroll/brightrolltest/exemplary/banner-video-native.json
deleted file mode 100644
index 97081b70..00000000
--- a/adapters/brightroll/brightrolltest/exemplary/banner-video-native.json
+++ /dev/null
@@ -1,176 +0,0 @@
-{
-  "mockBidRequest": {
-    "id": "test-request-id",
-    "imp": [
-      {
-        "id": "test-imp-id",
-        "banner": {
-          "format": [
-            {
-              "w": 300,
-              "h": 250
-            },
-            {
-              "w": 300,
-              "h": 600
-            }
-          ]
-        },
-        "ext": {
-          "bidder": {
-            "publisher": "adthrive"
-          }
-        }
-      },
-      {
-        "id": "unsupported-native-imp",
-        "native": {
-          "ver": "1.1",
-          "request": "{\"ver\":\"1.1\",\"context\":1,\"contextsubtype\":11,\"assets\":[{\"id\":1,\"required\":1,\"title\":{\"len\":500}},{\"id\":2,\"required\":1,\"img\":{\"type\":3,\"wmin\":1,\"hmin\":1}},{\"id\":3,\"required\":0,\"data\":{\"type\":1,\"len\":200}},{\"id\":4,\"required\":0,\"data\":{\"type\":2,\"len\":15000}},{\"id\":5,\"required\":0,\"data\":{\"type\":6,\"len\":40}},{\"id\":6,\"required\":0,\"data\":{\"type\":500}}]}"
-        },
-        "ext": {
-          "bidder": {
-            "publisher": "adthrive"
-          }
-        }
-      },
-      {
-        "id": "test-imp-video-id",
-        "video": {
-          "mimes": ["video/mp4"],
-          "protocols": [2, 5],
-          "w": 1024,
-          "h": 576
-        },
-        "ext": {
-          "bidder": {
-            "publisher": "adthrive"
-          }
-        }
-      }
-    ]
-  },
-  "httpCalls": [
-    {
-      "expectedRequest": {
-        "uri": "http://test-bid.ybp.yahoo.com/bid/appnexuspbs?publisher=adthrive",
-        "body": {
-          "id": "test-request-id",
-          "at":1,
-          "bcat": [
-            "IAB8-5",
-            "IAB8-18"
-          ],
-          "imp": [
-            {
-              "id": "test-imp-id",
-              "banner": {
-                "battr": [
-                  1,
-                  2,
-                  3
-                ],
-                "format": [
-                  {
-                    "w": 300,
-                    "h": 250
-                  },
-                  {
-                    "w": 300,
-                    "h": 600
-                  }
-                ],
-                "w": 300,
-                "h": 250
-              },
-              "ext": {
-                "bidder": {
-                  "publisher": "adthrive"
-                }
-              }
-            },
-            {
-              "id": "unsupported-native-imp",
-              "native": {
-                "ver": "1.1",
-                "request": "{\"ver\":\"1.1\",\"context\":1,\"contextsubtype\":11,\"assets\":[{\"id\":1,\"required\":1,\"title\":{\"len\":500}},{\"id\":2,\"required\":1,\"img\":{\"type\":3,\"wmin\":1,\"hmin\":1}},{\"id\":3,\"required\":0,\"data\":{\"type\":1,\"len\":200}},{\"id\":4,\"required\":0,\"data\":{\"type\":2,\"len\":15000}},{\"id\":5,\"required\":0,\"data\":{\"type\":6,\"len\":40}},{\"id\":6,\"required\":0,\"data\":{\"type\":500}}]}"
-              },
-              "ext": {
-                "bidder": {
-                  "publisher": "adthrive"
-                }
-              }
-            },
-            {
-              "id": "test-imp-video-id",
-              "video": {
-                "battr": [
-                  1,
-                  2,
-                  3
-                ],
-                "mimes": ["video/mp4"],
-                "protocols": [2, 5],
-                "w": 1024,
-                "h": 576
-              },
-              "ext": {
-                "bidder": {
-                  "publisher": "adthrive"
-                }
-              }
-            }
-          ]
-        }
-      },
-      "mockResponse": {
-        "status": 200,
-        "body": {
-          "id": "test-request-id",
-          "seatbid": [
-            {
-              "seat": "958",
-              "bid": [{
-                "id": "7706636740145184841",
-                "impid": "test-imp-video-id",
-                "price": 0.500000,
-                "adid": "29681110",
-                "adm": "some-test-ad",
-                "adomain": ["yahoo.com"],
-                "cid": "958",
-                "crid": "29681110",
-                "h": 576,
-                "w": 1024
-              }]
-            }
-          ],
-          "bidid": "5778926625248726496",
-          "cur": "USD"
-        }
-      }
-    }
-  ],
-
-  "expectedBidResponses": [
-    {
-      "bids": [
-        {
-          "bid": {
-            "id": "7706636740145184841",
-            "impid": "test-imp-video-id",
-            "price": 0.5,
-            "adm": "some-test-ad",
-            "adid": "29681110",
-            "adomain": ["yahoo.com"],
-            "cid": "958",
-            "crid": "29681110",
-            "w": 1024,
-            "h": 576
-          },
-          "type": "video"
-        }
-      ]
-    }
-  ]
-
-}
diff --git a/adapters/brightroll/brightrolltest/exemplary/banner-video.json b/adapters/brightroll/brightrolltest/exemplary/banner-video.json
deleted file mode 100644
index 17441152..00000000
--- a/adapters/brightroll/brightrolltest/exemplary/banner-video.json
+++ /dev/null
@@ -1,152 +0,0 @@
-{
-  "mockBidRequest": {
-    "id": "test-request-id",
-    "imp": [
-      {
-        "id": "test-imp-id",
-        "banner": {
-          "format": [
-            {
-              "w": 300,
-              "h": 250
-            },
-            {
-              "w": 300,
-              "h": 600
-            }
-          ]
-        },
-        "ext": {
-          "bidder": {
-            "publisher": "adthrive"
-          }
-        }
-      },
-      {
-        "id": "test-imp-video-id",
-        "video": {
-          "mimes": ["video/mp4"],
-          "protocols": [2, 5],
-          "w": 1024,
-          "h": 576
-        },
-        "ext": {
-          "bidder": {
-            "publisher": "adthrive"
-          }
-        }
-      }
-    ]
-  },
-  "httpCalls": [
-    {
-      "expectedRequest": {
-        "uri": "http://test-bid.ybp.yahoo.com/bid/appnexuspbs?publisher=adthrive",
-        "body": {
-          "id": "test-request-id",
-          "at":1,
-          "bcat": [
-            "IAB8-5",
-            "IAB8-18"
-          ],
-          "imp": [
-            {
-              "id": "test-imp-id",
-              "banner": {
-                "battr": [
-                  1,
-                  2,
-                  3
-                ],
-                "format": [
-                  {
-                    "w": 300,
-                    "h": 250
-                  },
-                  {
-                    "w": 300,
-                    "h": 600
-                  }
-                ],
-                "w": 300,
-                "h": 250
-              },
-              "ext": {
-                "bidder": {
-                  "publisher": "adthrive"
-                }
-              }
-            },
-            {
-              "id": "test-imp-video-id",
-              "video": {
-                "battr": [
-                  1,
-                  2,
-                  3
-                ],
-                "mimes": ["video/mp4"],
-                "protocols": [2, 5],
-                "w": 1024,
-                "h": 576
-              },
-              "ext": {
-                "bidder": {
-                  "publisher": "adthrive"
-                }
-              }
-            }
-          ]
-        }
-      },
-      "mockResponse": {
-        "status": 200,
-        "body": {
-          "id": "test-request-id",
-          "seatbid": [
-            {
-              "seat": "958",
-              "bid": [{
-                "id": "7706636740145184841",
-                "impid": "test-imp-video-id",
-                "price": 0.500000,
-                "adid": "29681110",
-                "adm": "some-test-ad",
-                "adomain": ["yahoo.com"],
-                "cid": "958",
-                "crid": "29681110",
-                "h": 576,
-                "w": 1024
-              }]
-            }
-          ],
-          "bidid": "5778926625248726496",
-          "cur": "USD"
-        }
-      }
-    }
-  ],
-
-  "expectedBidResponses": [
-    {
-      "bids": [
-        {
-          "bid": {
-            "id": "7706636740145184841",
-            "impid": "test-imp-video-id",
-            "price": 0.5,
-            "adm": "some-test-ad",
-            "adid": "29681110",
-            "adomain": ["yahoo.com"],
-            "cid": "958",
-            "crid": "29681110",
-            "w": 1024,
-            "h": 576
-          },
-          "type": "video"
-        }
-      ]
-    }
-  ]
-
-}
diff --git a/adapters/brightroll/brightrolltest/exemplary/simple-banner.json b/adapters/brightroll/brightrolltest/exemplary/simple-banner.json
deleted file mode 100644
index f5903850..00000000
--- a/adapters/brightroll/brightrolltest/exemplary/simple-banner.json
+++ /dev/null
@@ -1,119 +0,0 @@
-{
-  "mockBidRequest": {
-    "id": "test-request-id",
-    "imp": [
-      {
-        "id": "test-imp-id",
-        "banner": {
-          "format": [
-            {
-              "w": 300,
-              "h": 250
-            },
-            {
-              "w": 300,
-              "h": 600
-            }
-          ]
-        },
-        "ext": {
-          "bidder": {
-            "publisher": "adthrive"
-          }
-        }
-      }
-    ]
-  },
-
-  "httpCalls": [
-    {
-      "expectedRequest": {
-        "uri": "http://test-bid.ybp.yahoo.com/bid/appnexuspbs?publisher=adthrive",
-        "body": {
-          "id": "test-request-id",
-          "at":1,
-          "bcat": [
-            "IAB8-5",
-            "IAB8-18"
-          ],
-          "imp": [
-            {
-              "id": "test-imp-id",
-              "banner": {
-                "battr": [
-                  1,
-                  2,
-                  3
-                ],
-                "format": [
-                  {
-                    "w": 300,
-                    "h": 250
-                  },
-                  {
-                    "w": 300,
-                    "h": 600
-                  }
-                ],
-                "w": 300,
-                "h": 250
-              },
-              "ext": {
-                "bidder": {
-                  "publisher": "adthrive"
-                }
-              }
-            }
-          ]
-        }
-      },
-      "mockResponse": {
-        "status": 200,
-        "body": {
-          "id": "test-request-id",
-          "seatbid": [
-            {
-              "seat": "958",
-              "bid": [{
-                "id": "7706636740145184841",
-                "impid": "test-imp-id",
-                "price": 0.500000,
-                "adid": "29681110",
-                "adm": "some-test-ad",
-                "adomain": ["yahoo.com"],
-                "cid": "958",
-                "crid": "29681110",
-                "h": 250,
-                "w": 300
-              }]
-            }
-          ],
-          "bidid": "5778926625248726496",
-          "cur": "USD"
-        }
-      }
-    }
-  ],
-
-  "expectedBidResponses": [
-    {
-      "bids": [
-        {
-          "bid": {
-            "id": "7706636740145184841",
-            "impid": "test-imp-id",
-            "price": 0.5,
-            "adm": "some-test-ad",
-            "adid": "29681110",
-            "adomain": ["yahoo.com"],
-            "cid": "958",
-            "crid": "29681110",
-            "w": 300,
-            "h": 250
-          },
-          "type": "banner"
-        }
-      ]
-    }
-  ]
-}
diff --git a/adapters/brightroll/brightrolltest/exemplary/simple-video.json b/adapters/brightroll/brightrolltest/exemplary/simple-video.json
deleted file mode 100644
index 88539276..00000000
--- a/adapters/brightroll/brightrolltest/exemplary/simple-video.json
+++ /dev/null
@@ -1,98 +0,0 @@
-{
-  "mockBidRequest": {
-    "id": "test-request-id",
-    "imp": [
-      {
-        "id": "test-imp-id",
-        "video": {
-          "mimes": ["video/mp4"],
-          "protocols": [2, 5],
-          "w": 1024,
-          "h": 576
-        },
-        "ext":{
-          "bidder":{
-            "publisher": "adthrive"
-          }
-        }
-      }
-    ]
-  },
-
-  "httpCalls": [
-    {
-      "expectedRequest": {
-        "uri": "http://test-bid.ybp.yahoo.com/bid/appnexuspbs?publisher=adthrive",
-        "body": {
-          "id": "test-request-id",
-          "at":1,
-          "bcat": [
-            "IAB8-5",
-            "IAB8-18"
-          ],
-          "imp": [
-            {
-              "id": "test-imp-id",
-              "video": {
-                "battr": [
-                  1,
-                  2,
-                  3
-                ],
-                "mimes": ["video/mp4"],
-                "protocols": [2, 5],
-                "w": 1024,
-                "h": 576
-              },
-              "ext": {
-                "bidder": {
-                  "publisher": "adthrive"
-                }
-              }
-            }
-          ]
-        }
-      },
-      "mockResponse": {
-        "status": 200,
-        "body": {
-          "id": "test-request-id",
-          "cur": "USD",
-          "seatbid": [
-            {
-              "seat": "Brightroll",
-              "bid": [{
-                "id": "8ee514f1-b2b8-4abb-89fd-084437d1e800",
-                "impid": "test-imp-id",
-                "price": 0.500000,
-                "adm": "some-test-ad",
-                "crid": "crid_10",
-                "w": 1024,
-                "h": 576
-              }]
-            }
-          ]
-        }
-      }
-    }
-  ],
-
-  "expectedBidResponses": [
-    {
-      "bids": [
-        {
-          "bid": {
-            "id": "8ee514f1-b2b8-4abb-89fd-084437d1e800",
-            "impid": "test-imp-id",
-            "price": 0.5,
-            "adm": "some-test-ad",
-            "crid": "crid_10",
-            "w": 1024,
-            "h": 576
-          },
-          "type": "video"
-        }
-      ]
-    }
-  ]
-}
diff --git a/adapters/brightroll/brightrolltest/exemplary/valid-extension.json b/adapters/brightroll/brightrolltest/exemplary/valid-extension.json
deleted file mode 100644
index 9a5e571c..00000000
--- a/adapters/brightroll/brightrolltest/exemplary/valid-extension.json
+++ /dev/null
@@ -1,96 +0,0 @@
-{
-  "mockBidRequest": {
-    "id": "test-request-id",
-    "imp": [
-      {
-        "id": "test-imp-id",
-        "video": {
-          "mimes": ["video/mp4"],
-          "protocols": [2, 5],
-          "w": 1024,
-          "h": 576
-        },
-        "ext":{
-          "bidder":{
-            "publisher": "adthrive"
-           }
-        }
-      }
-    ]
-  },
-
-  "httpCalls": [
-    {
-      "expectedRequest": {
-        "uri": "http://test-bid.ybp.yahoo.com/bid/appnexuspbs?publisher=adthrive",
-        "body": {
-          "id": "test-request-id",
-          "at":1,
-          "bcat": [
-            "IAB8-5",
-            "IAB8-18"
-          ],
-          "imp": [
-            {
-              "id": "test-imp-id",
-              "video": {
-                "battr": [
-                  1,
-                  2,
-                  3
-                ],
-                "mimes": ["video/mp4"],
-                "protocols": [2, 5],
-                "w": 1024,
-                "h": 576
-              },
-              "ext": {
-                "bidder": {
-                  "publisher": "adthrive"
-                }
-              }
-            }
-          ]
-        }
-      },
-      "mockResponse": {
-        "status": 200,
-        "body": {
-          "id": "test-request-id",
-          "cur": "USD",
-          "seatbid": [
-            {
-              "seat": "brightroll",
-              "bid": [{
-                "id": "8ee514f1-b2b8-4abb-89fd-084437d1e800",
-                "impid": "test-imp-id",
-                "price": 0.500000,
-                "adm": "some-test-ad",
-                "crid": "crid_10",
-                "w": 1024,
-                "h": 576
-              }]
-            }
-          ]
-        }
-      }
-    }
-  ],
-
-  "expectedBidResponses": [
-    {
-      "bids": [{
-        "bid": {
-          "id": "8ee514f1-b2b8-4abb-89fd-084437d1e800",
-          "impid": "test-imp-id",
-          "price": 0.5,
-          "adm": "some-test-ad",
-          "crid": "crid_10",
-          "w": 1024,
-          "h": 576
-        },
-        "type": "video"
-      }]
-    }
-  ]
-}
diff --git a/adapters/brightroll/brightrolltest/exemplary/video-and-audio.json b/adapters/brightroll/brightrolltest/exemplary/video-and-audio.json
deleted file mode 100644
index 4184842b..00000000
--- a/adapters/brightroll/brightrolltest/exemplary/video-and-audio.json
+++ /dev/null
@@ -1,121 +0,0 @@
-{
-  "mockBidRequest": {
-    "id": "test-request-id",
-    "imp": [
-      {
-        "id": "test-imp-video-id",
-        "video": {
-          "mimes": ["video/mp4"],
-          "protocols": [2, 5],
-          "w": 1024,
-          "h": 576
-        },
-        "ext": {
-          "bidder": {
-            "publisher": "adthrive"
-          }
-        }
-      },
-      {
-        "id": "unsupported-audio-imp",
-        "audio": {
-          "mimes": [
-            "video/mp4"
-          ]
-        },
-        "ext": {
-          "bidder": {
-            "publisher": "adthrive"
-          }
-        }
-      }
-    ]
-  },
-  "httpCalls": [
-    {
-      "expectedRequest": {
-        "uri": "http://test-bid.ybp.yahoo.com/bid/appnexuspbs?publisher=adthrive",
-        "body": {
-          "id": "test-request-id",
-          "at":1,
-          "bcat": [
-            "IAB8-5",
-            "IAB8-18"
-          ],
-          "imp": [
-            {
-              "id": "test-imp-video-id",
-              "video": {
-                "battr": [
-                  1,
-                  2,
-                  3
-                ],
-                "mimes": ["video/mp4"],
-                "protocols": [2, 5],
-                "w": 1024,
-                "h": 576
-              },
-              "ext": {
-                "bidder": {
-                  "publisher": "adthrive"
-                }
-              }
-            },
-            {
-              "id": "unsupported-audio-imp",
-              "audio": {
-                "mimes": [
-                  "video/mp4"
-                ]
-              },
-              "ext": {
-                "bidder": {
-                  "publisher": "adthrive"
-                }
-              }
-            }
-          ]
-        }
-      },
-      "mockResponse": {
-        "status": 200,
-        "body": {
-          "id": "test-request-id",
-          "cur": "USD",
-          "seatbid": [
-            {
-              "seat": "brightroll",
-              "bid": [{
-                "id": "8ee514f1-b2b8-4abb-89fd-084437d1e800",
-                "impid": "test-imp-video-id",
-                "price": 0.500000,
-                "adm": "some-test-ad",
-                "crid": "crid_10",
-                "w": 1024,
-                "h": 576
-              }]
-            }
-          ]
-        }
-      }
-    }
-  ],
-
-  "expectedBidResponses": [
-    {
-      "bids": [{
-        "bid": {
-          "id": "8ee514f1-b2b8-4abb-89fd-084437d1e800",
-          "impid": "test-imp-video-id",
-          "price": 0.5,
-          "adm": "some-test-ad",
-          "crid": "crid_10",
-          "w": 1024,
-          "h": 576
-        },
-        "type": "video"
-      }]
-    }
-  ]
-}
diff --git a/adapters/brightroll/brightrolltest/supplemental/invalid-extension.json b/adapters/brightroll/brightrolltest/supplemental/invalid-extension.json
deleted file mode 100644
index 35b15638..00000000
--- a/adapters/brightroll/brightrolltest/supplemental/invalid-extension.json
+++ /dev/null
@@ -1,31 +0,0 @@
-{
-  "mockBidRequest": {
-    "id": "test-request-id",
-    "imp": [
-      {
-        "id": "test-invalid-ext-id",
-        "banner": {
-          "format": [
-            {
-              "w": 300,
-              "h": 250
-            },
-            {
-              "w": 300,
-              "h": 600
-            }
-          ]
-        },
-        "ext": {
-        }
-
-      }
-    ]
-  },
-  "expectedMakeRequestsErrors": [
-    {
-      "value": "ext.bidder.publisher not provided",
-      "comparison": "literal"
-    }
-  ]
-}
diff --git a/adapters/brightroll/brightrolltest/supplemental/invalid-imp.json b/adapters/brightroll/brightrolltest/supplemental/invalid-imp.json
deleted file mode 100644
index 01beec71..00000000
--- a/adapters/brightroll/brightrolltest/supplemental/invalid-imp.json
+++ /dev/null
@@ -1,16 +0,0 @@
-{
-  "mockBidRequest": {
-    "id": "test-request-id",
-    "ext": {
-      "bidder": {
-        "publisher": "adthrive"
-      }
-    }
-  },
-  "expectedMakeRequestsErrors": [
-    {
-      "value": "No impression in the bid request",
-      "comparison": "literal"
-    }
-  ]
-}
diff --git a/adapters/brightroll/brightrolltest/supplemental/invalid-publisher.json b/adapters/brightroll/brightrolltest/supplemental/invalid-publisher.json
deleted file mode 100644
index da48108a..00000000
--- a/adapters/brightroll/brightrolltest/supplemental/invalid-publisher.json
+++ /dev/null
@@ -1,34 +0,0 @@
-{
-  "mockBidRequest": {
-    "id": "test-request-id",
-    "imp": [
-      {
-        "id": "test-missing-req-param-id",
-        "banner": {
-          "format": [
-            {
-              "w": 300,
-              "h": 250
-            },
-            {
-              "w": 300,
-              "h": 600
-            }
-          ]
-        },
-        "ext": {
-          "bidder": {
-            "publisher":"test"
-          }
-        }
-
-      }
-    ]
-  },
-  "expectedMakeRequestsErrors": [
-    {
-      "value": "Invalid publisher",
-      "comparison": "literal"
-    }
-  ]
-}
\ No newline at end of file
diff --git a/adapters/brightroll/brightrolltest/supplemental/missing-extension.json b/adapters/brightroll/brightrolltest/supplemental/missing-extension.json
deleted file mode 100644
index 82ec775d..00000000
--- a/adapters/brightroll/brightrolltest/supplemental/missing-extension.json
+++ /dev/null
@@ -1,22 +0,0 @@
-{
-  "mockBidRequest": {
-    "id": "test-request-id",
-    "imp": [
-      {
-        "id": "test-missing-ext-id",
-        "video": {
-          "mimes": ["video/mp4"],
-          "protocols": [2, 5],
-          "w": 1024,
-          "h": 576
-        }
-      }
-    ]
-  },
-  "expectedMakeRequestsErrors": [
-    {
-      "value": "ext.bidder not provided",
-      "comparison": "literal"
-    }
-  ]
-}
\ No newline at end of file
diff --git a/adapters/brightroll/brightrolltest/supplemental/missing-param.json b/adapters/brightroll/brightrolltest/supplemental/missing-param.json
deleted file mode 100644
index 08e82c0e..00000000
--- a/adapters/brightroll/brightrolltest/supplemental/missing-param.json
+++ /dev/null
@@ -1,34 +0,0 @@
-{
-  "mockBidRequest": {
-    "id": "test-request-id",
-    "imp": [
-      {
-        "id": "test-missing-req-param-id",
-        "banner": {
-          "format": [
-            {
-              "w": 300,
-              "h": 250
-            },
-            {
-              "w": 300,
-              "h": 600
-            }
-          ]
-        },
-        "ext": {
-          "bidder": {
-            "publisher":""
-          }
-        }
-
-      }
-    ]
-  },
-  "expectedMakeRequestsErrors": [
-    {
-      "value": "publisher is empty",
-      "comparison": "literal"
-    }
-  ]
-}
diff --git a/adapters/brightroll/params_test.go b/adapters/brightroll/params_test.go
deleted file mode 100644
index c14ee6d7..00000000
--- a/adapters/brightroll/params_test.go
+++ /dev/null
@@ -1,62 +0,0 @@
-package brightroll
-
-import (
-	"encoding/json"
-	"testing"
-
-	"github.com/prebid/prebid-server/openrtb_ext"
-)
-
-// This file actually intends to test static/bidder-params/brightroll.json
-//
-// These also validate the format of the external API: request.imp[i].ext.prebid.bidder.brightroll
-
-// TestValidParams makes sure that the Brightroll 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.BidderBrightroll, json.RawMessage(validParam)); err != nil {
-			t.Errorf("Schema rejected Brightroll params: %s", validParam)
-		}
-	}
-}
-
-// TestInvalidParams makes sure that the Brightroll 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.BidderBrightroll, json.RawMessage(invalidParam)); err == nil {
-			t.Errorf("Schema allowed unexpected params: %s", invalidParam)
-		}
-	}
-}
-
-var validParams = []string{
-	`{"publisher": "testpublisher"}`,
-	`{"publisher": "123"}`,
-	`{"publisher": "cafemedia"}`,
-	`{"publisher": "test", "headerbidding": false}`,
-}
-
-var invalidParams = []string{
-	`{"publisher": 100}`,
-	`{"headerbidding": false}`,
-	`{"publisher": true}`,
-	`{"publisherId": 123, "headerbidding": true}`,
-	`{"publisherID": "1"}`,
-	``,
-	`null`,
-	`true`,
-	`9`,
-	`1.2`,
-	`[]`,
-	`{}`,
-}
diff --git a/adapters/gamma/params_test.go b/adapters/gamma/params_test.go
index 56f1b591..c6a149c2 100644
--- a/adapters/gamma/params_test.go
+++ b/adapters/gamma/params_test.go
@@ -8,8 +8,6 @@ import (
 )
 
 // This file actually intends to test static/bidder-params/gamma.json
-//
-// These also validate the format of the external API: request.imp[i].ext.prebid.bidder.brightroll
 
 // TestValidParams makes sure that the Gamma schema accepts all imp.ext fields which we intend to support.
 func TestValidParams(t *testing.T) {
diff --git a/adapters/ownadx/ownadx_test.go b/adapters/ownadx/ownadx_test.go
new file mode 100644
index 00000000..9bd7ba70
--- /dev/null
+++ b/adapters/ownadx/ownadx_test.go
@@ -0,0 +1,17 @@
+package ownadx
+
+import (
+	"github.com/prebid/prebid-server/adapters/adapterstest"
+	"github.com/prebid/prebid-server/config"
+	"github.com/prebid/prebid-server/openrtb_ext"
+	"github.com/stretchr/testify/assert"
+	"testing"
+)
+
+func TestJsonSamples(t *testing.T) {
+	bidder, buildErr := Builder(openrtb_ext.BidderOwnAdx, config.Adapter{
+		Endpoint: "https://pbs.prebid-ownadx.com/bidder/bid/{{.AccountID}}/{{.ZoneID}}?token={{.SourceId}}"}, config.Server{ExternalUrl: "http://hosturl.com", GvlID: 1, DataCenter: "2"})
+
+	assert.NoError(t, buildErr)
+	adapterstest.RunJSONBidderTest(t, "ownadxtest", bidder)
+}
diff --git a/adapters/ownadx/ownadxtest/exemplary/banner.json b/adapters/ownadx/ownadxtest/exemplary/banner.json
new file mode 100644
index 00000000..f77321c5
--- /dev/null
+++ b/adapters/ownadx/ownadxtest/exemplary/banner.json
@@ -0,0 +1,166 @@
+{
+  "mockBidRequest": {
+    "id": "id",
+    "imp": [
+      {
+        "id": "id",
+        "secure": 1,
+        "bidfloor": 0.01,
+        "bidfloorcur": "USD",
+        "banner": {
+          "w": 300,
+          "h": 250
+        },
+        "ext": {
+          "bidder": {
+            "tokenId": "126151698247",
+            "sspId": "11",
+            "seatId": "2"
+          }
+        }
+      }
+    ],
+    "device": {
+      "ua": "UA",
+      "ip": "123.3.4.123"
+    },
+    "regs": {
+      "ext": {
+        "gdpr": 0
+      }
+    },
+    "user": {
+      "id": "userid"
+    },
+    "site": {
+      "id": "id",
+      "domain": "test,com",
+      "cat": [
+        "IAB12"
+      ],
+      "publisher": {
+        "id": "pubid"
+      }
+    }
+  },
+  "httpCalls": [
+    {
+      "expectedRequest": {
+        "uri": "https://pbs.prebid-ownadx.com/bidder/bid/2/11?token=126151698247",
+        "body": {
+          "id": "id",
+          "imp": [
+            {
+              "id": "id",
+              "secure": 1,
+              "bidfloor": 0.01,
+              "bidfloorcur": "USD",
+              "banner": {
+                "w": 300,
+                "h": 250
+              },
+              "ext": {
+                "bidder": {
+                  "tokenId": "126151698247",
+                  "sspId": "11",
+                  "seatId": "2"
+                }
+              }
+            }
+          ],
+          "device": {
+            "ua": "UA",
+            "ip": "123.3.4.123"
+          },
+          "regs": {
+            "ext": {
+              "gdpr": 0
+            }
+          },
+          "user": {
+            "id": "userid"
+          },
+          "site": {
+            "id": "id",
+            "domain": "test,com",
+            "cat": [
+              "IAB12"
+            ],
+            "publisher": {
+              "id": "pubid"
+            }
+          }
+        }
+      },
+      "mockResponse": {
+        "status": 200,
+        "body": {
+          "id": "id",
+          "bidid": "id",
+          "seatbid": [
+            {
+              "bid": [
+                {
+                  "mtype": 1,
+                  "id": "id",
+                  "impid": "id",
+                  "price": 0.1,
+                  "nurl": "http://test.com/nurl",
+                  "burl": "http://test.com/burl",
+                  "adm": "<span>Test</span>",
+                  "adomain": [
+                    "test.com"
+                  ],
+                  "cat": [
+                    "IAB1"
+                  ],
+                  "cid": "cid",
+                  "crid": "crid",
+                  "w": 300,
+                  "h": 250,
+                  "ext": {
+                    "mediaType": "banner"
+                  }
+                }
+              ],
+              "seat": "seat"
+            }
+          ],
+          "cur": "USD"
+        }
+      }
+    }
+  ],
+  "expectedBidResponses": [
+    {
+      "currency": "USD",
+      "bids": [
+        {
+          "bid": {
+            "mtype": 1,
+            "id": "id",
+            "impid": "id",
+            "price": 0.1,
+            "nurl": "http://test.com/nurl",
+            "burl": "http://test.com/burl",
+            "adm": "<span>Test</span>",
+            "adomain": [
+              "test.com"
+            ],
+            "cat": [
+              "IAB1"
+            ],
+            "cid": "cid",
+            "crid": "crid",
+            "w": 300,
+            "h": 250,
+            "ext": {
+              "mediaType": "banner"
+            }
+          },
+          "type": "banner"
+        }
+      ]
+    }
+  ]
+}
\ No newline at end of file
diff --git a/adapters/ownadx/ownadxtest/exemplary/video.json b/adapters/ownadx/ownadxtest/exemplary/video.json
new file mode 100644
index 00000000..7cac425c
--- /dev/null
+++ b/adapters/ownadx/ownadxtest/exemplary/video.json
@@ -0,0 +1,196 @@
+{
+  "mockBidRequest": {
+    "id": "id",
+    "imp": [
+      {
+        "id": "id",
+        "secure": 1,
+        "bidfloor": 0.01,
+        "bidfloorcur": "USD",
+        "video": {
+          "mimes": [
+            "video/mp4",
+            "video/ogg",
+            "video/webm"
+          ],
+          "minduration": 3,
+          "maxduration": 3000,
+          "protocols": [
+            2,
+            3,
+            5,
+            6,
+            7,
+            8
+          ],
+          "w": 480,
+          "h": 320,
+          "linearity": 1,
+          "playbackmethod": [
+            2
+          ],
+          "pos": 0
+        },
+        "ext": {
+          "bidder": {
+            "tokenId": "126151698247",
+            "sspId": "11",
+            "seatId": "2"
+          }
+        }
+      }
+    ],
+    "device": {
+      "ua": "UA",
+      "ip": "123.3.4.123"
+    },
+    "regs": {
+      "ext": {
+        "gdpr": 0
+      }
+    },
+    "user": {
+      "id": "userid"
+    },
+    "site": {
+      "id": "id",
+      "domain": "test,com",
+      "cat": [
+        "IAB12"
+      ],
+      "publisher": {
+        "id": "pubid"
+      }
+    }
+  },
+  "httpCalls": [
+    {
+      "expectedRequest": {
+        "uri": "https://pbs.prebid-ownadx.com/bidder/bid/2/11?token=126151698247",
+        "body": {
+          "id": "id",
+          "imp": [
+            {
+              "id": "id",
+              "secure": 1,
+              "bidfloor": 0.01,
+              "bidfloorcur": "USD",
+              "video": {
+                "mimes": [
+                  "video/mp4",
+                  "video/ogg",
+                  "video/webm"
+                ],
+                "minduration": 3,
+                "maxduration": 3000,
+                "protocols": [
+                  2,
+                  3,
+                  5,
+                  6,
+                  7,
+                  8
+                ],
+                "w": 480,
+                "h": 320,
+                "linearity": 1,
+                "playbackmethod": [
+                  2
+                ],
+                "pos": 0
+              },
+              "ext": {
+                "bidder": {
+                  "tokenId": "126151698247",
+                  "sspId": "11",
+                  "seatId": "2"
+                }
+              }
+            }
+          ],
+          "device": {
+            "ua": "UA",
+            "ip": "123.3.4.123"
+          },
+          "regs": {
+            "ext": {
+              "gdpr": 0
+            }
+          },
+          "user": {
+            "id": "userid"
+          },
+          "site": {
+            "id": "id",
+            "domain": "test,com",
+            "cat": [
+              "IAB12"
+            ],
+            "publisher": {
+              "id": "pubid"
+            }
+          }
+        }
+      },
+      "mockResponse": {
+        "status": 200,
+        "body": {
+          "id": "id",
+          "bidid": "id",
+          "seatbid": [
+            {
+              "bid": [
+                {
+                  "id": "id",
+                  "impid": "id",
+                  "price": 0.1,
+                  "nurl": "http://test.com/nurl",
+                  "burl": "http://test.com/burl",
+                  "adm": "<VAST>",
+                  "adomain": [
+                    "test.com"
+                  ],
+                  "cat": [
+                    "IAB1"
+                  ],
+                  "cid": "cid",
+                  "crid": "crid",
+                  "mtype": 2
+                }
+              ],
+              "seat": "seat"
+            }
+          ],
+          "cur": "USD"
+        }
+      }
+    }
+  ],
+  "expectedBidResponses": [
+    {
+      "currency": "USD",
+      "bids": [
+        {
+          "bid": {
+            "id": "id",
+            "impid": "id",
+            "price": 0.1,
+            "nurl": "http://test.com/nurl",
+            "burl": "http://test.com/burl",
+            "adm": "<VAST>",
+            "adomain": [
+              "test.com"
+            ],
+            "cat": [
+              "IAB1"
+            ],
+            "cid": "cid",
+            "crid": "crid",
+            "mtype": 2
+          },
+          "type": "video"
+        }
+      ]
+    }
+  ]
+}
\ No newline at end of file
diff --git a/adapters/ownadx/ownadxtest/supplemental/bad-server-response.json b/adapters/ownadx/ownadxtest/supplemental/bad-server-response.json
new file mode 100644
index 00000000..61b6da04
--- /dev/null
+++ b/adapters/ownadx/ownadxtest/supplemental/bad-server-response.json
@@ -0,0 +1,108 @@
+{
+  "mockBidRequest": {
+    "id": "id",
+    "imp": [
+      {
+        "id": "id",
+        "secure": 1,
+        "bidfloor": 0.01,
+        "bidfloorcur": "USD",
+        "banner": {
+          "w": 300,
+          "h": 250
+        },
+        "ext": {
+          "bidder": {
+            "tokenId": "126151698247",
+            "sspId": "11",
+            "seatId": "2"
+          }
+        }
+      }
+    ],
+    "device": {
+      "ua": "UA",
+      "ip": "123.3.4.123"
+    },
+    "regs": {
+      "ext": {
+        "gdpr": 0
+      }
+    },
+    "user": {
+      "id": "userid"
+    },
+    "site": {
+      "id": "id",
+      "domain": "test,com",
+      "cat": [
+        "IAB12"
+      ],
+      "publisher": {
+        "id": "pubid"
+      }
+    }
+  },
+  "httpCalls": [
+    {
+      "expectedRequest": {
+        "uri": "https://pbs.prebid-ownadx.com/bidder/bid/2/11?token=126151698247",
+        "body": {
+          "id": "id",
+          "imp": [
+            {
+              "id": "id",
+              "secure": 1,
+              "bidfloor": 0.01,
+              "bidfloorcur": "USD",
+              "banner": {
+                "w": 300,
+                "h": 250
+              },
+              "ext": {
+                "bidder": {
+                  "tokenId": "126151698247",
+                  "sspId": "11",
+                  "seatId": "2"
+                }
+              }
+            }
+          ],
+          "device": {
+            "ua": "UA",
+            "ip": "123.3.4.123"
+          },
+          "regs": {
+            "ext": {
+              "gdpr": 0
+            }
+          },
+          "user": {
+            "id": "userid"
+          },
+          "site": {
+            "id": "id",
+            "domain": "test,com",
+            "cat": [
+              "IAB12"
+            ],
+            "publisher": {
+              "id": "pubid"
+            }
+          }
+        }
+      },
+      "mockResponse": {
+        "status": 200
+
+      }
+    }
+  ],
+  "expectedMakeBidsErrors": [
+    {
+      "value": "Bad server response ",
+      "comparison": "literal"
+    }
+  ],
+  "expectedBidResponses": []
+}
\ No newline at end of file
diff --git a/adapters/ownadx/ownadxtest/supplemental/bid-empty-.json b/adapters/ownadx/ownadxtest/supplemental/bid-empty-.json
new file mode 100644
index 00000000..8d40e878
--- /dev/null
+++ b/adapters/ownadx/ownadxtest/supplemental/bid-empty-.json
@@ -0,0 +1,118 @@
+{
+  "mockBidRequest": {
+    "id": "id",
+    "imp": [
+      {
+        "id": "id",
+        "secure": 1,
+        "bidfloor": 0.01,
+        "bidfloorcur": "USD",
+        "banner": {
+          "w": 300,
+          "h": 250
+        },
+        "ext": {
+          "bidder": {
+            "tokenId": "126151698247",
+            "sspId": "11",
+            "seatId": "2"
+          }
+        }
+      }
+    ],
+    "device": {
+      "ua": "UA",
+      "ip": "123.3.4.123"
+    },
+    "regs": {
+      "ext": {
+        "gdpr": 0
+      }
+    },
+    "user": {
+      "id": "userid"
+    },
+    "site": {
+      "id": "id",
+      "domain": "test,com",
+      "cat": [
+        "IAB12"
+      ],
+      "publisher": {
+        "id": "pubid"
+      }
+    }
+  },
+  "httpCalls": [
+    {
+      "expectedRequest": {
+        "uri": "https://pbs.prebid-ownadx.com/bidder/bid/2/11?token=126151698247",
+        "body": {
+          "id": "id",
+          "imp": [
+            {
+              "id": "id",
+              "secure": 1,
+              "bidfloor": 0.01,
+              "bidfloorcur": "USD",
+              "banner": {
+                "w": 300,
+                "h": 250
+              },
+              "ext": {
+                "bidder": {
+                  "tokenId": "126151698247",
+                  "sspId": "11",
+                  "seatId": "2"
+                }
+              }
+            }
+          ],
+          "device": {
+            "ua": "UA",
+            "ip": "123.3.4.123"
+          },
+          "regs": {
+            "ext": {
+              "gdpr": 0
+            }
+          },
+          "user": {
+            "id": "userid"
+          },
+          "site": {
+            "id": "id",
+            "domain": "test,com",
+            "cat": [
+              "IAB12"
+            ],
+            "publisher": {
+              "id": "pubid"
+            }
+          }
+        }
+      },
+      "mockResponse": {
+        "status": 200,
+        "body": {
+          "id": "id",
+          "bidid": "id",
+          "seatbid": [
+            {
+              "seat": "seat"
+            }
+          ],
+          "cur": "USD"
+        }
+
+      }
+    }
+  ],
+  "expectedMakeBidsErrors": [
+    {
+      "value": "Bid cannot be empty ",
+      "comparison": "literal"
+    }
+  ],
+  "expectedBidResponses": []
+}
\ No newline at end of file
diff --git a/adapters/ownadx/ownadxtest/supplemental/bidext-type.json b/adapters/ownadx/ownadxtest/supplemental/bidext-type.json
new file mode 100644
index 00000000..b1d63428
--- /dev/null
+++ b/adapters/ownadx/ownadxtest/supplemental/bidext-type.json
@@ -0,0 +1,141 @@
+{
+  "mockBidRequest": {
+    "id": "id",
+    "imp": [
+      {
+        "id": "id",
+        "secure": 1,
+        "bidfloor": 0.01,
+        "bidfloorcur": "USD",
+        "banner": {
+          "w": 300,
+          "h": 250
+        },
+        "ext": {
+          "bidder": {
+            "tokenId": "126151698247",
+            "sspId": "11",
+            "seatId": "2"
+          }
+        }
+      }
+    ],
+    "device": {
+      "ua": "UA",
+      "ip": "123.3.4.123"
+    },
+    "regs": {
+      "ext": {
+        "gdpr": 0
+      }
+    },
+    "user": {
+      "id": "userid"
+    },
+    "site": {
+      "id": "id",
+      "domain": "test,com",
+      "cat": [
+        "IAB12"
+      ],
+      "publisher": {
+        "id": "pubid"
+      }
+    }
+  },
+  "httpCalls": [
+    {
+      "expectedRequest": {
+        "uri": "https://pbs.prebid-ownadx.com/bidder/bid/2/11?token=126151698247",
+        "body": {
+          "id": "id",
+          "imp": [
+            {
+              "id": "id",
+              "secure": 1,
+              "bidfloor": 0.01,
+              "bidfloorcur": "USD",
+              "banner": {
+                "w": 300,
+                "h": 250
+              },
+              "ext": {
+                "bidder": {
+                  "tokenId": "126151698247",
+                  "sspId": "11",
+                  "seatId": "2"
+                }
+              }
+            }
+          ],
+          "device": {
+            "ua": "UA",
+            "ip": "123.3.4.123"
+          },
+          "regs": {
+            "ext": {
+              "gdpr": 0
+            }
+          },
+          "user": {
+            "id": "userid"
+          },
+          "site": {
+            "id": "id",
+            "domain": "test,com",
+            "cat": [
+              "IAB12"
+            ],
+            "publisher": {
+              "id": "pubid"
+            }
+          }
+        }
+      },
+      "mockResponse": {
+        "status": 200,
+        "body": {
+          "id": "id",
+          "bidid": "id",
+          "seatbid": [
+            {
+              "bid": [
+                {
+                  "id": "id",
+                  "impid": "id",
+                  "price": 0.1,
+                  "nurl": "http://test.com/nurl",
+                  "burl": "http://test.com/burl",
+                  "adm": "<span>Test</span>",
+                  "adomain": [
+                    "test.com"
+                  ],
+                  "cat": [
+                    "IAB1"
+                  ],
+                  "cid": "cid",
+                  "crid": "crid",
+                  "w": 300,
+                  "h": 250,
+                  "ext": {
+
+                  }
+                }
+              ],
+              "seat": "seat"
+            }
+          ],
+          "cur": "USD"
+        }
+
+      }
+    }
+  ],
+  "expectedMakeBidsErrors": [
+    {
+      "value": "Bid type is invalid",
+      "comparison": "literal"
+    }
+  ],
+  "expectedBidResponses": []
+}
\ No newline at end of file
diff --git a/adapters/ownadx/ownadxtest/supplemental/http-status-204.json b/adapters/ownadx/ownadxtest/supplemental/http-status-204.json
new file mode 100644
index 00000000..4b7f8663
--- /dev/null
+++ b/adapters/ownadx/ownadxtest/supplemental/http-status-204.json
@@ -0,0 +1,51 @@
+{
+  "mockBidRequest": {
+    "id": "test-request-id",
+    "imp": [
+      {
+        "id": "test-imp-id",
+        "banner": {
+          "w": 300,
+          "h": 250
+        },
+        "ext": {
+          "bidder": {
+            "tokenId": "126151698247",
+            "sspId": "11",
+            "seatId": "2"
+          }
+        }
+      }
+    ]
+  },
+  "httpCalls": [
+    {
+      "expectedRequest": {
+        "uri": "https://pbs.prebid-ownadx.com/bidder/bid/2/11?token=126151698247",
+        "body": {
+          "id": "test-request-id",
+          "imp": [
+            {
+              "id": "test-imp-id",
+              "banner": {
+                "w": 300,
+                "h": 250
+              },
+              "ext": {
+                "bidder": {
+                  "tokenId": "126151698247",
+                  "sspId": "11",
+                  "seatId": "2"
+                }
+              }
+            }
+          ]
+        }
+      },
+      "mockResponse": {
+        "status": 204
+      }
+    }
+  ],
+  "expectedBidResponses": []
+}
\ No newline at end of file
diff --git a/adapters/ownadx/ownadxtest/supplemental/http-status-400.json b/adapters/ownadx/ownadxtest/supplemental/http-status-400.json
new file mode 100644
index 00000000..035bc323
--- /dev/null
+++ b/adapters/ownadx/ownadxtest/supplemental/http-status-400.json
@@ -0,0 +1,57 @@
+{
+  "mockBidRequest": {
+    "id": "test-request-id",
+    "imp": [
+      {
+        "id": "test-imp-id",
+        "banner": {
+          "w": 300,
+          "h": 250
+        },
+        "ext": {
+          "bidder": {
+            "tokenId": "126151698247",
+            "sspId": "11",
+            "seatId": "2"
+          }
+        }
+      }
+    ]
+  },
+  "httpCalls": [
+    {
+      "expectedRequest": {
+        "uri": "https://pbs.prebid-ownadx.com/bidder/bid/2/11?token=126151698247",
+        "body": {
+          "id": "test-request-id",
+          "imp": [
+            {
+              "id": "test-imp-id",
+              "banner": {
+                "w": 300,
+                "h": 250
+              },
+              "ext": {
+                "bidder": {
+                  "tokenId": "126151698247",
+                  "sspId": "11",
+                  "seatId": "2"
+                }
+              }
+            }
+          ]
+        }
+      },
+      "mockResponse": {
+        "status": 400
+      }
+    }
+  ],
+  "expectedMakeBidsErrors": [
+    {
+      "value": "Bad request: 400",
+      "comparison": "literal"
+    }
+  ],
+  "expectedBidResponses": []
+}
\ No newline at end of file
diff --git a/adapters/ownadx/ownadxtest/supplemental/invalid-req-empty-imp.json b/adapters/ownadx/ownadxtest/supplemental/invalid-req-empty-imp.json
new file mode 100644
index 00000000..0407ed6c
--- /dev/null
+++ b/adapters/ownadx/ownadxtest/supplemental/invalid-req-empty-imp.json
@@ -0,0 +1,34 @@
+{
+  "expectedMakeRequestsErrors": [
+    {
+      "value": "No impression in the bid request",
+      "comparison": "literal"
+    }
+  ],
+  "mockBidRequest": {
+    "id": "id",
+    "device": {
+      "ua": "UA",
+      "ip": "123.3.4.123"
+    },
+    "regs": {
+      "ext": {
+        "gdpr": 0
+      }
+    },
+    "user": {
+      "id": "userid"
+    },
+    "site": {
+      "id": "id",
+      "domain": "test,com",
+      "cat": [
+        "IAB12"
+      ],
+      "publisher": {
+        "id": "pubid"
+      }
+    }
+  },
+  "httpCalls": []
+}
diff --git a/adapters/ownadx/ownadxtest/supplemental/invalid-req-imp-ext.json b/adapters/ownadx/ownadxtest/supplemental/invalid-req-imp-ext.json
new file mode 100644
index 00000000..9b8ba0d1
--- /dev/null
+++ b/adapters/ownadx/ownadxtest/supplemental/invalid-req-imp-ext.json
@@ -0,0 +1,47 @@
+{
+  "expectedMakeRequestsErrors": [
+    {
+      "value": "Bidder extension not valid or can't be unmarshalled",
+      "comparison": "literal"
+    }
+  ],
+  "mockBidRequest": {
+    "id": "id",
+    "imp": [
+      {
+        "id": "id",
+        "secure": 1,
+        "bidfloor": 0.01,
+        "bidfloorcur": "USD",
+        "banner": {
+          "w": 300,
+          "h": 250
+        }
+
+      }
+    ],
+    "device": {
+      "ua": "UA",
+      "ip": "123.3.4.123"
+    },
+    "regs": {
+      "ext": {
+        "gdpr": 0
+      }
+    },
+    "user": {
+      "id": "userid"
+    },
+    "site": {
+      "id": "id",
+      "domain": "test,com",
+      "cat": [
+        "IAB12"
+      ],
+      "publisher": {
+        "id": "pubid"
+      }
+    }
+  },
+  "httpCalls": []
+}
diff --git a/adapters/ownadx/ownadxtest/supplemental/invalid-req-imp.ext.bidder.json b/adapters/ownadx/ownadxtest/supplemental/invalid-req-imp.ext.bidder.json
new file mode 100644
index 00000000..b04e037d
--- /dev/null
+++ b/adapters/ownadx/ownadxtest/supplemental/invalid-req-imp.ext.bidder.json
@@ -0,0 +1,49 @@
+{
+  "expectedMakeRequestsErrors": [
+    {
+      "value": "Error while unmarshaling bidder extension",
+      "comparison": "literal"
+    }
+  ],
+  "mockBidRequest": {
+    "id": "id",
+    "imp": [
+      {
+        "id": "id",
+        "secure": 1,
+        "bidfloor": 0.01,
+        "bidfloorcur": "USD",
+        "banner": {
+          "w": 300,
+          "h": 250
+        },
+        "ext": {
+          "bidder": []
+        }
+      }
+    ],
+    "device": {
+      "ua": "UA",
+      "ip": "123.3.4.123"
+    },
+    "regs": {
+      "ext": {
+        "gdpr": 0
+      }
+    },
+    "user": {
+      "id": "userid"
+    },
+    "site": {
+      "id": "id",
+      "domain": "test,com",
+      "cat": [
+        "IAB12"
+      ],
+      "publisher": {
+        "id": "pubid"
+      }
+    }
+  },
+  "httpCalls": []
+}
diff --git a/adapters/ownadx/ownadxtest/supplemental/seatbid-empty-.json b/adapters/ownadx/ownadxtest/supplemental/seatbid-empty-.json
new file mode 100644
index 00000000..f49ece3e
--- /dev/null
+++ b/adapters/ownadx/ownadxtest/supplemental/seatbid-empty-.json
@@ -0,0 +1,113 @@
+{
+  "mockBidRequest": {
+    "id": "id",
+    "imp": [
+      {
+        "id": "id",
+        "secure": 1,
+        "bidfloor": 0.01,
+        "bidfloorcur": "USD",
+        "banner": {
+          "w": 300,
+          "h": 250
+        },
+        "ext": {
+          "bidder": {
+            "tokenId": "126151698247",
+            "sspId": "11",
+            "seatId": "2"
+          }
+        }
+      }
+    ],
+    "device": {
+      "ua": "UA",
+      "ip": "123.3.4.123"
+    },
+    "regs": {
+      "ext": {
+        "gdpr": 0
+      }
+    },
+    "user": {
+      "id": "userid"
+    },
+    "site": {
+      "id": "id",
+      "domain": "test,com",
+      "cat": [
+        "IAB12"
+      ],
+      "publisher": {
+        "id": "pubid"
+      }
+    }
+  },
+  "httpCalls": [
+    {
+      "expectedRequest": {
+        "uri": "https://pbs.prebid-ownadx.com/bidder/bid/2/11?token=126151698247",
+        "body": {
+          "id": "id",
+          "imp": [
+            {
+              "id": "id",
+              "secure": 1,
+              "bidfloor": 0.01,
+              "bidfloorcur": "USD",
+              "banner": {
+                "w": 300,
+                "h": 250
+              },
+              "ext": {
+                "bidder": {
+                  "tokenId": "126151698247",
+                  "sspId": "11",
+                  "seatId": "2"
+                }
+              }
+            }
+          ],
+          "device": {
+            "ua": "UA",
+            "ip": "123.3.4.123"
+          },
+          "regs": {
+            "ext": {
+              "gdpr": 0
+            }
+          },
+          "user": {
+            "id": "userid"
+          },
+          "site": {
+            "id": "id",
+            "domain": "test,com",
+            "cat": [
+              "IAB12"
+            ],
+            "publisher": {
+              "id": "pubid"
+            }
+          }
+        }
+      },
+      "mockResponse": {
+        "status": 200,
+        "body": {
+          "id": "id",
+          "bidid": "id",
+          "cur": "USD"
+        }
+
+      }
+    }
+  ],
+  "expectedMakeBidsErrors": [
+    {
+      "value": "Array SeatBid cannot be empty ",
+      "comparison": "literal"
+    }
+  ],
+  "expectedBidResponses": []
+}
\ No newline at end of file
diff --git a/adapters/ownadx/ownadxtest/supplemental/unexpected-status.json b/adapters/ownadx/ownadxtest/supplemental/unexpected-status.json
new file mode 100644
index 00000000..f501a110
--- /dev/null
+++ b/adapters/ownadx/ownadxtest/supplemental/unexpected-status.json
@@ -0,0 +1,107 @@
+{
+  "mockBidRequest": {
+    "id": "id",
+    "imp": [
+      {
+        "id": "id",
+        "secure": 1,
+        "bidfloor": 0.01,
+        "bidfloorcur": "USD",
+        "banner": {
+          "w": 300,
+          "h": 250
+        },
+        "ext": {
+          "bidder": {
+            "tokenId": "126151698247",
+            "sspId": "11",
+            "seatId": "2"
+          }
+        }
+      }
+    ],
+    "device": {
+      "ua": "UA",
+      "ip": "123.3.4.123"
+    },
+    "regs": {
+      "ext": {
+        "gdpr": 0
+      }
+    },
+    "user": {
+      "id": "userid"
+    },
+    "site": {
+      "id": "id",
+      "domain": "test,com",
+      "cat": [
+        "IAB12"
+      ],
+      "publisher": {
+        "id": "pubid"
+      }
+    }
+  },
+  "httpCalls": [
+    {
+      "expectedRequest": {
+        "uri": "https://pbs.prebid-ownadx.com/bidder/bid/2/11?token=126151698247",
+        "body": {
+          "id": "id",
+          "imp": [
+            {
+              "id": "id",
+              "secure": 1,
+              "bidfloor": 0.01,
+              "bidfloorcur": "USD",
+              "banner": {
+                "w": 300,
+                "h": 250
+              },
+              "ext": {
+                "bidder": {
+                  "tokenId": "126151698247",
+                  "sspId": "11",
+                  "seatId": "2"
+                }
+              }
+            }
+          ],
+          "device": {
+            "ua": "UA",
+            "ip": "123.3.4.123"
+          },
+          "regs": {
+            "ext": {
+              "gdpr": 0
+            }
+          },
+          "user": {
+            "id": "userid"
+          },
+          "site": {
+            "id": "id",
+            "domain": "test,com",
+            "cat": [
+              "IAB12"
+            ],
+            "publisher": {
+              "id": "pubid"
+            }
+          }
+        }
+      },
+      "mockResponse": {
+        "status": 403
+      }
+    }
+  ],
+  "expectedMakeBidsErrors": [
+    {
+      "value": "Unexpected status code: 403. Run with request.test = 1 for more info.",
+      "comparison": "literal"
+    }
+  ],
+  "expectedBidResponses": []
+}
\ No newline at end of file
diff --git a/adapters/rtbhouse/rtbhousetest/exemplary/currency-conversion.json b/adapters/rtbhouse/rtbhousetest/exemplary/currency-conversion.json
new file mode 100644
index 00000000..95bfb1e4
--- /dev/null
+++ b/adapters/rtbhouse/rtbhousetest/exemplary/currency-conversion.json
@@ -0,0 +1,123 @@
+{
+    "mockBidRequest": {
+      "id": "test-request-id",
+      "imp": [
+        {
+          "id": "test-imp-id",
+          "banner": {
+            "format": [
+              {
+                "w": 300,
+                "h": 250
+              }
+            ]
+          },
+          "bidfloor": 1.00,
+          "bidfloorcur": "EUR",
+          "ext": {
+            "bidder": {
+              "placementId": "12345"
+            }
+          }
+        }
+      ],
+      "ext": {
+        "prebid": {
+          "currency": {
+            "rates": {
+              "EUR": {
+                "USD": 0.05
+              }
+            },
+            "usepbsrates": false
+          }
+        }
+      }
+    },
+    "httpCalls": [
+      {
+        "expectedRequest": {
+          "uri": "http://localhost/prebid_server",
+          "body": {
+            "id": "test-request-id",
+            "cur": ["USD"],
+            "imp": [
+              {
+                "banner": {
+                  "format": [
+                    {
+                      "h": 250,
+                      "w": 300
+                    }
+                  ]
+                },
+                "bidfloor": 0.05,
+                "bidfloorcur": "USD",
+                "ext": {
+                  "bidder": {
+                    "placementId": "12345"
+                  }
+                },
+                "id": "test-imp-id"
+              }
+            ],
+            "ext": {
+              "prebid": {
+                "currency": {
+                  "rates": {
+                    "EUR": {
+                      "USD": 0.05
+                    }
+                  },
+                  "usepbsrates": false
+                }
+              }
+             }
+          }
+        },
+        "mockResponse": {
+          "status": 200,
+          "body": {
+            "id": "test-response-id",
+            "cur": "USD",
+            "seatbid": [{
+              "seat": "rtbhouse",
+              "bid": [{
+                "id": "randomid",
+                "impid": "test-imp-id",
+                "price": 300,
+                "adid": "12345678",
+                "adm": "some-test-ad",
+                "cid": "987",
+                "crid": "12345678",
+                "h": 250,
+                "w": 300
+                }
+              ]
+            }]
+          }
+        }
+      }
+    ],
+    "expectedBidResponses": [
+      {
+        "currency": "USD",
+        "bids": [
+          {
+            "bid": {
+              "id": "randomid",
+              "impid": "test-imp-id",
+              "price": 300,
+              "adid": "12345678",
+              "adm": "some-test-ad",
+              "cid": "987",
+              "crid": "12345678",
+              "h": 250,
+              "w": 300
+            },
+            "type": "banner"
+          }
+        ]
+      }
+    ]
+  }
diff --git a/adapters/rtbhouse/rtbhousetest/exemplary/simple-banner.json b/adapters/rtbhouse/rtbhousetest/exemplary/simple-banner.json
index 3a636450..2a8c4681 100644
--- a/adapters/rtbhouse/rtbhousetest/exemplary/simple-banner.json
+++ b/adapters/rtbhouse/rtbhousetest/exemplary/simple-banner.json
@@ -23,6 +23,7 @@
             "uri": "http://localhost/prebid_server",
             "body": {
                 "id": "test-request-id",
+                "cur": ["USD"],
                 "site": {
                     "page": "https://good.site/url"
                 },
diff --git a/adapters/rtbhouse/rtbhousetest/supplemental/bad_response.json b/adapters/rtbhouse/rtbhousetest/supplemental/bad_response.json
index f84f5555..e0861fb0 100644
--- a/adapters/rtbhouse/rtbhousetest/supplemental/bad_response.json
+++ b/adapters/rtbhouse/rtbhousetest/supplemental/bad_response.json
@@ -26,6 +26,7 @@
         "uri": "http://localhost/prebid_server",
         "body": {
           "id": "test-request-id",
+          "cur": ["USD"],
           "imp": [
             {
               "id": "test-imp-id",
diff --git a/adapters/rtbhouse/rtbhousetest/supplemental/currency-missing-conversion.json b/adapters/rtbhouse/rtbhousetest/supplemental/currency-missing-conversion.json
new file mode 100644
index 00000000..d7d65e43
--- /dev/null
+++ b/adapters/rtbhouse/rtbhousetest/supplemental/currency-missing-conversion.json
@@ -0,0 +1,43 @@
+{
+    "mockBidRequest": {
+      "id": "test-request-id",
+      "imp": [
+        {
+          "id": "banner-imp-id",
+          "banner": {
+            "format": [
+              {
+                "w": 300,
+                "h": 250
+              }
+            ]
+          },
+          "bidfloor": 1.00,
+          "bidfloorcur": "JPY",
+          "ext": {
+            "bidder": {
+              "placement": "12345"
+            }
+          }
+        }
+      ],
+      "ext": {
+        "prebid": {
+          "currency": {
+              "rates": {
+              "MXN": {
+                  "USD": 0.05
+              }
+              },
+              "usepbsrates": false
+          }
+        }
+      }
+    },
+    "expectedMakeRequestsErrors": [
+      {
+        "value": "Currency conversion rate not found: 'JPY' => 'USD'",
+        "comparison": "literal"
+      }
+    ]
+  }
diff --git a/adapters/rtbhouse/rtbhousetest/supplemental/status_204.json b/adapters/rtbhouse/rtbhousetest/supplemental/status_204.json
index 0702c103..bb7754ec 100644
--- a/adapters/rtbhouse/rtbhousetest/supplemental/status_204.json
+++ b/adapters/rtbhouse/rtbhousetest/supplemental/status_204.json
@@ -26,6 +26,7 @@
         "uri": "http://localhost/prebid_server",
         "body": {
           "id": "test-request-id",
+          "cur": ["USD"],
           "imp": [
             {
               "id": "test-imp-id",
diff --git a/adapters/rtbhouse/rtbhousetest/supplemental/status_400.json b/adapters/rtbhouse/rtbhousetest/supplemental/status_400.json
index 65d21406..0b527932 100644
--- a/adapters/rtbhouse/rtbhousetest/supplemental/status_400.json
+++ b/adapters/rtbhouse/rtbhousetest/supplemental/status_400.json
@@ -26,6 +26,7 @@
         "uri": "http://localhost/prebid_server",
         "body": {
           "id": "test-request-id",
+          "cur": ["USD"],
           "imp": [
             {
               "id": "test-imp-id",
diff --git a/adapters/rtbhouse/rtbhousetest/supplemental/status_418.json b/adapters/rtbhouse/rtbhousetest/supplemental/status_418.json
index 4c5dd576..6ff3860e 100644
--- a/adapters/rtbhouse/rtbhousetest/supplemental/status_418.json
+++ b/adapters/rtbhouse/rtbhousetest/supplemental/status_418.json
@@ -26,6 +26,7 @@
         "uri": "http://localhost/prebid_server",
         "body": {
           "id": "test-request-id",
+          "cur": ["USD"],
           "imp": [
             {
               "id": "test-imp-id",
diff --git a/adapters/vox/params_test.go b/adapters/vox/params_test.go
new file mode 100644
index 00000000..be148d3b
--- /dev/null
+++ b/adapters/vox/params_test.go
@@ -0,0 +1,56 @@
+package vox
+
+import (
+	"encoding/json"
+	"testing"
+
+	"github.com/prebid/prebid-server/openrtb_ext"
+)
+
+func TestValidParams(t *testing.T) {
+	validator, err := openrtb_ext.NewBidderParamsValidator("../../static/bidder-params")
+	if err != nil {
+		t.Fatalf("Failed to fetch the json schema. %v", err)
+	}
+
+	for _, p := range validParams {
+		if err := validator.Validate(openrtb_ext.BidderVox, json.RawMessage(p)); err != nil {
+			t.Errorf("Schema rejected valid params: %s", p)
+		}
+	}
+}
+
+func TestInvalidParams(t *testing.T) {
+	validator, err := openrtb_ext.NewBidderParamsValidator("../../static/bidder-params")
+	if err != nil {
+		t.Fatalf("Failed to fetch the json schema. %v", err)
+	}
+
+	for _, p := range invalidParams {
+		if err := validator.Validate(openrtb_ext.BidderVox, json.RawMessage(p)); err == nil {
+			t.Errorf("Schema allowed invalid params: %s", p)
+		}
+	}
+}
+
+var validParams = []string{
+	`{"placementId": "64be6fe6685a271d37e900d2"}`,
+	`{"placementId": "Any String Basically"}`,
+	`{"placementId":""}`,
+	`{"placementId":"id", "imageUrl":"http://site.com/img1.png"}`,
+	`{"placementId":"id", "imageUrl":"http://site.com/img1.png", "displaySizes":["123x90", "1x1", "987x1111"]}`,
+}
+
+var invalidParams = []string{
+	`{"placementId": 42}`,
+	`{"placementId": null}`,
+	`{"placementId": 3.1415}`,
+	`{"placementId": true}`,
+	`{"placementId": false}`,
+	`{"placementId":"id", "imageUrl": null}`,
+	`{"placementId":"id", "imageUrl": true}`,
+	`{"placementId":"id", "imageUrl": []}`,
+	`{"placementId":"id", "imageUrl": "http://some.url", "displaySizes": null}`,
+	`{"placementId":"id", "imageUrl": "http://some.url", "displaySizes": {}}`,
+	`{"placementId":"id", "imageUrl": "http://some.url", "displaySizes": "String"}`,
+}
diff --git a/adapters/vox/vox_test.go b/adapters/vox/vox_test.go
new file mode 100644
index 00000000..95d11a8a
--- /dev/null
+++ b/adapters/vox/vox_test.go
@@ -0,0 +1,21 @@
+package vox
+
+import (
+	"testing"
+
+	"github.com/prebid/prebid-server/adapters/adapterstest"
+	"github.com/prebid/prebid-server/config"
+	"github.com/prebid/prebid-server/openrtb_ext"
+)
+
+func TestJsonSamples(t *testing.T) {
+	bidder, buildErr := Builder(openrtb_ext.BidderVox, config.Adapter{
+		Endpoint: "http://somecoolurlfor.vox"},
+		config.Server{ExternalUrl: "http://somecoolurlfor.vox", GvlID: 1, DataCenter: "2"})
+
+	if buildErr != nil {
+		t.Fatalf("Builder returned unexpected error: %v", buildErr)
+	}
+
+	adapterstest.RunJSONBidderTest(t, "voxtest", bidder)
+}
diff --git a/adapters/vox/voxtest/exemplary/banner.json b/adapters/vox/voxtest/exemplary/banner.json
new file mode 100644
index 00000000..898fcea4
--- /dev/null
+++ b/adapters/vox/voxtest/exemplary/banner.json
@@ -0,0 +1,82 @@
+{
+  "mockBidRequest": {
+    "id": "cf4b0abb-7ccc-4057-9914-c85f467260c6",
+    "site": {
+      "page": "http://some.site/url/page?id=338"
+    },
+    "cur": [ "USD" ],
+    "imp": [{
+        "id": "8a7510f9-0ca1-44c4-a8c6-1ce639b5eef9",
+        "banner": { "w": 100, "h": 100 },
+        "bidfloorcur": "USD",
+        "ext": {
+          "bidder": {
+            "placementId":"64b939146d66df22ccae95c5"
+          }
+        }
+      }]
+  },
+
+  "httpCalls": [{
+      "expectedRequest": {
+        "uri": "http://somecoolurlfor.vox",
+        "headers": {},
+        "body": {
+          "id": "cf4b0abb-7ccc-4057-9914-c85f467260c6",
+          "site": {
+            "page": "http://some.site/url/page?id=338"
+          },
+          "imp": [
+            {
+              "id": "8a7510f9-0ca1-44c4-a8c6-1ce639b5eef9",
+              "banner": {
+                "w": 100,
+                "h": 100
+              },
+              "bidfloorcur": "USD",
+              "ext": {
+                "bidder": {
+                  "placementId": "64b939146d66df22ccae95c5"
+                }
+              }
+            }
+          ],
+          "cur": [ "USD" ]
+        }
+      },
+      "mockResponse": {
+        "status": 200,
+        "headers": {},
+        "body": {
+          "id": "cf4b0abb-7ccc-4057-9914-c85f467260c6",
+          "cur": "USD",
+          "seatbid": [{
+            "bid": [{
+              "id": "e6a143f3-5176-4607-b09e-0e67e358b0b6",
+              "impid": "8a7510f9-0ca1-44c4-a8c6-1ce639b5eef9",
+              "price": 3.1415,
+              "adm": "<html><h1>Hi, there</h1></html>",
+              "w": 50,
+              "h": 50,
+              "mtype": 1
+            }]
+           }]
+        }}
+    }],
+
+    "expectedBidResponses": [{
+      "bids": [{
+        "currency": "USD",
+        "bid": {
+          "id": "e6a143f3-5176-4607-b09e-0e67e358b0b6",
+          "impid": "8a7510f9-0ca1-44c4-a8c6-1ce639b5eef9",
+          "price": 3.1415,
+          "adm": "<html><h1>Hi, there</h1></html>",
+          "w": 50,
+          "h": 50,
+          "mtype": 1
+        },
+        "type": "banner"
+      }]
+    }]
+}
\ No newline at end of file
diff --git a/adapters/vox/voxtest/exemplary/video.json b/adapters/vox/voxtest/exemplary/video.json
new file mode 100644
index 00000000..37464da1
--- /dev/null
+++ b/adapters/vox/voxtest/exemplary/video.json
@@ -0,0 +1,87 @@
+{
+  "mockBidRequest": {
+    "id": "9a4bdb67-43ef-488f-937d-cd01e9dce43d",
+    "site": {
+      "page": "http://blog.com/article/1"
+    },
+    "cur": ["PLN"],
+    "imp": [{
+        "id": "d190d6f3-5264-4df5-91b2-8a9c72cbeb6a",
+        "bidfloorcur": "PLN",
+        "video": {
+          "mimes": ["video/mp4"],
+          "minduration": 5,
+          "maxduration": 15,
+          "w": 1280,
+          "h": 720
+        },
+        "ext": {
+          "bidder": {
+            "placementId":"64b9486efecad2330718e233"
+          }
+        }
+      }]
+  },
+
+  "httpCalls": [{
+    "expectedRequest": {
+      "uri": "http://somecoolurlfor.vox",
+      "header": {},
+      "body": {
+        "id": "9a4bdb67-43ef-488f-937d-cd01e9dce43d",
+        "site": {
+          "page": "http://blog.com/article/1"
+        },
+        "cur": [ "PLN" ],
+        "imp": [{
+            "id": "d190d6f3-5264-4df5-91b2-8a9c72cbeb6a",
+            "bidfloorcur": "PLN",
+            "video": {
+              "mimes": ["video/mp4"],
+              "minduration": 5,
+              "maxduration": 15,
+              "w": 1280,
+              "h": 720
+            },
+            "ext": {
+              "bidder": {
+                "placementId":"64b9486efecad2330718e233"
+              }
+            }
+          }]
+      }
+    },
+
+    "mockResponse": {
+      "status": 200,
+      "headers": {},
+      "body": {
+        "id":"d64fdeae-cb1c-4322-8fb6-18f5ec49bb76",
+        "cur": "PLN",
+        "seatbid": [{
+          "bid": [{
+            "id": "05349123-29e2-4be0-b662-48914f75ebe1",
+            "impid": "d190d6f3-5264-4df5-91b2-8a9c72cbeb6a",
+            "price": 2.149,
+            "adm": "<VAST>...</VAST>",
+            "mtype": 2
+          }]
+        }]
+      }
+    }
+  }],
+
+  "expectedBidResponses": [{
+    "bids": [{
+      "currency": "PLN",
+      "bid": {
+        "id": "05349123-29e2-4be0-b662-48914f75ebe1",
+        "impid": "d190d6f3-5264-4df5-91b2-8a9c72cbeb6a",
+        "price": 2.149,
+        "adm": "<VAST>...</VAST>",
+        "mtype": 2
+      },
+      "type": "video"
+    }]
+  }]
+}
\ No newline at end of file
diff --git a/adapters/vox/voxtest/supplemental/response_204_to_nocontent.json b/adapters/vox/voxtest/supplemental/response_204_to_nocontent.json
new file mode 100644
index 00000000..0a49b85d
--- /dev/null
+++ b/adapters/vox/voxtest/supplemental/response_204_to_nocontent.json
@@ -0,0 +1,45 @@
+{
+  "mockBidRequest": {
+    "id": "Some Id (445455)",
+    "cur": ["AUD"],
+    "imp": [{
+      "id": "Impression id #1",
+      "banner": {},
+      "bidfloorcur": "AUD",
+      "ext": {
+        "bidder": {
+          "placementId": "64b94ad4ed136806629dd51c"
+        }
+      }
+    }]
+  },
+
+  "httpCalls": [{
+    "expectedRequest": {
+      "uri": "http://somecoolurlfor.vox",
+      "headers": {},
+      "body": {
+        "id": "Some Id (445455)",
+        "cur": ["AUD"],
+        "imp": [{
+          "id": "Impression id #1",
+          "banner": {},
+          "bidfloorcur": "AUD",
+          "ext": {
+            "bidder": {
+              "placementId": "64b94ad4ed136806629dd51c"
+            }
+          }
+        }]
+      }
+    },
+
+    "mockResponse": {
+      "status": 204,
+      "headers": {},
+      "body": {}
+    }
+  }],
+
+  "expectedBidResponses": []
+}
\ No newline at end of file
diff --git a/adapters/vox/voxtest/supplemental/response_500_to_error.json b/adapters/vox/voxtest/supplemental/response_500_to_error.json
new file mode 100644
index 00000000..f245ef45
--- /dev/null
+++ b/adapters/vox/voxtest/supplemental/response_500_to_error.json
@@ -0,0 +1,56 @@
+{
+  "mockBidRequest": {
+    "id": "It is hard to make up ids.",
+    "site": {
+      "page": "http://catsanddogs.com/article/887"
+    },
+    "cur": ["USD"],
+    "imp": [{
+      "id": "Impression id #4",
+      "bidfloorcur": "USD",
+      "video": { "w": 1280, "h": 720, "mimes": ["video/mp4"] },
+      "ext": {
+        "bidder": {
+          "placementId": "64b94ca2b5eb3605e0a0e3be"
+        }
+      }
+    }]
+  },
+
+  "httpCalls": [{
+    "expectedRequest": {
+      "uri": "http://somecoolurlfor.vox",
+      "heades": {},
+      "body": {
+        "id": "It is hard to make up ids.",
+        "site": {
+          "page": "http://catsanddogs.com/article/887"
+        },
+        "cur": ["USD"],
+        "imp": [{
+          "id": "Impression id #4",
+          "bidfloorcur": "USD",
+          "video": { "w": 1280, "h": 720, "mimes": ["video/mp4"] },
+          "ext": {
+            "bidder": {
+              "placementId": "64b94ca2b5eb3605e0a0e3be"
+            }
+          }
+        }]
+      }
+    },
+
+    "mockResponse": {
+      "status": 500,
+      "headers": {},
+      "body": {}
+    }
+  }],
+
+  "expectedBidResponses": [],
+
+  "expectedMakeBidsErrors": [{
+    "value": "Unexpected status code: 500",
+    "comparison": "regex"
+  }]
+}
\ No newline at end of file
diff --git a/config/config_test.go b/config/config_test.go
index 402f9ada..30733372 100644
--- a/config/config_test.go
+++ b/config/config_test.go
@@ -885,7 +885,6 @@ func TestUserSyncFromEnv(t *testing.T) {
 	assert.Nil(t, cfg.BidderInfos["bidder2"].Syncer.Redirect)
 	assert.Nil(t, cfg.BidderInfos["bidder2"].Syncer.SupportCORS)
 
-	assert.Nil(t, cfg.BidderInfos["brightroll"].Syncer)
 }
 
 func TestBidderInfoFromEnv(t *testing.T) {
diff --git a/endpoints/openrtb2/amp_auction_test.go b/endpoints/openrtb2/amp_auction_test.go
index d709a702..87033f71 100644
--- a/endpoints/openrtb2/amp_auction_test.go
+++ b/endpoints/openrtb2/amp_auction_test.go
@@ -216,6 +216,7 @@ func TestAMPPageInfo(t *testing.T) {
 		openrtb_ext.BuildBidderMap(),
 		empty_fetcher.EmptyFetcher{},
 		hooks.EmptyPlanBuilder{},
+		nil,
 	)
 	request := httptest.NewRequest("GET", fmt.Sprintf("/openrtb2/auction/amp?tag_id=1&curl=%s", url.QueryEscape(page)), nil)
 	recorder := httptest.NewRecorder()
@@ -319,6 +320,7 @@ func TestGDPRConsent(t *testing.T) {
 			openrtb_ext.BuildBidderMap(),
 			empty_fetcher.EmptyFetcher{},
 			hooks.EmptyPlanBuilder{},
+			nil,
 		)
 
 		// Invoke Endpoint
@@ -742,6 +744,7 @@ func TestCCPAConsent(t *testing.T) {
 			openrtb_ext.BuildBidderMap(),
 			empty_fetcher.EmptyFetcher{},
 			hooks.EmptyPlanBuilder{},
+			nil,
 		)
 
 		// Invoke Endpoint
@@ -855,6 +858,7 @@ func TestConsentWarnings(t *testing.T) {
 			openrtb_ext.BuildBidderMap(),
 			empty_fetcher.EmptyFetcher{},
 			hooks.EmptyPlanBuilder{},
+			nil,
 		)
 
 		// Invoke Endpoint
@@ -953,6 +957,7 @@ func TestNewAndLegacyConsentBothProvided(t *testing.T) {
 			openrtb_ext.BuildBidderMap(),
 			empty_fetcher.EmptyFetcher{},
 			hooks.EmptyPlanBuilder{},
+			nil,
 		)
 
 		// Invoke Endpoint
@@ -1007,6 +1012,7 @@ func TestAMPSiteExt(t *testing.T) {
 		openrtb_ext.BuildBidderMap(),
 		empty_fetcher.EmptyFetcher{},
 		hooks.EmptyPlanBuilder{},
+		nil,
 	)
 	request, err := http.NewRequest("GET", "/openrtb2/auction/amp?tag_id=1", nil)
 	if !assert.NoError(t, err) {
@@ -1049,6 +1055,7 @@ func TestAmpBadRequests(t *testing.T) {
 		openrtb_ext.BuildBidderMap(),
 		empty_fetcher.EmptyFetcher{},
 		hooks.EmptyPlanBuilder{},
+		nil,
 	)
 	for requestID := range badRequests {
 		request := httptest.NewRequest("GET", fmt.Sprintf("/openrtb2/auction/amp?tag_id=%s", requestID), nil)
@@ -1082,6 +1089,7 @@ func TestAmpDebug(t *testing.T) {
 		openrtb_ext.BuildBidderMap(),
 		empty_fetcher.EmptyFetcher{},
 		hooks.EmptyPlanBuilder{},
+		nil,
 	)
 
 	for requestID := range requests {
@@ -1217,6 +1225,7 @@ func TestQueryParamOverrides(t *testing.T) {
 		openrtb_ext.BuildBidderMap(),
 		empty_fetcher.EmptyFetcher{},
 		hooks.EmptyPlanBuilder{},
+		nil,
 	)
 
 	requestID := "1"
@@ -1374,6 +1383,7 @@ func (s formatOverrideSpec) execute(t *testing.T) {
 		openrtb_ext.BuildBidderMap(),
 		empty_fetcher.EmptyFetcher{},
 		hooks.EmptyPlanBuilder{},
+		nil,
 	)
 
 	url := fmt.Sprintf("/openrtb2/auction/amp?tag_id=1&debug=1&w=%d&h=%d&ow=%d&oh=%d&ms=%s&account=%s", s.width, s.height, s.overrideWidth, s.overrideHeight, s.multisize, s.account)
@@ -1914,6 +1924,7 @@ func ampObjectTestSetup(t *testing.T, inTagId string, inStoredRequest json.RawMe
 		openrtb_ext.BuildBidderMap(),
 		empty_fetcher.EmptyFetcher{},
 		hooks.EmptyPlanBuilder{},
+		nil,
 	)
 	return &actualAmpObject, endpoint
 }
@@ -1966,6 +1977,7 @@ func TestAmpAuctionResponseHeaders(t *testing.T) {
 		openrtb_ext.BuildBidderMap(),
 		empty_fetcher.EmptyFetcher{},
 		hooks.EmptyPlanBuilder{},
+		nil,
 	)
 
 	for _, test := range testCases {
@@ -2001,6 +2013,7 @@ func TestRequestWithTargeting(t *testing.T) {
 		openrtb_ext.BuildBidderMap(),
 		empty_fetcher.EmptyFetcher{},
 		hooks.EmptyPlanBuilder{},
+		nil,
 	)
 	url, err := url.Parse("/openrtb2/auction/amp")
 	assert.NoError(t, err, "unexpected error received while parsing url")
diff --git a/endpoints/openrtb2/auction_benchmark_test.go b/endpoints/openrtb2/auction_benchmark_test.go
index 9ad3c768..a3c19ee0 100644
--- a/endpoints/openrtb2/auction_benchmark_test.go
+++ b/endpoints/openrtb2/auction_benchmark_test.go
@@ -111,6 +111,7 @@ func BenchmarkOpenrtbEndpoint(b *testing.B) {
 		nil,
 		empty_fetcher.EmptyFetcher{},
 		hooks.EmptyPlanBuilder{},
+		nil,
 	)
 
 	b.ResetTimer()
diff --git a/endpoints/openrtb2/auction_test.go b/endpoints/openrtb2/auction_test.go
index c6604fcf..b39f6e03 100644
--- a/endpoints/openrtb2/auction_test.go
+++ b/endpoints/openrtb2/auction_test.go
@@ -454,7 +454,9 @@ func TestExplicitUserId(t *testing.T) {
 		[]byte{},
 		openrtb_ext.BuildBidderMap(),
 		empty_fetcher.EmptyFetcher{},
-		hooks.EmptyPlanBuilder{})
+		hooks.EmptyPlanBuilder{},
+		nil,
+	)
 
 	endpoint(httptest.NewRecorder(), request, nil)
 
@@ -510,7 +512,9 @@ func doBadAliasRequest(t *testing.T, filename string, expectMsg string) {
 		aliasJSON,
 		bidderMap,
 		empty_fetcher.EmptyFetcher{},
-		hooks.EmptyPlanBuilder{})
+		hooks.EmptyPlanBuilder{},
+		nil,
+	)
 
 	request := httptest.NewRequest("POST", "/openrtb2/auction", bytes.NewReader(testBidRequest))
 	recorder := httptest.NewRecorder()
@@ -562,7 +566,9 @@ func TestNilExchange(t *testing.T) {
 		[]byte{},
 		openrtb_ext.BuildBidderMap(),
 		empty_fetcher.EmptyFetcher{},
-		hooks.EmptyPlanBuilder{})
+		hooks.EmptyPlanBuilder{},
+		nil,
+	)
 
 	if err == nil {
 		t.Errorf("NewEndpoint should return an error when given a nil Exchange.")
@@ -586,7 +592,9 @@ func TestNilValidator(t *testing.T) {
 		[]byte{},
 		openrtb_ext.BuildBidderMap(),
 		empty_fetcher.EmptyFetcher{},
-		hooks.EmptyPlanBuilder{})
+		hooks.EmptyPlanBuilder{},
+		nil,
+	)
 
 	if err == nil {
 		t.Errorf("NewEndpoint should return an error when given a nil BidderParamValidator.")
@@ -610,7 +618,9 @@ func TestExchangeError(t *testing.T) {
 		[]byte{},
 		openrtb_ext.BuildBidderMap(),
 		empty_fetcher.EmptyFetcher{},
-		hooks.EmptyPlanBuilder{})
+		hooks.EmptyPlanBuilder{},
+		nil,
+	)
 
 	request := httptest.NewRequest("POST", "/openrtb2/auction", strings.NewReader(validRequest(t, "site.json")))
 	recorder := httptest.NewRecorder()
@@ -735,7 +745,9 @@ func TestImplicitIPsEndToEnd(t *testing.T) {
 			[]byte{},
 			openrtb_ext.BuildBidderMap(),
 			empty_fetcher.EmptyFetcher{},
-			hooks.EmptyPlanBuilder{})
+			hooks.EmptyPlanBuilder{},
+			nil,
+		)
 
 		httpReq := httptest.NewRequest("POST", "/openrtb2/auction", strings.NewReader(validRequest(t, test.reqJSONFile)))
 		httpReq.Header.Set("X-Forwarded-For", test.xForwardedForHeader)
@@ -933,7 +945,9 @@ func TestImplicitDNTEndToEnd(t *testing.T) {
 			[]byte{},
 			openrtb_ext.BuildBidderMap(),
 			empty_fetcher.EmptyFetcher{},
-			hooks.EmptyPlanBuilder{})
+			hooks.EmptyPlanBuilder{},
+			nil,
+		)
 
 		httpReq := httptest.NewRequest("POST", "/openrtb2/auction", strings.NewReader(validRequest(t, test.reqJSONFile)))
 		httpReq.Header.Set("DNT", test.dntHeader)
@@ -1144,6 +1158,7 @@ func TestStoredRequests(t *testing.T) {
 		hardcodedResponseIPValidator{response: true},
 		empty_fetcher.EmptyFetcher{},
 		hooks.EmptyPlanBuilder{},
+		nil,
 	}
 
 	testStoreVideoAttr := []bool{true, true, false, false, false}
@@ -1517,6 +1532,7 @@ func TestValidateRequest(t *testing.T) {
 		hardcodedResponseIPValidator{response: true},
 		empty_fetcher.EmptyFetcher{},
 		hooks.EmptyPlanBuilder{},
+		nil,
 	}
 
 	testCases := []struct {
@@ -1611,7 +1627,7 @@ func TestValidateRequest(t *testing.T) {
 							Ext: []byte(`{"appnexus":{"placementId": 12345678}}`),
 						},
 					},
-					Ext: []byte(`{"prebid":{"aliases":{"brightroll":"appnexus"}, "aliasgvlids":{"pubmatic1":1}}}`),
+					Ext: []byte(`{"prebid":{"aliases":{"yahoossp":"appnexus"}, "aliasgvlids":{"pubmatic1":1}}}`),
 				},
 			},
 			givenIsAmp:            false,
@@ -1642,11 +1658,11 @@ func TestValidateRequest(t *testing.T) {
 							Ext: []byte(`{"appnexus":{"placementId": 12345678}}`),
 						},
 					},
-					Ext: []byte(`{"prebid":{"aliases":{"brightroll":"appnexus"}, "aliasgvlids":{"brightroll":0}}}`),
+					Ext: []byte(`{"prebid":{"aliases":{"yahoossp":"appnexus"}, "aliasgvlids":{"yahoossp":0}}}`),
 				},
 			},
 			givenIsAmp:            false,
-			expectedErrorList:     []error{errors.New("request.ext.prebid.aliasgvlids. Invalid vendorId 0 for alias: brightroll. Choose a different vendorId, or remove this entry.")},
+			expectedErrorList:     []error{errors.New("request.ext.prebid.aliasgvlids. Invalid vendorId 0 for alias: yahoossp. Choose a different vendorId, or remove this entry.")},
 			expectedChannelObject: &openrtb_ext.ExtRequestPrebidChannel{Name: appChannel, Version: ""},
 		},
 		{
@@ -1673,7 +1689,7 @@ func TestValidateRequest(t *testing.T) {
 							Ext: []byte(`{"appnexus":{"placementId": 12345678}}`),
 						},
 					},
-					Ext: []byte(`{"prebid":{"aliases":{"brightroll":"appnexus"}, "aliasgvlids":{"brightroll":1}}}`),
+					Ext: []byte(`{"prebid":{"aliases":{"yahoossp":"appnexus"}, "aliasgvlids":{"yahoossp":1}}}`),
 				},
 			},
 			givenIsAmp:            false,
@@ -2235,6 +2251,7 @@ func TestSetIntegrationType(t *testing.T) {
 		hardcodedResponseIPValidator{response: true},
 		empty_fetcher.EmptyFetcher{},
 		hooks.EmptyPlanBuilder{},
+		nil,
 	}
 
 	testCases := []struct {
@@ -2300,6 +2317,7 @@ func TestStoredRequestGenerateUuid(t *testing.T) {
 		hardcodedResponseIPValidator{response: true},
 		empty_fetcher.EmptyFetcher{},
 		hooks.EmptyPlanBuilder{},
+		nil,
 	}
 
 	req := &openrtb2.BidRequest{}
@@ -2403,6 +2421,7 @@ func TestOversizedRequest(t *testing.T) {
 		hardcodedResponseIPValidator{response: true},
 		empty_fetcher.EmptyFetcher{},
 		hooks.EmptyPlanBuilder{},
+		nil,
 	}
 
 	req := httptest.NewRequest("POST", "/openrtb2/auction", strings.NewReader(reqBody))
@@ -2441,6 +2460,7 @@ func TestRequestSizeEdgeCase(t *testing.T) {
 		hardcodedResponseIPValidator{response: true},
 		empty_fetcher.EmptyFetcher{},
 		hooks.EmptyPlanBuilder{},
+		nil,
 	}
 
 	req := httptest.NewRequest("POST", "/openrtb2/auction", strings.NewReader(reqBody))
@@ -2473,6 +2493,7 @@ func TestNoEncoding(t *testing.T) {
 		openrtb_ext.BuildBidderMap(),
 		empty_fetcher.EmptyFetcher{},
 		hooks.EmptyPlanBuilder{},
+		nil,
 	)
 	request := httptest.NewRequest("POST", "/openrtb2/auction", strings.NewReader(validRequest(t, "site.json")))
 	recorder := httptest.NewRecorder()
@@ -2557,6 +2578,7 @@ func TestContentType(t *testing.T) {
 		openrtb_ext.BuildBidderMap(),
 		empty_fetcher.EmptyFetcher{},
 		hooks.EmptyPlanBuilder{},
+		nil,
 	)
 	request := httptest.NewRequest("POST", "/openrtb2/auction", strings.NewReader(validRequest(t, "site.json")))
 	recorder := httptest.NewRecorder()
@@ -2777,6 +2799,7 @@ func TestValidateImpExt(t *testing.T) {
 		hardcodedResponseIPValidator{response: true},
 		empty_fetcher.EmptyFetcher{},
 		hooks.EmptyPlanBuilder{},
+		nil,
 	}
 
 	for _, group := range testGroups {
@@ -2829,6 +2852,7 @@ func TestCurrencyTrunc(t *testing.T) {
 		hardcodedResponseIPValidator{response: true},
 		empty_fetcher.EmptyFetcher{},
 		hooks.EmptyPlanBuilder{},
+		nil,
 	}
 
 	ui := int64(1)
@@ -2876,6 +2900,7 @@ func TestCCPAInvalid(t *testing.T) {
 		hardcodedResponseIPValidator{response: true},
 		empty_fetcher.EmptyFetcher{},
 		hooks.EmptyPlanBuilder{},
+		nil,
 	}
 
 	ui := int64(1)
@@ -2927,6 +2952,7 @@ func TestNoSaleInvalid(t *testing.T) {
 		hardcodedResponseIPValidator{response: true},
 		empty_fetcher.EmptyFetcher{},
 		hooks.EmptyPlanBuilder{},
+		nil,
 	}
 
 	ui := int64(1)
@@ -2981,6 +3007,7 @@ func TestValidateSourceTID(t *testing.T) {
 		hardcodedResponseIPValidator{response: true},
 		empty_fetcher.EmptyFetcher{},
 		hooks.EmptyPlanBuilder{},
+		nil,
 	}
 
 	ui := int64(1)
@@ -3025,6 +3052,7 @@ func TestSChainInvalid(t *testing.T) {
 		hardcodedResponseIPValidator{response: true},
 		empty_fetcher.EmptyFetcher{},
 		hooks.EmptyPlanBuilder{},
+		nil,
 	}
 
 	ui := int64(1)
@@ -3520,6 +3548,7 @@ func TestEidPermissionsInvalid(t *testing.T) {
 		hardcodedResponseIPValidator{response: true},
 		empty_fetcher.EmptyFetcher{},
 		hooks.EmptyPlanBuilder{},
+		nil,
 	}
 
 	ui := int64(1)
@@ -3768,7 +3797,9 @@ func TestIOS14EndToEnd(t *testing.T) {
 		[]byte{},
 		openrtb_ext.BuildBidderMap(),
 		empty_fetcher.EmptyFetcher{},
-		hooks.EmptyPlanBuilder{})
+		hooks.EmptyPlanBuilder{},
+		nil,
+	)
 
 	httpReq := httptest.NewRequest("POST", "/openrtb2/auction", strings.NewReader(validRequest(t, "app-ios140-no-ifa.json")))
 
@@ -3834,6 +3865,7 @@ func TestAuctionWarnings(t *testing.T) {
 		hardcodedResponseIPValidator{response: true},
 		empty_fetcher.EmptyFetcher{},
 		hooks.EmptyPlanBuilder{},
+		nil,
 	}
 	for _, test := range testCases {
 		t.Run(test.name, func(t *testing.T) {
@@ -3879,6 +3911,7 @@ func TestParseRequestParseImpInfoError(t *testing.T) {
 		hardcodedResponseIPValidator{response: true},
 		empty_fetcher.EmptyFetcher{},
 		hooks.EmptyPlanBuilder{},
+		nil,
 	}
 
 	hookExecutor := hookexecution.NewHookExecutor(deps.hookExecutionPlanBuilder, hookexecution.EndpointAuction, deps.metricsEngine)
@@ -3958,6 +3991,7 @@ func TestParseGzipedRequest(t *testing.T) {
 		hardcodedResponseIPValidator{response: true},
 		empty_fetcher.EmptyFetcher{},
 		hooks.EmptyPlanBuilder{},
+		nil,
 	}
 
 	hookExecutor := hookexecution.NewHookExecutor(deps.hookExecutionPlanBuilder, hookexecution.EndpointAuction, deps.metricsEngine)
@@ -4451,7 +4485,9 @@ func TestAuctionResponseHeaders(t *testing.T) {
 		[]byte{},
 		openrtb_ext.BuildBidderMap(),
 		empty_fetcher.EmptyFetcher{},
-		hooks.EmptyPlanBuilder{})
+		hooks.EmptyPlanBuilder{},
+		nil,
+	)
 
 	for _, test := range testCases {
 		httpReq := httptest.NewRequest("POST", "/openrtb2/auction", strings.NewReader(test.requestBody))
@@ -4555,6 +4591,7 @@ func TestParseRequestMergeBidderParams(t *testing.T) {
 				hardcodedResponseIPValidator{response: true},
 				empty_fetcher.EmptyFetcher{},
 				hooks.EmptyPlanBuilder{},
+				nil,
 			}
 
 			hookExecutor := hookexecution.NewHookExecutor(deps.hookExecutionPlanBuilder, hookexecution.EndpointAuction, deps.metricsEngine)
@@ -4657,6 +4694,7 @@ func TestParseRequestStoredResponses(t *testing.T) {
 				hardcodedResponseIPValidator{response: true},
 				&mockStoredResponseFetcher{mockStoredResponses},
 				hooks.EmptyPlanBuilder{},
+				nil,
 			}
 
 			hookExecutor := hookexecution.NewHookExecutor(deps.hookExecutionPlanBuilder, hookexecution.EndpointAuction, deps.metricsEngine)
@@ -4744,6 +4782,7 @@ func TestParseRequestStoredBidResponses(t *testing.T) {
 				hardcodedResponseIPValidator{response: true},
 				&mockStoredResponseFetcher{mockStoredBidResponses},
 				hooks.EmptyPlanBuilder{},
+				nil,
 			}
 
 			hookExecutor := hookexecution.NewHookExecutor(deps.hookExecutionPlanBuilder, hookexecution.EndpointAuction, deps.metricsEngine)
@@ -4780,6 +4819,7 @@ func TestValidateStoredResp(t *testing.T) {
 		hardcodedResponseIPValidator{response: true},
 		&mockStoredResponseFetcher{},
 		hooks.EmptyPlanBuilder{},
+		nil,
 	}
 
 	testCases := []struct {
@@ -5592,6 +5632,7 @@ func TestParseRequestMultiBid(t *testing.T) {
 				hardcodedResponseIPValidator{response: true},
 				empty_fetcher.EmptyFetcher{},
 				hooks.EmptyPlanBuilder{},
+				nil,
 			}
 
 			hookExecutor := hookexecution.NewHookExecutor(deps.hookExecutionPlanBuilder, hookexecution.EndpointAuction, deps.metricsEngine)
diff --git a/endpoints/openrtb2/test_utils.go b/endpoints/openrtb2/test_utils.go
index a93325ea..401fbf62 100644
--- a/endpoints/openrtb2/test_utils.go
+++ b/endpoints/openrtb2/test_utils.go
@@ -1290,7 +1290,7 @@ func buildTestEndpoint(test testCase, cfg *config.Configuration) (httprouter.Han
 		planBuilder = hooks.EmptyPlanBuilder{}
 	}
 
-	var endpointBuilder func(uuidutil.UUIDGenerator, exchange.Exchange, openrtb_ext.BidderParamValidator, stored_requests.Fetcher, stored_requests.AccountFetcher, *config.Configuration, metrics.MetricsEngine, analytics.PBSAnalyticsModule, map[string]string, []byte, map[string]openrtb_ext.BidderName, stored_requests.Fetcher, hooks.ExecutionPlanBuilder) (httprouter.Handle, error)
+	var endpointBuilder func(uuidutil.UUIDGenerator, exchange.Exchange, openrtb_ext.BidderParamValidator, stored_requests.Fetcher, stored_requests.AccountFetcher, *config.Configuration, metrics.MetricsEngine, analytics.PBSAnalyticsModule, map[string]string, []byte, map[string]openrtb_ext.BidderName, stored_requests.Fetcher, hooks.ExecutionPlanBuilder, *exchange.TmaxAdjustmentsPreprocessed) (httprouter.Handle, error)
 
 	switch test.endpointType {
 	case AMP_ENDPOINT:
@@ -1313,6 +1313,7 @@ func buildTestEndpoint(test testCase, cfg *config.Configuration) (httprouter.Han
 		bidderMap,
 		storedResponseFetcher,
 		planBuilder,
+		nil,
 	)
 
 	return endpoint, testExchange.(*exchangeTestWrapper), mockBidServersArray, mockCurrencyRatesServer, err
diff --git a/endpoints/openrtb2/video_auction_test.go b/endpoints/openrtb2/video_auction_test.go
index 496a2ac8..1f28efb8 100644
--- a/endpoints/openrtb2/video_auction_test.go
+++ b/endpoints/openrtb2/video_auction_test.go
@@ -1225,6 +1225,7 @@ func mockDepsWithMetrics(t *testing.T, ex *mockExchangeVideo) (*endpointDeps, *m
 		hardcodedResponseIPValidator{response: true},
 		empty_fetcher.EmptyFetcher{},
 		hooks.EmptyPlanBuilder{},
+		nil,
 	}
 	return deps, metrics, mockModule
 }
@@ -1270,6 +1271,7 @@ func mockDeps(t *testing.T, ex *mockExchangeVideo) *endpointDeps {
 		hardcodedResponseIPValidator{response: true},
 		empty_fetcher.EmptyFetcher{},
 		hooks.EmptyPlanBuilder{},
+		nil,
 	}
 }
 
@@ -1293,6 +1295,7 @@ func mockDepsAppendBidderNames(t *testing.T, ex *mockExchangeAppendBidderNames)
 		hardcodedResponseIPValidator{response: true},
 		empty_fetcher.EmptyFetcher{},
 		hooks.EmptyPlanBuilder{},
+		nil,
 	}
 
 	return deps
@@ -1318,6 +1321,7 @@ func mockDepsNoBids(t *testing.T, ex *mockExchangeVideoNoBids) *endpointDeps {
 		hardcodedResponseIPValidator{response: true},
 		empty_fetcher.EmptyFetcher{},
 		hooks.EmptyPlanBuilder{},
+		nil,
 	}
 
 	return edep
diff --git a/endpoints/setuid_test.go b/endpoints/setuid_test.go
index 9176041e..65bc2655 100644
--- a/endpoints/setuid_test.go
+++ b/endpoints/setuid_test.go
@@ -50,7 +50,7 @@ func TestSetUIDEndpoint(t *testing.T) {
 			description:            "Set uid for valid bidder",
 		},
 		{
-			uri:                    "/setuid?bidder=adnxs&uid=123",
+			uri:                    "/setuid?bidder=appnexus&uid=123",
 			syncersBidderNameToKey: map[string]string{"appnexus": "adnxs"},
 			existingSyncs:          nil,
 			gdprAllowsHostCookies:  true,
diff --git a/exchange/adapter_util_test.go b/exchange/adapter_util_test.go
index 3b818bdd..2f3c8e9a 100644
--- a/exchange/adapter_util_test.go
+++ b/exchange/adapter_util_test.go
@@ -222,6 +222,7 @@ func TestGetDisabledBiddersErrorMessages(t *testing.T) {
 				"oftmedia":       `Bidder "oftmedia" is no longer available in Prebid Server. Please update your configuration.`,
 				"groupm":         `Bidder "groupm" is no longer available in Prebid Server. Please update your configuration.`,
 				"verizonmedia":   `Bidder "verizonmedia" is no longer available in Prebid Server. Please update your configuration.`,
+				"brightroll":     `Bidder "brightroll" is no longer available in Prebid Server. Please update your configuration.`,
 			},
 		},
 		{
@@ -236,6 +237,7 @@ func TestGetDisabledBiddersErrorMessages(t *testing.T) {
 				"oftmedia":       `Bidder "oftmedia" is no longer available in Prebid Server. Please update your configuration.`,
 				"groupm":         `Bidder "groupm" is no longer available in Prebid Server. Please update your configuration.`,
 				"verizonmedia":   `Bidder "verizonmedia" is no longer available in Prebid Server. Please update your configuration.`,
+				"brightroll":     `Bidder "brightroll" is no longer available in Prebid Server. Please update your configuration.`,
 			},
 		},
 		{
@@ -251,6 +253,7 @@ func TestGetDisabledBiddersErrorMessages(t *testing.T) {
 				"oftmedia":       `Bidder "oftmedia" is no longer available in Prebid Server. Please update your configuration.`,
 				"groupm":         `Bidder "groupm" is no longer available in Prebid Server. Please update your configuration.`,
 				"verizonmedia":   `Bidder "verizonmedia" is no longer available in Prebid Server. Please update your configuration.`,
+				"brightroll":     `Bidder "brightroll" is no longer available in Prebid Server. Please update your configuration.`,
 			},
 		},
 		{
@@ -266,6 +269,7 @@ func TestGetDisabledBiddersErrorMessages(t *testing.T) {
 				"oftmedia":       `Bidder "oftmedia" is no longer available in Prebid Server. Please update your configuration.`,
 				"groupm":         `Bidder "groupm" is no longer available in Prebid Server. Please update your configuration.`,
 				"verizonmedia":   `Bidder "verizonmedia" is no longer available in Prebid Server. Please update your configuration.`,
+				"brightroll":     `Bidder "brightroll" is no longer available in Prebid Server. Please update your configuration.`,
 			},
 		},
 	}
diff --git a/exchange/bidder_test.go b/exchange/bidder_test.go
index 9d36808c..3eeb3e90 100644
--- a/exchange/bidder_test.go
+++ b/exchange/bidder_test.go
@@ -3063,3 +3063,79 @@ func TestGetBidType(t *testing.T) {
 		})
 	}
 }
+
+type mockBidderTmaxCtx struct {
+	startTime, deadline time.Time
+	ok                  bool
+}
+
+func (m *mockBidderTmaxCtx) Deadline() (deadline time.Time, _ bool) {
+	return m.deadline, m.ok
+}
+func (m *mockBidderTmaxCtx) RemainingDurationMS(deadline time.Time) int64 {
+	return deadline.Sub(m.startTime).Milliseconds()
+}
+
+func TestUpdateBidderTmax(t *testing.T) {
+	respStatus := 200
+	respBody := "{\"bid\":false}"
+	server := httptest.NewServer(mockHandler(respStatus, "getBody", respBody))
+	defer server.Close()
+
+	requestHeaders := http.Header{}
+	requestHeaders.Add("Content-Type", "application/json")
+
+	currencyConverter := currency.NewRateConverter(&http.Client{}, "", time.Duration(0))
+	var requestTmax int64 = 700
+
+	bidderReq := BidderRequest{
+		BidRequest: &openrtb2.BidRequest{Imp: []openrtb2.Imp{{ID: "impId"}}, TMax: requestTmax},
+		BidderName: "test",
+	}
+	extraInfo := &adapters.ExtraRequestInfo{}
+
+	tests := []struct {
+		description     string
+		requestTmax     int64
+		tmaxAdjustments *TmaxAdjustmentsPreprocessed
+		assertFn        func(actualTmax int64) bool
+	}{
+		{
+			description:     "tmax-is-not-enabled",
+			requestTmax:     requestTmax,
+			tmaxAdjustments: &TmaxAdjustmentsPreprocessed{IsEnforced: false},
+			assertFn: func(actualTmax int64) bool {
+				return requestTmax == actualTmax
+			},
+		},
+		{
+			description:     "updates-bidder-tmax",
+			requestTmax:     requestTmax,
+			tmaxAdjustments: &TmaxAdjustmentsPreprocessed{IsEnforced: true, BidderResponseDurationMin: 100, BidderNetworkLatencyBuffer: 50, PBSResponsePreparationDuration: 50},
+			assertFn: func(actualTmax int64) bool {
+				return requestTmax > actualTmax
+			},
+		},
+	}
+	for _, test := range tests {
+		t.Run(test.description, func(t *testing.T) {
+			bidderImpl := &goodSingleBidder{
+				httpRequest: &adapters.RequestData{
+					Method:  "POST",
+					Uri:     server.URL,
+					Body:    []byte("{\"key\":\"val\"}"),
+					Headers: http.Header{},
+				},
+				bidResponse: &adapters.BidderResponse{},
+			}
+
+			ctx, cancel := context.WithDeadline(context.Background(), time.Now().Add(50*time.Millisecond))
+			defer cancel()
+			bidReqOptions := bidRequestOptions{tmaxAdjustments: test.tmaxAdjustments}
+			bidder := AdaptBidder(bidderImpl, server.Client(), &config.Configuration{}, &metricsConfig.NilMetricsEngine{}, openrtb_ext.BidderAppnexus, &config.DebugInfo{Allow: false}, "")
+			_, _, errs := bidder.requestBid(ctx, bidderReq, currencyConverter.Rates(), extraInfo, &adscert.NilSigner{}, bidReqOptions, openrtb_ext.ExtAlternateBidderCodes{}, &hookexecution.EmptyHookExecutor{}, nil)
+			assert.Empty(t, errs)
+			assert.True(t, test.assertFn(bidderImpl.bidRequest.TMax))
+		})
+	}
+}
diff --git a/exchange/exchangetest/request-ext-prebid-filtering.json b/exchange/exchangetest/request-ext-prebid-filtering.json
index 499f779a..4355f517 100644
--- a/exchange/exchangetest/request-ext-prebid-filtering.json
+++ b/exchange/exchangetest/request-ext-prebid-filtering.json
@@ -60,6 +60,14 @@
                         "rubicon": {
                             "not": "permitted-for-appnexus"
                         }
+                    },
+                    "sdk": {
+                        "renderers": [
+                            {
+                                "name": "test-name",
+                                "version": "test-version"
+                            }
+                        ]
                     }
                 }
             }
@@ -108,6 +116,14 @@
                             "bidderparams": {
                                 "param1": 1,
                                 "paramA": "A"
+                            },
+                            "sdk": {
+                                "renderers": [
+                                    {
+                                        "name": "test-name",
+                                        "version": "test-version"
+                                    }
+                                ]
                             }
                         }
                     }
diff --git a/exchange/tmax_adjustments_test.go b/exchange/tmax_adjustments_test.go
new file mode 100644
index 00000000..7e6a02ab
--- /dev/null
+++ b/exchange/tmax_adjustments_test.go
@@ -0,0 +1,108 @@
+package exchange
+
+import (
+	"testing"
+	"time"
+
+	"github.com/prebid/prebid-server/config"
+	"github.com/stretchr/testify/assert"
+)
+
+func TestGetBidderTmax(t *testing.T) {
+	var (
+		requestTmaxMS               int64 = 700
+		bidderNetworkLatencyBuffer  uint  = 50
+		responsePreparationDuration uint  = 60
+	)
+	requestTmaxNS := requestTmaxMS * int64(time.Millisecond)
+	startTime := time.Date(2023, 5, 30, 1, 0, 0, 0, time.UTC)
+	deadline := time.Date(2023, 5, 30, 1, 0, 0, int(requestTmaxNS), time.UTC)
+	ctx := &mockBidderTmaxCtx{startTime: startTime, deadline: deadline, ok: true}
+	tests := []struct {
+		description     string
+		ctx             bidderTmaxContext
+		requestTmax     int64
+		expectedTmax    int64
+		tmaxAdjustments TmaxAdjustmentsPreprocessed
+	}{
+		{
+			description:     "returns-requestTmax-when-IsEnforced-is-false",
+			ctx:             ctx,
+			requestTmax:     requestTmaxMS,
+			tmaxAdjustments: TmaxAdjustmentsPreprocessed{IsEnforced: false},
+			expectedTmax:    requestTmaxMS,
+		},
+		{
+			description:     "returns-requestTmax-when-BidderResponseDurationMin-is-not-set",
+			ctx:             ctx,
+			requestTmax:     requestTmaxMS,
+			tmaxAdjustments: TmaxAdjustmentsPreprocessed{IsEnforced: true, BidderResponseDurationMin: 0},
+			expectedTmax:    requestTmaxMS,
+		},
+		{
+			description:     "returns-requestTmax-when-BidderNetworkLatencyBuffer-and-PBSResponsePreparationDuration-is-not-set",
+			ctx:             ctx,
+			requestTmax:     requestTmaxMS,
+			tmaxAdjustments: TmaxAdjustmentsPreprocessed{IsEnforced: true, BidderResponseDurationMin: 100, BidderNetworkLatencyBuffer: 0, PBSResponsePreparationDuration: 0},
+			expectedTmax:    requestTmaxMS,
+		},
+		{
+			description:     "returns-requestTmax-when-context-deadline-is-not-set",
+			ctx:             &mockBidderTmaxCtx{ok: false},
+			requestTmax:     requestTmaxMS,
+			tmaxAdjustments: TmaxAdjustmentsPreprocessed{IsEnforced: true, BidderResponseDurationMin: 100, BidderNetworkLatencyBuffer: 50, PBSResponsePreparationDuration: 60},
+			expectedTmax:    requestTmaxMS,
+		},
+		{
+			description:     "returns-remaing-duration-by-subtracting-BidderNetworkLatencyBuffer-and-PBSResponsePreparationDuration",
+			ctx:             ctx,
+			requestTmax:     requestTmaxMS,
+			tmaxAdjustments: TmaxAdjustmentsPreprocessed{IsEnforced: true, BidderResponseDurationMin: 100, BidderNetworkLatencyBuffer: bidderNetworkLatencyBuffer, PBSResponsePreparationDuration: responsePreparationDuration},
+			expectedTmax:    ctx.RemainingDurationMS(deadline) - int64(bidderNetworkLatencyBuffer) - int64(responsePreparationDuration),
+		},
+	}
+	for _, test := range tests {
+		t.Run(test.description, func(t *testing.T) {
+			assert.Equal(t, test.expectedTmax, getBidderTmax(test.ctx, test.requestTmax, test.tmaxAdjustments))
+		})
+	}
+}
+
+func TestProcessTMaxAdjustments(t *testing.T) {
+	tests := []struct {
+		description     string
+		expected        *TmaxAdjustmentsPreprocessed
+		tmaxAdjustments config.TmaxAdjustments
+	}{
+		{
+			description:     "returns-nil-when-tmax-is-not-enabled",
+			tmaxAdjustments: config.TmaxAdjustments{Enabled: false},
+			expected:        nil,
+		},
+		{
+			description:     "BidderResponseDurationMin-is-not-set",
+			tmaxAdjustments: config.TmaxAdjustments{Enabled: true, BidderResponseDurationMin: 0, BidderNetworkLatencyBuffer: 10, PBSResponsePreparationDuration: 20},
+			expected:        &TmaxAdjustmentsPreprocessed{IsEnforced: false, BidderResponseDurationMin: 0, BidderNetworkLatencyBuffer: 10, PBSResponsePreparationDuration: 20},
+		},
+		{
+			description:     "BidderNetworkLatencyBuffer-and-PBSResponsePreparationDuration-are-not-set",
+			tmaxAdjustments: config.TmaxAdjustments{Enabled: true, BidderResponseDurationMin: 100, BidderNetworkLatencyBuffer: 0, PBSResponsePreparationDuration: 0},
+			expected:        &TmaxAdjustmentsPreprocessed{IsEnforced: false, BidderResponseDurationMin: 100, BidderNetworkLatencyBuffer: 0, PBSResponsePreparationDuration: 0},
+		},
+		{
+			description:     "BidderNetworkLatencyBuffer-is-not-set",
+			tmaxAdjustments: config.TmaxAdjustments{Enabled: true, BidderResponseDurationMin: 100, BidderNetworkLatencyBuffer: 0, PBSResponsePreparationDuration: 10},
+			expected:        &TmaxAdjustmentsPreprocessed{IsEnforced: true, BidderResponseDurationMin: 100, BidderNetworkLatencyBuffer: 0, PBSResponsePreparationDuration: 10},
+		},
+		{
+			description:     "PBSResponsePreparationDuration-is-not-set",
+			tmaxAdjustments: config.TmaxAdjustments{Enabled: true, BidderResponseDurationMin: 100, BidderNetworkLatencyBuffer: 10, PBSResponsePreparationDuration: 0},
+			expected:        &TmaxAdjustmentsPreprocessed{IsEnforced: true, BidderResponseDurationMin: 100, BidderNetworkLatencyBuffer: 10, PBSResponsePreparationDuration: 0},
+		},
+	}
+	for _, test := range tests {
+		t.Run(test.description, func(t *testing.T) {
+			assert.Equal(t, test.expected, ProcessTMaxAdjustments(test.tmaxAdjustments))
+		})
+	}
+}
diff --git a/exchange/utils_test.go b/exchange/utils_test.go
index 973edc53..aff1b3c0 100644
--- a/exchange/utils_test.go
+++ b/exchange/utils_test.go
@@ -74,8 +74,6 @@ func assertReq(t *testing.T, bidderRequests []BidderRequest,
 	assert.NotEqual(t, bidderRequests, 0, "cleanOpenRTBRequest should split request into individual bidder requests")
 
 	// assert for PI data
-	// Both appnexus and brightroll should be allowed since brightroll
-	// is used as an alias for appnexus in the test request
 	for _, req := range bidderRequests {
 		if !applyCOPPA && consentedVendors[req.BidderName.String()] {
 			assert.NotEqual(t, req.BidRequest.User.BuyerUID, "", "cleanOpenRTBRequest shouldn't clean PI data as per COPPA or for a consented vendor as per GDPR or per CCPA")
@@ -459,7 +457,7 @@ func TestCleanOpenRTBRequests(t *testing.T) {
 			bidReqAssertions: assertReq,
 			hasError:         false,
 			applyCOPPA:       false,
-			consentedVendors: map[string]bool{"appnexus": true, "brightroll": true},
+			consentedVendors: map[string]bool{"appnexus": true},
 		},
 	}
 
@@ -508,13 +506,6 @@ func TestCleanOpenRTBRequestsWithFPD(t *testing.T) {
 	}
 	fpd[openrtb_ext.BidderName("rubicon")] = &apnFpd
 
-	brightrollFpd := firstpartydata.ResolvedFirstPartyData{
-		Site: &openrtb2.Site{Name: "fpdBrightrollSite"},
-		App:  &openrtb2.App{Name: "fpdBrightrollApp"},
-		User: &openrtb2.User{Keywords: "fpdBrightrollUser"},
-	}
-	fpd[openrtb_ext.BidderName("brightroll")] = &brightrollFpd
-
 	emptyTCF2Config := gdpr.NewTCF2Config(config.TCF2{}, config.AccountGDPR{})
 
 	testCases := []struct {
@@ -2296,6 +2287,7 @@ func TestCleanOpenRTBRequestsWithOpenRTBDowngrade(t *testing.T) {
 	bidReq.User.ID = ""
 	bidReq.User.BuyerUID = ""
 	bidReq.User.Yob = 0
+	bidReq.User.Geo = &openrtb2.Geo{Lat: 123.46}
 
 	downgradedRegs := *bidReq.Regs
 	downgradedUser := *bidReq.User
@@ -2392,14 +2384,14 @@ func TestBuildRequestExtForBidder(t *testing.T) {
 		{
 			description:  "Prebid - Allowed Fields Only",
 			bidderParams: nil,
-			requestExt:   json.RawMessage(`{"prebid":{"integration":"a","channel":{"name":"b","version":"c"},"debug":true,"currency":{"rates":{"FOO":{"BAR":42}},"usepbsrates":true}, "server": {"externalurl": "url", "gvlid": 1, "datacenter": "2"}}}`),
-			expectedJson: json.RawMessage(`{"prebid":{"integration":"a","channel":{"name":"b","version":"c"},"debug":true,"currency":{"rates":{"FOO":{"BAR":42}},"usepbsrates":true}, "server": {"externalurl": "url", "gvlid": 1, "datacenter": "2"}}}`),
+			requestExt:   json.RawMessage(`{"prebid":{"integration":"a","channel":{"name":"b","version":"c"},"debug":true,"currency":{"rates":{"FOO":{"BAR":42}},"usepbsrates":true}, "server": {"externalurl": "url", "gvlid": 1, "datacenter": "2"}, "sdk": {"renderers": [{"name": "r1"}]}}}`),
+			expectedJson: json.RawMessage(`{"prebid":{"integration":"a","channel":{"name":"b","version":"c"},"debug":true,"currency":{"rates":{"FOO":{"BAR":42}},"usepbsrates":true}, "server": {"externalurl": "url", "gvlid": 1, "datacenter": "2"}, "sdk": {"renderers": [{"name": "r1"}]}}}`),
 		},
 		{
 			description:  "Prebid - Allowed Fields + Bidder Params",
 			bidderParams: map[string]json.RawMessage{bidder: bidderParams},
-			requestExt:   json.RawMessage(`{"prebid":{"integration":"a","channel":{"name":"b","version":"c"},"debug":true,"currency":{"rates":{"FOO":{"BAR":42}},"usepbsrates":true}, "server": {"externalurl": "url", "gvlid": 1, "datacenter": "2"}}}`),
-			expectedJson: json.RawMessage(`{"prebid":{"integration":"a","channel":{"name":"b","version":"c"},"debug":true,"currency":{"rates":{"FOO":{"BAR":42}},"usepbsrates":true}, "server": {"externalurl": "url", "gvlid": 1, "datacenter": "2"}, "bidderparams":"bar"}}`),
+			requestExt:   json.RawMessage(`{"prebid":{"integration":"a","channel":{"name":"b","version":"c"},"debug":true,"currency":{"rates":{"FOO":{"BAR":42}},"usepbsrates":true}, "server": {"externalurl": "url", "gvlid": 1, "datacenter": "2"}, "sdk": {"renderers": [{"name": "r1"}]}}}`),
+			expectedJson: json.RawMessage(`{"prebid":{"integration":"a","channel":{"name":"b","version":"c"},"debug":true,"currency":{"rates":{"FOO":{"BAR":42}},"usepbsrates":true}, "server": {"externalurl": "url", "gvlid": 1, "datacenter": "2"}, "sdk": {"renderers": [{"name": "r1"}]}, "bidderparams":"bar"}}`),
 		},
 		{
 			description:  "Other",
@@ -2410,8 +2402,8 @@ func TestBuildRequestExtForBidder(t *testing.T) {
 		{
 			description:  "Prebid + Other + Bider Params",
 			bidderParams: map[string]json.RawMessage{bidder: bidderParams},
-			requestExt:   json.RawMessage(`{"other":"foo","prebid":{"integration":"a","channel":{"name":"b","version":"c"},"debug":true,"currency":{"rates":{"FOO":{"BAR":42}},"usepbsrates":true}, "server": {"externalurl": "url", "gvlid": 1, "datacenter": "2"}}}`),
-			expectedJson: json.RawMessage(`{"other":"foo","prebid":{"integration":"a","channel":{"name":"b","version":"c"},"debug":true,"currency":{"rates":{"FOO":{"BAR":42}},"usepbsrates":true}, "server": {"externalurl": "url", "gvlid": 1, "datacenter": "2"}, "bidderparams":"bar"}}`),
+			requestExt:   json.RawMessage(`{"other":"foo","prebid":{"integration":"a","channel":{"name":"b","version":"c"},"debug":true,"currency":{"rates":{"FOO":{"BAR":42}},"usepbsrates":true}, "server": {"externalurl": "url", "gvlid": 1, "datacenter": "2"}, "sdk": {"renderers": [{"name": "r1"}]}}}`),
+			expectedJson: json.RawMessage(`{"other":"foo","prebid":{"integration":"a","channel":{"name":"b","version":"c"},"debug":true,"currency":{"rates":{"FOO":{"BAR":42}},"usepbsrates":true}, "server": {"externalurl": "url", "gvlid": 1, "datacenter": "2"}, "sdk": {"renderers": [{"name": "r1"}]}, "bidderparams":"bar"}}`),
 		},
 		{
 			description:          "Prebid + AlternateBidderCodes in pbs config but current bidder not in AlternateBidderCodes config",
@@ -2580,9 +2572,9 @@ func newAdapterAliasBidRequest(t *testing.T) *openrtb2.BidRequest {
 					H: 600,
 				}},
 			},
-			Ext: json.RawMessage(`{"appnexus": {"placementId": 1},"brightroll": {"placementId": 105}}`),
+			Ext: json.RawMessage(`{"appnexus": {"placementId": 1},"somealias": {"placementId": 105}}`),
 		}},
-		Ext: json.RawMessage(`{"prebid":{"aliases":{"brightroll":"appnexus"}}}`),
+		Ext: json.RawMessage(`{"prebid":{"aliases":{"somealias":"appnexus"}}}`),
 	}
 }
 
@@ -2610,6 +2602,7 @@ func newBidRequest(t *testing.T) *openrtb2.BidRequest {
 			BuyerUID: "their-id",
 			Yob:      1982,
 			Ext:      json.RawMessage(`{}`),
+			Geo:      &openrtb2.Geo{Lat: 123.456},
 		},
 		Imp: []openrtb2.Imp{{
 			ID: "some-imp-id",
@@ -3196,17 +3189,17 @@ func Test_parseAliasesGVLIDs(t *testing.T) {
 			"AliasGVLID Parsed Correctly",
 			args{
 				orig: &openrtb2.BidRequest{
-					Ext: json.RawMessage(`{"prebid":{"aliases":{"brightroll":"appnexus"}, "aliasgvlids":{"brightroll":1}}}`),
+					Ext: json.RawMessage(`{"prebid":{"aliases":{"somealiascode":"appnexus"}, "aliasgvlids":{"somealiascode":1}}}`),
 				},
 			},
-			map[string]uint16{"brightroll": 1},
+			map[string]uint16{"somealiascode": 1},
 			false,
 		},
 		{
 			"AliasGVLID parsing error",
 			args{
 				orig: &openrtb2.BidRequest{
-					Ext: json.RawMessage(`{"prebid":{"aliases":{"brightroll":"appnexus"}, "aliasgvlids": {"brightroll":"abc"}`),
+					Ext: json.RawMessage(`{"prebid":{"aliases":{"somealiascode":"appnexus"}, "aliasgvlids": {"somealiascode":"abc"}`),
 				},
 			},
 			nil,
@@ -3216,7 +3209,7 @@ func Test_parseAliasesGVLIDs(t *testing.T) {
 			"Invalid AliasGVLID",
 			args{
 				orig: &openrtb2.BidRequest{
-					Ext: json.RawMessage(`{"prebid":{"aliases":{"brightroll":"appnexus"}, "aliasgvlids":"abc"}`),
+					Ext: json.RawMessage(`{"prebid":{"aliases":{"somealiascode":"appnexus"}, "aliasgvlids":"abc"}`),
 				},
 			},
 			nil,
@@ -3226,7 +3219,7 @@ func Test_parseAliasesGVLIDs(t *testing.T) {
 			"Missing AliasGVLID",
 			args{
 				orig: &openrtb2.BidRequest{
-					Ext: json.RawMessage(`{"prebid":{"aliases":{"brightroll":"appnexus"}}`),
+					Ext: json.RawMessage(`{"prebid":{"aliases":{"somealiascode":"appnexus"}}`),
 				},
 			},
 			nil,
@@ -4275,33 +4268,76 @@ func TestGetMediaTypeForBid(t *testing.T) {
 	}
 }
 
-func TemporarilyDisabledTestCleanOpenRTBRequestsActivitiesFetchBids(t *testing.T) {
+func TestCleanOpenRTBRequestsActivitiesFetchBids(t *testing.T) {
 	testCases := []struct {
-		name              string
-		req               *openrtb2.BidRequest
-		componentName     string
-		allow             bool
-		expectedReqNumber int
+		name                 string
+		req                  *openrtb2.BidRequest
+		privacyConfig        *config.AccountPrivacy
+		componentName        string
+		allow                bool
+		expectedReqNumber    int
+		expectedUserYOB      int64
+		expectedUserLat      float64
+		expectedDeviceDIDMD5 string
 	}{
 		{
-			name:              "request_with_one_bidder_allowed",
-			req:               newBidRequest(t),
-			componentName:     "appnexus",
-			allow:             true,
-			expectedReqNumber: 1,
-		},
-		{
-			name:              "request_with_one_bidder_not_allowed",
-			req:               newBidRequest(t),
-			componentName:     "appnexus",
-			allow:             false,
-			expectedReqNumber: 0,
+			name:                 "fetch_bids_request_with_one_bidder_allowed",
+			req:                  newBidRequest(t),
+			privacyConfig:        getFetchBidsActivityConfig("appnexus", true),
+			expectedReqNumber:    1,
+			expectedUserYOB:      1982,
+			expectedUserLat:      123.456,
+			expectedDeviceDIDMD5: "some device ID hash",
+		},
+		{
+			name:                 "fetch_bids_request_with_one_bidder_not_allowed",
+			req:                  newBidRequest(t),
+			privacyConfig:        getFetchBidsActivityConfig("appnexus", false),
+			expectedReqNumber:    0,
+			expectedUserYOB:      1982,
+			expectedUserLat:      123.456,
+			expectedDeviceDIDMD5: "some device ID hash",
+		},
+		{
+			name:                 "transmit_ufpd_allowed",
+			req:                  newBidRequest(t),
+			privacyConfig:        getTransmitUFPDActivityConfig("appnexus", true),
+			expectedReqNumber:    1,
+			expectedUserYOB:      1982,
+			expectedUserLat:      123.456,
+			expectedDeviceDIDMD5: "some device ID hash",
+		},
+		{
+			name:                 "transmit_ufpd_deny",
+			req:                  newBidRequest(t),
+			privacyConfig:        getTransmitUFPDActivityConfig("appnexus", false),
+			expectedReqNumber:    1,
+			expectedUserYOB:      0,
+			expectedUserLat:      123.456,
+			expectedDeviceDIDMD5: "",
+		},
+		{
+			name:                 "transmit_precise_geo_allowed",
+			req:                  newBidRequest(t),
+			privacyConfig:        getTransmitPreciseGeoActivityConfig("appnexus", true),
+			expectedReqNumber:    1,
+			expectedUserYOB:      1982,
+			expectedUserLat:      123.456,
+			expectedDeviceDIDMD5: "some device ID hash",
+		},
+		{
+			name:                 "transmit_precise_geo_deny",
+			req:                  newBidRequest(t),
+			privacyConfig:        getTransmitPreciseGeoActivityConfig("appnexus", false),
+			expectedReqNumber:    1,
+			expectedUserYOB:      1982,
+			expectedUserLat:      123.46,
+			expectedDeviceDIDMD5: "some device ID hash",
 		},
 	}
 
 	for _, test := range testCases {
-		privacyConfig := getDefaultActivityConfig(test.componentName, test.allow)
-		activities, err := privacy.NewActivityControl(privacyConfig)
+		activities, err := privacy.NewActivityControl(test.privacyConfig)
 		assert.NoError(t, err, "")
 		auctionReq := AuctionRequest{
 			BidRequestWrapper: &openrtb_ext.RequestWrapper{BidRequest: test.req},
@@ -4321,25 +4357,51 @@ func TemporarilyDisabledTestCleanOpenRTBRequestsActivitiesFetchBids(t *testing.T
 			bidderRequests, _, errs := reqSplitter.cleanOpenRTBRequests(context.Background(), auctionReq, nil, gdpr.SignalNo)
 			assert.Empty(t, errs)
 			assert.Len(t, bidderRequests, test.expectedReqNumber)
+
+			if test.expectedReqNumber == 1 {
+				assert.Equal(t, test.expectedUserYOB, bidderRequests[0].BidRequest.User.Yob)
+				assert.Equal(t, test.expectedUserLat, bidderRequests[0].BidRequest.User.Geo.Lat)
+				assert.Equal(t, test.expectedDeviceDIDMD5, bidderRequests[0].BidRequest.Device.DIDMD5)
+			}
 		})
 	}
 }
 
-func getDefaultActivityConfig(componentName string, allow bool) *config.AccountPrivacy {
-	return &config.AccountPrivacy{
-		AllowActivities: config.AllowActivities{
-			FetchBids: config.Activity{
-				Default: ptrutil.ToPtr(true),
-				Rules: []config.ActivityRule{
-					{
-						Allow: allow,
-						Condition: config.ActivityCondition{
-							ComponentName: []string{componentName},
-							ComponentType: []string{"bidder"},
-						},
-					},
+func buildDefaultActivityConfig(componentName string, allow bool) config.Activity {
+	return config.Activity{
+		Default: ptrutil.ToPtr(true),
+		Rules: []config.ActivityRule{
+			{
+				Allow: allow,
+				Condition: config.ActivityCondition{
+					ComponentName: []string{componentName},
+					ComponentType: []string{"bidder"},
 				},
 			},
 		},
 	}
 }
+
+func getFetchBidsActivityConfig(componentName string, allow bool) *config.AccountPrivacy {
+	return &config.AccountPrivacy{
+		AllowActivities: config.AllowActivities{
+			FetchBids: buildDefaultActivityConfig(componentName, allow),
+		},
+	}
+}
+
+func getTransmitUFPDActivityConfig(componentName string, allow bool) *config.AccountPrivacy {
+	return &config.AccountPrivacy{
+		AllowActivities: config.AllowActivities{
+			TransmitUserFPD: buildDefaultActivityConfig(componentName, allow),
+		},
+	}
+}
+
+func getTransmitPreciseGeoActivityConfig(componentName string, allow bool) *config.AccountPrivacy {
+	return &config.AccountPrivacy{
+		AllowActivities: config.AllowActivities{
+			TransmitPreciseGeo: buildDefaultActivityConfig(componentName, allow),
+		},
+	}
+}
diff --git a/privacy/enforcement_test.go b/privacy/enforcement_test.go
index a9d7ca92..be1ab0dc 100644
--- a/privacy/enforcement_test.go
+++ b/privacy/enforcement_test.go
@@ -8,7 +8,7 @@ import (
 	"github.com/stretchr/testify/mock"
 )
 
-func TestAny(t *testing.T) {
+func TestAnyLegacy(t *testing.T) {
 	testCases := []struct {
 		enforcement Enforcement
 		expected    bool
@@ -50,12 +50,12 @@ func TestAny(t *testing.T) {
 	}
 
 	for _, test := range testCases {
-		result := test.enforcement.Any()
+		result := test.enforcement.AnyLegacy()
 		assert.Equal(t, test.expected, result, test.description)
 	}
 }
 
-func TestApply(t *testing.T) {
+func TestApplyGDPR(t *testing.T) {
 	testCases := []struct {
 		description        string
 		enforcement        Enforcement
@@ -216,6 +216,130 @@ func TestApply(t *testing.T) {
 	}
 }
 
+func TestApplyToggle(t *testing.T) {
+	testCases := []struct {
+		description                  string
+		enforcement                  Enforcement
+		expectedScrubRequestExecuted bool
+		expectedScrubUserExecuted    bool
+		expectedScrubDeviceExecuted  bool
+	}{
+		{
+			description: "All enforced - only ScrubRequest execution expected",
+			enforcement: Enforcement{
+				CCPA:       true,
+				COPPA:      true,
+				GDPRGeo:    true,
+				GDPRID:     true,
+				LMT:        true,
+				UFPD:       true,
+				Eids:       true,
+				PreciseGeo: true,
+				TID:        true,
+			},
+			expectedScrubRequestExecuted: true,
+			expectedScrubUserExecuted:    false,
+			expectedScrubDeviceExecuted:  false,
+		},
+		{
+			description: "All Legacy and no activities - ScrubUser and ScrubDevice execution expected",
+			enforcement: Enforcement{
+				CCPA:       true,
+				COPPA:      true,
+				GDPRGeo:    true,
+				GDPRID:     true,
+				LMT:        true,
+				UFPD:       false,
+				Eids:       false,
+				PreciseGeo: false,
+				TID:        false,
+			},
+			expectedScrubRequestExecuted: false,
+			expectedScrubUserExecuted:    true,
+			expectedScrubDeviceExecuted:  true,
+		},
+		{
+			description: "Some Legacy and some activities - ScrubRequest, ScrubUser and ScrubDevice execution expected",
+			enforcement: Enforcement{
+				CCPA:       true,
+				COPPA:      true,
+				GDPRGeo:    true,
+				GDPRID:     true,
+				LMT:        true,
+				UFPD:       true,
+				Eids:       false,
+				PreciseGeo: false,
+				TID:        false,
+			},
+			expectedScrubRequestExecuted: true,
+			expectedScrubUserExecuted:    true,
+			expectedScrubDeviceExecuted:  true,
+		},
+		{
+			description: "Some Legacy and some activities - ScrubRequest execution expected",
+			enforcement: Enforcement{
+				CCPA:       true,
+				COPPA:      true,
+				GDPRGeo:    true,
+				GDPRID:     true,
+				LMT:        true,
+				UFPD:       true,
+				Eids:       true,
+				PreciseGeo: true,
+				TID:        false,
+			},
+			expectedScrubRequestExecuted: true,
+			expectedScrubUserExecuted:    false,
+			expectedScrubDeviceExecuted:  false,
+		},
+		{
+			description: "Some Legacy and some activities overlap - ScrubRequest and ScrubUser execution expected",
+			enforcement: Enforcement{
+				CCPA:       true,
+				COPPA:      true,
+				GDPRGeo:    true,
+				GDPRID:     true,
+				LMT:        true,
+				UFPD:       true,
+				Eids:       false,
+				PreciseGeo: true,
+				TID:        false,
+			},
+			expectedScrubRequestExecuted: true,
+			expectedScrubUserExecuted:    true,
+			expectedScrubDeviceExecuted:  false,
+		},
+	}
+
+	for _, test := range testCases {
+		t.Run(test.description, func(t *testing.T) {
+			req := &openrtb2.BidRequest{
+				Device: &openrtb2.Device{},
+				User:   &openrtb2.User{},
+			}
+			replacedDevice := &openrtb2.Device{}
+			replacedUser := &openrtb2.User{}
+
+			m := &mockScrubber{}
+
+			if test.expectedScrubRequestExecuted {
+				m.On("ScrubRequest", req, test.enforcement).Return(req).Once()
+			}
+			if test.expectedScrubUserExecuted {
+				m.On("ScrubUser", req.User, ScrubStrategyUserIDAndDemographic, ScrubStrategyGeoFull).Return(replacedUser).Once()
+			}
+			if test.expectedScrubDeviceExecuted {
+				m.On("ScrubDevice", req.Device, ScrubStrategyDeviceIDAll, ScrubStrategyIPV4Lowest8, ScrubStrategyIPV6Lowest32, ScrubStrategyGeoFull).Return(replacedDevice).Once()
+			}
+
+			test.enforcement.apply(req, m)
+
+			m.AssertExpectations(t)
+
+		})
+	}
+}
+
 func TestApplyNoneApplicable(t *testing.T) {
 	req := &openrtb2.BidRequest{}
 
@@ -227,6 +351,11 @@ func TestApplyNoneApplicable(t *testing.T) {
 		GDPRGeo: false,
 		GDPRID:  false,
 		LMT:     false,
+
+		UFPD:       false,
+		PreciseGeo: false,
+		TID:        false,
+		Eids:       false,
 	}
 	enforcement.apply(req, m)
 
@@ -248,6 +377,11 @@ type mockScrubber struct {
 	mock.Mock
 }
 
+func (m *mockScrubber) ScrubRequest(bidRequest *openrtb2.BidRequest, enforcement Enforcement) *openrtb2.BidRequest {
+	args := m.Called(bidRequest, enforcement)
+	return args.Get(0).(*openrtb2.BidRequest)
+}
+
 func (m *mockScrubber) ScrubDevice(device *openrtb2.Device, id ScrubStrategyDeviceID, ipv4 ScrubStrategyIPV4, ipv6 ScrubStrategyIPV6, geo ScrubStrategyGeo) *openrtb2.Device {
 	args := m.Called(device, id, ipv4, ipv6, geo)
 	return args.Get(0).(*openrtb2.Device)
diff --git a/privacy/enforcer_test.go b/privacy/enforcer_test.go
index e87a9eb2..2b9715f8 100644
--- a/privacy/enforcer_test.go
+++ b/privacy/enforcer_test.go
@@ -8,7 +8,7 @@ import (
 	"testing"
 )
 
-func TemporarilyDisabledTestNewActivityControl(t *testing.T) {
+func TestNewActivityControl(t *testing.T) {
 
 	testCases := []struct {
 		name            string
diff --git a/privacy/scrubber_test.go b/privacy/scrubber_test.go
index 7b4afac2..0691b2c7 100644
--- a/privacy/scrubber_test.go
+++ b/privacy/scrubber_test.go
@@ -9,24 +9,7 @@ import (
 )
 
 func TestScrubDevice(t *testing.T) {
-	device := &openrtb2.Device{
-		DIDMD5:   "anyDIDMD5",
-		DIDSHA1:  "anyDIDSHA1",
-		DPIDMD5:  "anyDPIDMD5",
-		DPIDSHA1: "anyDPIDSHA1",
-		MACSHA1:  "anyMACSHA1",
-		MACMD5:   "anyMACMD5",
-		IFA:      "anyIFA",
-		IP:       "1.2.3.4",
-		IPv6:     "2001:0db8:0000:0000:0000:ff00:0042:8329",
-		Geo: &openrtb2.Geo{
-			Lat:   123.456,
-			Lon:   678.89,
-			Metro: "some metro",
-			City:  "some city",
-			ZIP:   "some zip",
-		},
-	}
+	device := getTestDevice()
 
 	testCases := []struct {
 		description string
@@ -197,20 +180,7 @@ func TestScrubDeviceNil(t *testing.T) {
 }
 
 func TestScrubUser(t *testing.T) {
-	user := &openrtb2.User{
-		ID:       "anyID",
-		BuyerUID: "anyBuyerUID",
-		Yob:      42,
-		Gender:   "anyGender",
-		Ext:      json.RawMessage(`{}`),
-		Geo: &openrtb2.Geo{
-			Lat:   123.456,
-			Lon:   678.89,
-			Metro: "some metro",
-			City:  "some city",
-			ZIP:   "some zip",
-		},
-	}
+	user := getTestUser()
 
 	testCases := []struct {
 		description string
@@ -333,6 +303,171 @@ func TestScrubUserNil(t *testing.T) {
 	assert.Nil(t, result)
 }
 
+func TestScrubRequest(t *testing.T) {
+
+	imps := []openrtb2.Imp{
+		{ID: "testId", Ext: json.RawMessage(`{"test": 1, "tid": 2}`)},
+	}
+	source := &openrtb2.Source{
+		TID: "testTid",
+	}
+	device := getTestDevice()
+	user := getTestUser()
+	user.Ext = json.RawMessage(`{"data": 1, "eids": 2}`)
+	user.EIDs = []openrtb2.EID{{Source: "test"}}
+
+	testCases := []struct {
+		description    string
+		enforcement    Enforcement
+		userExtPresent bool
+		expected       *openrtb2.BidRequest
+	}{
+		{
+			description:    "enforce transmitUFPD with user.ext",
+			enforcement:    Enforcement{UFPD: true},
+			userExtPresent: true,
+			expected: &openrtb2.BidRequest{
+				Imp:    imps,
+				Source: source,
+				User: &openrtb2.User{
+					EIDs: []openrtb2.EID{{Source: "test"}},
+					Geo:  user.Geo,
+					Ext:  json.RawMessage(`{"eids":2}`),
+				},
+				Device: &openrtb2.Device{
+					IP:   "1.2.3.4",
+					IPv6: "2001:0db8:0000:0000:0000:ff00:0042:8329",
+					Geo:  device.Geo,
+				},
+			},
+		},
+		{
+			description:    "enforce transmitUFPD without user.ext",
+			enforcement:    Enforcement{UFPD: true},
+			userExtPresent: false,
+			expected: &openrtb2.BidRequest{
+				Imp:    imps,
+				Source: source,
+				User: &openrtb2.User{
+					EIDs: []openrtb2.EID{{Source: "test"}},
+					Geo:  user.Geo,
+				},
+				Device: &openrtb2.Device{
+					IP:   "1.2.3.4",
+					IPv6: "2001:0db8:0000:0000:0000:ff00:0042:8329",
+					Geo:  device.Geo,
+				},
+			},
+		},
+		{
+			description:    "enforce transmitEids",
+			enforcement:    Enforcement{Eids: true},
+			userExtPresent: true,
+			expected: &openrtb2.BidRequest{
+				Imp:    imps,
+				Source: source,
+				Device: device,
+				User: &openrtb2.User{
+					ID:       "anyID",
+					BuyerUID: "anyBuyerUID",
+					Yob:      42,
+					Gender:   "anyGender",
+					Geo:      user.Geo,
+					EIDs:     nil,
+					Ext:      json.RawMessage(`{"data":1}`),
+				},
+			},
+		},
+		{
+			description:    "enforce transmitTid",
+			enforcement:    Enforcement{TID: true},
+			userExtPresent: true,
+			expected: &openrtb2.BidRequest{
+				Imp: []openrtb2.Imp{
+					{ID: "testId", Ext: json.RawMessage(`{"test":1}`)},
+				},
+				Source: &openrtb2.Source{
+					TID: "",
+				},
+				Device: device,
+				User: &openrtb2.User{
+					ID:       "anyID",
+					BuyerUID: "anyBuyerUID",
+					Yob:      42,
+					Gender:   "anyGender",
+					Geo:      user.Geo,
+					EIDs:     []openrtb2.EID{{Source: "test"}},
+					Ext:      json.RawMessage(`{"data": 1, "eids": 2}`),
+				},
+			},
+		},
+		{
+			description:    "enforce precise Geo",
+			enforcement:    Enforcement{PreciseGeo: true},
+			userExtPresent: true,
+			expected: &openrtb2.BidRequest{
+				Imp:    imps,
+				Source: source,
+				User: &openrtb2.User{
+					ID:       "anyID",
+					BuyerUID: "anyBuyerUID",
+					Yob:      42,
+					Gender:   "anyGender",
+					Geo: &openrtb2.Geo{
+						Lat: 123.46, Lon: 678.89,
+						Metro: "some metro",
+						City:  "some city",
+						ZIP:   "some zip",
+					},
+					EIDs: []openrtb2.EID{{Source: "test"}},
+					Ext:  json.RawMessage(`{"data": 1, "eids": 2}`),
+				},
+				Device: &openrtb2.Device{
+					IFA:      "anyIFA",
+					DIDSHA1:  "anyDIDSHA1",
+					DIDMD5:   "anyDIDMD5",
+					DPIDSHA1: "anyDPIDSHA1",
+					DPIDMD5:  "anyDPIDMD5",
+					MACSHA1:  "anyMACSHA1",
+					MACMD5:   "anyMACMD5",
+					IP:       "1.2.3.0",
+					IPv6:     "2001:0db8:0000:0000:0000:ff00:0:0",
+					Geo: &openrtb2.Geo{
+						Lat: 123.46, Lon: 678.89,
+						Metro: "some metro",
+						City:  "some city",
+						ZIP:   "some zip",
+					},
+				},
+			},
+		},
+	}
+
+	for _, test := range testCases {
+		t.Run(test.description, func(t *testing.T) {
+			bidRequest := &openrtb2.BidRequest{
+				Imp: []openrtb2.Imp{
+					{ID: "testId", Ext: json.RawMessage(`{"test": 1, "tid": 2}`)},
+				},
+				Source: &openrtb2.Source{
+					TID: "testTid",
+				},
+				User:   getTestUser(),
+				Device: getTestDevice(),
+			}
+			if test.userExtPresent {
+				bidRequest.User.Ext = json.RawMessage(`{"data": 1, "eids": 2}`)
+			} else {
+				bidRequest.User.Ext = nil
+			}
+			bidRequest.User.EIDs = []openrtb2.EID{{Source: "test"}}
+
+			result := NewScrubber().ScrubRequest(bidRequest, test.enforcement)
+			assert.Equal(t, test.expected, result, test.description)
+		})
+	}
+}
+
 func TestScrubIPV4(t *testing.T) {
 	testCases := []struct {
 		IP          string
@@ -572,7 +707,46 @@ func TestScrubUserExtIDs(t *testing.T) {
 	}
 
 	for _, test := range testCases {
-		result := scrubUserExtIDs(test.userExt)
+		result := scrubExtIDs(test.userExt, "eids")
 		assert.Equal(t, test.expected, result, test.description)
 	}
 }
+
+func getTestUser() *openrtb2.User {
+	return &openrtb2.User{
+		ID:       "anyID",
+		BuyerUID: "anyBuyerUID",
+		Yob:      42,
+		Gender:   "anyGender",
+		Ext:      json.RawMessage(`{}`),
+		Geo: &openrtb2.Geo{
+			Lat:   123.456,
+			Lon:   678.89,
+			Metro: "some metro",
+			City:  "some city",
+			ZIP:   "some zip",
+		},
+	}
+}
+
+func getTestDevice() *openrtb2.Device {
+	return &openrtb2.Device{
+		DIDMD5:   "anyDIDMD5",
+		DIDSHA1:  "anyDIDSHA1",
+		DPIDMD5:  "anyDPIDMD5",
+		DPIDSHA1: "anyDPIDSHA1",
+		MACSHA1:  "anyMACSHA1",
+		MACMD5:   "anyMACMD5",
+		IFA:      "anyIFA",
+		IP:       "1.2.3.4",
+		IPv6:     "2001:0db8:0000:0000:0000:ff00:0042:8329",
+		Geo: &openrtb2.Geo{
+			Lat:   123.456,
+			Lon:   678.89,
+			Metro: "some metro",
+			City:  "some city",
+			ZIP:   "some zip",
+		},
+	}
+
+}
diff --git a/usersync/syncer_test.go b/usersync/syncer_test.go
index 6e727c3a..c309b899 100644
--- a/usersync/syncer_test.go
+++ b/usersync/syncer_test.go
@@ -26,6 +26,7 @@ func TestNewSyncer(t *testing.T) {
 	testCases := []struct {
 		description         string
 		givenKey            string
+		givenBidderName     string
 		givenIFrameConfig   *config.SyncerEndpoint
 		givenRedirectConfig *config.SyncerEndpoint
 		givenExternalURL    string
@@ -37,6 +38,7 @@ func TestNewSyncer(t *testing.T) {
 		{
 			description:         "Missing Key",
 			givenKey:            "",
+			givenBidderName:     "",
 			givenIFrameConfig:   iframeConfig,
 			givenRedirectConfig: nil,
 			expectedError:       "key is required",
@@ -44,6 +46,7 @@ func TestNewSyncer(t *testing.T) {
 		{
 			description:         "Missing Endpoints",
 			givenKey:            "a",
+			givenBidderName:     "bidderA",
 			givenIFrameConfig:   nil,
 			givenRedirectConfig: nil,
 			expectedError:       "at least one endpoint (iframe and/or redirect) is required",
@@ -51,6 +54,7 @@ func TestNewSyncer(t *testing.T) {
 		{
 			description:         "IFrame & Redirect Endpoints",
 			givenKey:            "a",
+			givenBidderName:     "bidderA",
 			givenIFrameConfig:   iframeConfig,
 			givenRedirectConfig: redirectConfig,
 			expectedDefault:     SyncTypeIFrame,
@@ -60,13 +64,15 @@ func TestNewSyncer(t *testing.T) {
 		{
 			description:         "IFrame - Parse Error",
 			givenKey:            "a",
+			givenBidderName:     "bidderA",
 			givenIFrameConfig:   errParseConfig,
 			givenRedirectConfig: nil,
-			expectedError:       "iframe template: a_usersync_url:1: function \"malformed\" not defined",
+			expectedError:       "iframe template: biddera_usersync_url:1: function \"malformed\" not defined",
 		},
 		{
 			description:         "IFrame - Validation Error",
 			givenKey:            "a",
+			givenBidderName:     "bidderA",
 			givenIFrameConfig:   errInvalidConfig,
 			givenRedirectConfig: nil,
 			expectedError:       "iframe composed url: \"notAURL:http%3A%2F%2Fhost.com%2Fhost\" is invalid",
@@ -74,13 +80,15 @@ func TestNewSyncer(t *testing.T) {
 		{
 			description:         "Redirect - Parse Error",
 			givenKey:            "a",
+			givenBidderName:     "bidderA",
 			givenIFrameConfig:   nil,
 			givenRedirectConfig: errParseConfig,
-			expectedError:       "redirect template: a_usersync_url:1: function \"malformed\" not defined",
+			expectedError:       "redirect template: biddera_usersync_url:1: function \"malformed\" not defined",
 		},
 		{
 			description:         "Redirect - Validation Error",
 			givenKey:            "a",
+			givenBidderName:     "bidderA",
 			givenIFrameConfig:   nil,
 			givenRedirectConfig: errInvalidConfig,
 			expectedError:       "redirect composed url: \"notAURL:http%3A%2F%2Fhost.com%2Fhost\" is invalid",
@@ -88,6 +96,7 @@ func TestNewSyncer(t *testing.T) {
 		{
 			description:         "Syncer Level External URL",
 			givenKey:            "a",
+			givenBidderName:     "bidderA",
 			givenExternalURL:    "http://syncer.com",
 			givenIFrameConfig:   iframeConfig,
 			givenRedirectConfig: redirectConfig,
@@ -106,7 +115,7 @@ func TestNewSyncer(t *testing.T) {
 			ExternalURL: test.givenExternalURL,
 		}
 
-		result, err := NewSyncer(hostConfig, syncerConfig)
+		result, err := NewSyncer(hostConfig, syncerConfig, test.givenBidderName)
 
 		if test.expectedError == "" {
 			assert.NoError(t, err, test.description+":err")
@@ -196,7 +205,7 @@ func TestBuildTemplate(t *testing.T) {
 			expectedRendered: "hasNoComposedMacros,gdpr=A",
 		},
 		{
-			description: "All Composed Macros",
+			description: "All Composed Macros - SyncerKey",
 			givenSyncerEndpoint: config.SyncerEndpoint{
 				URL:         "https://bidder.com/sync?redirect={{.RedirectURL}}",
 				RedirectURL: "{{.ExternalURL}}/setuid?bidder={{.SyncerKey}}&f={{.SyncType}}&gdpr={{.GDPR}}&gpp={{.GPP}}&gpp_sid={{.GPPSID}}&uid={{.UserMacro}}",
@@ -205,6 +214,16 @@ func TestBuildTemplate(t *testing.T) {
 			},
 			expectedRendered: "https://bidder.com/sync?redirect=http%3A%2F%2Fsyncer.com%2Fsetuid%3Fbidder%3DanyKey%26f%3Dx%26gdpr%3DA%26gpp%3DD%26gpp_sid%3D1%26uid%3D%24UID%24",
 		},
+		{
+			description: "All Composed Macros - BidderName",
+			givenSyncerEndpoint: config.SyncerEndpoint{
+				URL:         "https://bidder.com/sync?redirect={{.RedirectURL}}",
+				RedirectURL: "{{.ExternalURL}}/setuid?bidder={{.BidderName}}&f={{.SyncType}}&gdpr={{.GDPR}}&gpp={{.GPP}}&gpp_sid={{.GPPSID}}&uid={{.UserMacro}}",
+				ExternalURL: "http://syncer.com",
+				UserMacro:   "$UID$",
+			},
+			expectedRendered: "https://bidder.com/sync?redirect=http%3A%2F%2Fsyncer.com%2Fsetuid%3Fbidder%3DanyKey%26f%3Dx%26gdpr%3DA%26gpp%3DD%26gpp_sid%3D1%26uid%3D%24UID%24",
+		},
 		{
 			description: "Redirect URL + External URL From Host",
 			givenSyncerEndpoint: config.SyncerEndpoint{
diff --git a/usersync/syncersbuilder_test.go b/usersync/syncersbuilder_test.go
index b3672a50..fbc18639 100644
--- a/usersync/syncersbuilder_test.go
+++ b/usersync/syncersbuilder_test.go
@@ -48,7 +48,7 @@ func TestBuildSyncers(t *testing.T) {
 			givenConfig:      hostConfig,
 			givenBidderInfos: map[string]config.BidderInfo{"bidder1": infoKeyAPopulated},
 			expectedIFramesURLs: map[string]string{
-				"bidder1": "https://bidder.com/iframe?redirect=http%3A%2F%2Fhost.com%2Fa%2Fhost",
+				"bidder1": "https://bidder.com/iframe?redirect=http%3A%2F%2Fhost.com%2Fbidder1%2Fhost",
 			},
 		},
 		{
@@ -64,7 +64,7 @@ func TestBuildSyncers(t *testing.T) {
 			givenConfig:      hostConfig,
 			givenBidderInfos: map[string]config.BidderInfo{"bidder1": infoKeyAError},
 			expectedErrors: []string{
-				"cannot create syncer for bidder bidder1 with key a: iframe template: a_usersync_url:1: function \"xRedirectURL\" not defined",
+				"cannot create syncer for bidder bidder1 with key a: iframe template: bidder1_usersync_url:1: function \"xRedirectURL\" not defined",
 			},
 		},
 		{
@@ -72,8 +72,8 @@ func TestBuildSyncers(t *testing.T) {
 			givenConfig:      hostConfig,
 			givenBidderInfos: map[string]config.BidderInfo{"bidder1": infoKeyAPopulated, "bidder2": infoKeyBPopulated},
 			expectedIFramesURLs: map[string]string{
-				"bidder1": "https://bidder.com/iframe?redirect=http%3A%2F%2Fhost.com%2Fa%2Fhost",
-				"bidder2": "https://bidder.com/iframe?redirect=http%3A%2F%2Fhost.com%2Fb%2Fhost",
+				"bidder1": "https://bidder.com/iframe?redirect=http%3A%2F%2Fhost.com%2Fbidder1%2Fhost",
+				"bidder2": "https://bidder.com/iframe?redirect=http%3A%2F%2Fhost.com%2Fbidder2%2Fhost",
 			},
 		},
 		{
@@ -81,8 +81,8 @@ func TestBuildSyncers(t *testing.T) {
 			givenConfig:      hostConfig,
 			givenBidderInfos: map[string]config.BidderInfo{"bidder1": infoKeyAPopulated, "bidder2": infoKeyAEmpty},
 			expectedIFramesURLs: map[string]string{
-				"bidder1": "https://bidder.com/iframe?redirect=http%3A%2F%2Fhost.com%2Fa%2Fhost",
-				"bidder2": "https://bidder.com/iframe?redirect=http%3A%2F%2Fhost.com%2Fa%2Fhost",
+				"bidder1": "https://bidder.com/iframe?redirect=http%3A%2F%2Fhost.com%2Fbidder1%2Fhost",
+				"bidder2": "https://bidder.com/iframe?redirect=http%3A%2F%2Fhost.com%2Fbidder2%2Fhost",
 			},
 		},
 		{
@@ -106,7 +106,8 @@ func TestBuildSyncers(t *testing.T) {
 			givenConfig:      hostConfig,
 			givenBidderInfos: map[string]config.BidderInfo{"bidder1": infoKeyAEmpty, "bidder2": infoKeyAError},
 			expectedErrors: []string{
-				"cannot create syncer for bidder bidder2 with key a: iframe template: a_usersync_url:1: function \"xRedirectURL\" not defined",
+				"cannot create syncer for bidder bidder2 with key a: iframe template: bidder1_usersync_url:1: function \"xRedirectURL\" not defined",
+				"cannot create syncer for bidder bidder2 with key a: iframe template: bidder2_usersync_url:1: function \"xRedirectURL\" not defined",
 			},
 		},
 		{
@@ -114,7 +115,7 @@ func TestBuildSyncers(t *testing.T) {
 			givenConfig:      hostConfig,
 			givenBidderInfos: map[string]config.BidderInfo{"bidder1": {}, "bidder2": infoKeyBPopulated},
 			expectedIFramesURLs: map[string]string{
-				"bidder2": "https://bidder.com/iframe?redirect=http%3A%2F%2Fhost.com%2Fb%2Fhost",
+				"bidder2": "https://bidder.com/iframe?redirect=http%3A%2F%2Fhost.com%2Fbidder2%2Fhost",
 			},
 		},
 		{
@@ -122,7 +123,7 @@ func TestBuildSyncers(t *testing.T) {
 			givenConfig:      hostConfig,
 			givenBidderInfos: map[string]config.BidderInfo{"bidder1": infoKeyADisabled, "bidder2": infoKeyBPopulated},
 			expectedIFramesURLs: map[string]string{
-				"bidder2": "https://bidder.com/iframe?redirect=http%3A%2F%2Fhost.com%2Fb%2Fhost",
+				"bidder2": "https://bidder.com/iframe?redirect=http%3A%2F%2Fhost.com%2Fbidder2%2Fhost",
 			},
 		},
 		{
@@ -130,7 +131,7 @@ func TestBuildSyncers(t *testing.T) {
 			givenConfig:      hostConfig,
 			givenBidderInfos: map[string]config.BidderInfo{"bidder1": infoKeyASupportsOnly, "bidder2": infoKeyBPopulated},
 			expectedIFramesURLs: map[string]string{
-				"bidder2": "https://bidder.com/iframe?redirect=http%3A%2F%2Fhost.com%2Fb%2Fhost",
+				"bidder2": "https://bidder.com/iframe?redirect=http%3A%2F%2Fhost.com%2Fbidder2%2Fhost",
 			},
 		},
 		{
@@ -138,7 +139,7 @@ func TestBuildSyncers(t *testing.T) {
 			givenConfig:      hostConfig,
 			givenBidderInfos: map[string]config.BidderInfo{"bidder1": infoKeyAError, "bidder2": infoKeyBEmpty},
 			expectedErrors: []string{
-				"cannot create syncer for bidder bidder1 with key a: iframe template: a_usersync_url:1: function \"xRedirectURL\" not defined",
+				"cannot create syncer for bidder bidder1 with key a: iframe template: bidder1_usersync_url:1: function \"xRedirectURL\" not defined",
 				"cannot create syncer for bidder bidder2 with key b: at least one endpoint (iframe and/or redirect) is required",
 			},
 		},
@@ -147,7 +148,7 @@ func TestBuildSyncers(t *testing.T) {
 			givenConfig:      config.Configuration{ExternalURL: "http://host.com", UserSync: config.UserSync{ExternalURL: "http://hostoverride.com", RedirectURL: "{{.ExternalURL}}/{{.SyncerKey}}/host"}},
 			givenBidderInfos: map[string]config.BidderInfo{"bidder1": infoKeyAPopulated},
 			expectedIFramesURLs: map[string]string{
-				"bidder1": "https://bidder.com/iframe?redirect=http%3A%2F%2Fhostoverride.com%2Fa%2Fhost",
+				"bidder1": "https://bidder.com/iframe?redirect=http%3A%2F%2Fhostoverride.com%2Fbidder1%2Fhost",
 			},
 		},
 	}